aboutsummaryrefslogtreecommitdiff
path: root/sdk
diff options
context:
space:
mode:
Diffstat (limited to 'sdk')
-rw-r--r--sdk/common/NvBlastFixedQueue.h141
-rw-r--r--sdk/compiler/cmake/NvBlastExtAuthoring.cmake25
-rw-r--r--sdk/compiler/cmake/NvBlastExtShaders.cmake8
-rw-r--r--sdk/compiler/cmake/NvBlastGlobals.cmake1
-rw-r--r--sdk/compiler/cmake/linux/NvBlastExtAuthoring.cmake4
-rw-r--r--sdk/compiler/cmake/linux/NvBlastExtShaders.cmake2
-rw-r--r--sdk/extensions/assetutils/include/NvBlastExtAssetUtils.h38
-rw-r--r--sdk/extensions/assetutils/source/NvBlastExtAssetUtils.cpp73
-rw-r--r--sdk/extensions/authoring/include/NvBlastExtAuthoring.h74
-rw-r--r--sdk/extensions/authoring/include/NvBlastExtAuthoringBondGenerator.h20
-rw-r--r--sdk/extensions/authoring/include/NvBlastExtAuthoringCollisionBuilder.h22
-rw-r--r--sdk/extensions/authoring/include/NvBlastExtAuthoringFractureTool.h11
-rw-r--r--sdk/extensions/authoring/include/NvBlastExtAuthoringTypes.h4
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp258
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.cpp200
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.h137
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.cpp142
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.h9
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringBooleanTool.cpp40
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilderImpl.cpp98
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilderImpl.h15
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.cpp316
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.h16
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.cpp16
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.h6
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/FloatMath.h507
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/btAlignedAllocator.h104
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/btAlignedObjectArray.h448
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/btConvexHullComputer.h97
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/btMinMax.h65
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/btScalar.h533
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/btVector3.h715
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/vhacdCircularList.h79
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/vhacdICHull.h98
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/vhacdManifoldMesh.h142
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/vhacdMesh.h130
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/vhacdMutex.h148
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/vhacdRaycastMesh.h39
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/vhacdSArray.h158
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/vhacdTimer.h121
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/vhacdVHACD.h383
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/vhacdVector.h168
-rw-r--r--sdk/extensions/authoring/source/VHACD/inc/vhacdVolume.h430
-rw-r--r--sdk/extensions/authoring/source/VHACD/public/VHACD.h170
-rw-r--r--sdk/extensions/authoring/source/VHACD/src/FloatMath.cpp18
-rw-r--r--sdk/extensions/authoring/source/VHACD/src/VHACD-ASYNC.cpp360
-rw-r--r--sdk/extensions/authoring/source/VHACD/src/VHACD.cpp1784
-rw-r--r--sdk/extensions/authoring/source/VHACD/src/btAlignedAllocator.cpp180
-rw-r--r--sdk/extensions/authoring/source/VHACD/src/btConvexHullComputer.cpp2479
-rw-r--r--sdk/extensions/authoring/source/VHACD/src/vhacdICHull.cpp731
-rw-r--r--sdk/extensions/authoring/source/VHACD/src/vhacdManifoldMesh.cpp202
-rw-r--r--sdk/extensions/authoring/source/VHACD/src/vhacdMesh.cpp366
-rw-r--r--sdk/extensions/authoring/source/VHACD/src/vhacdRaycastMesh.cpp208
-rw-r--r--sdk/extensions/authoring/source/VHACD/src/vhacdVolume.cpp1622
-rw-r--r--sdk/extensions/physx/include/NvBlastExtPxAsset.h12
-rw-r--r--sdk/extensions/physx/include/NvBlastExtPxFamily.h18
-rw-r--r--sdk/extensions/physx/include/NvBlastExtPxManager.h2
-rw-r--r--sdk/extensions/physx/source/physics/NvBlastExtImpactDamageManager.cpp45
-rw-r--r--sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.cpp8
-rw-r--r--sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.h23
-rw-r--r--sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.cpp10
-rw-r--r--sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.h17
-rw-r--r--sdk/extensions/shaders/include/NvBlastExtDamageShaders.h158
-rw-r--r--sdk/extensions/shaders/source/NvBlastExtDamageAcceleratorAABBTree.cpp380
-rw-r--r--sdk/extensions/shaders/source/NvBlastExtDamageAcceleratorAABBTree.h148
-rw-r--r--sdk/extensions/shaders/source/NvBlastExtDamageAcceleratorInternal.h96
-rw-r--r--sdk/extensions/shaders/source/NvBlastExtDamageAccelerators.cpp43
-rw-r--r--sdk/extensions/shaders/source/NvBlastExtDamageShaders.cpp657
-rw-r--r--sdk/globals/include/NvBlastDebugRender.h64
-rw-r--r--sdk/lowlevel/include/NvBlast.h2
-rw-r--r--sdk/lowlevel/include/NvBlastTypes.h33
-rw-r--r--sdk/lowlevel/source/NvBlastActor.cpp10
-rw-r--r--sdk/lowlevel/source/NvBlastActor.h2
-rw-r--r--sdk/toolkit/include/NvBlastTkActor.h35
-rw-r--r--sdk/toolkit/include/NvBlastTkFamily.h14
-rw-r--r--sdk/toolkit/source/NvBlastTkActorImpl.cpp92
-rw-r--r--sdk/toolkit/source/NvBlastTkActorImpl.h83
-rw-r--r--sdk/toolkit/source/NvBlastTkFamilyImpl.cpp4
-rw-r--r--sdk/toolkit/source/NvBlastTkFamilyImpl.h17
-rw-r--r--sdk/toolkit/source/NvBlastTkTaskImpl.cpp2
80 files changed, 15491 insertions, 645 deletions
diff --git a/sdk/common/NvBlastFixedQueue.h b/sdk/common/NvBlastFixedQueue.h
new file mode 100644
index 0000000..ede6b71
--- /dev/null
+++ b/sdk/common/NvBlastFixedQueue.h
@@ -0,0 +1,141 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2016-2017 NVIDIA Corporation. All rights reserved.
+
+
+#ifndef NVBLASTFIXEDQUEUE_H
+#define NVBLASTFIXEDQUEUE_H
+
+#include "NvBlastAssert.h"
+#include "NvBlastMemory.h"
+
+namespace Nv
+{
+namespace Blast
+{
+
+/*!
+FixedQueue is a queue container which is intended to be used with placement new on chunk of memory.
+It'll use following memory for data layout. As follows:
+
+// some memory
+char ​*buf = new char[64 *​ 1024];
+
+// placement new on this memory
+FixedQueue<SomeClass>* arr = new (buf) FixedQueue<SomeClass>();
+
+// you can get max requiredMemorySize by an array of 'capacity' elements count to use memory left
+buf = buf + FixedQueue<SomeClass>::requiredMemorySize(capacity);
+
+*/
+template <class T>
+class FixedQueue
+{
+public:
+ explicit FixedQueue(uint32_t maxEntries) : m_num(0), m_head(0), m_tail(0), m_maxEntries(maxEntries)
+ {
+ }
+
+ static size_t requiredMemorySize(uint32_t capacity)
+ {
+ return align16(sizeof(FixedQueue<T>)) + align16(capacity * sizeof(T));
+ }
+
+ T popFront()
+ {
+ NVBLAST_ASSERT(m_num>0);
+
+ m_num--;
+ T& element = data()[m_tail];
+ m_tail = (m_tail+1) % (m_maxEntries);
+ return element;
+ }
+
+ T front()
+ {
+ NVBLAST_ASSERT(m_num>0);
+
+ return data()[m_tail];
+ }
+
+ T popBack()
+ {
+ NVBLAST_ASSERT(m_num>0);
+
+ m_num--;
+ m_head = (m_head-1) % (m_maxEntries);
+ return data()[m_head];
+ }
+
+ T back()
+ {
+ NVBLAST_ASSERT(m_num>0);
+
+ uint32_t headAccess = (m_head-1) % (m_maxEntries);
+ return data()[headAccess];
+ }
+
+ bool pushBack(const T& element)
+ {
+ if (m_num == m_maxEntries) return false;
+ data()[m_head] = element;
+
+ m_num++;
+ m_head = (m_head+1) % (m_maxEntries);
+
+ return true;
+ }
+
+ bool empty() const
+ {
+ return m_num == 0;
+ }
+
+ uint32_t size() const
+ {
+ return m_num;
+ }
+
+
+private:
+ uint32_t m_num;
+ uint32_t m_head;
+ uint32_t m_tail;
+ uint32_t m_maxEntries;
+
+ T* data()
+ {
+ return (T*)((char*)this + sizeof(FixedQueue<T>));
+ }
+
+private:
+ FixedQueue(const FixedQueue& that);
+};
+
+} // namespace Blast
+} // namespace Nv
+
+#endif // ifndef NVBLASTFIXEDQUEUE_H
diff --git a/sdk/compiler/cmake/NvBlastExtAuthoring.cmake b/sdk/compiler/cmake/NvBlastExtAuthoring.cmake
index 9ba6173..00ecfc1 100644
--- a/sdk/compiler/cmake/NvBlastExtAuthoring.cmake
+++ b/sdk/compiler/cmake/NvBlastExtAuthoring.cmake
@@ -13,7 +13,6 @@ FIND_PACKAGE(PxSharedSDK $ENV{PM_PxShared_VERSION} REQUIRED)
FIND_PACKAGE(PhysXSDK $ENV{PM_PhysX_VERSION} REQUIRED)
FIND_PACKAGE(BoostMultiprecision $ENV{PM_BoostMultiprecision_VERSION} REQUIRED)
-
# Include here after the directories are defined so that the platform specific file can use the variables.
include(${PROJECT_CMAKE_FILES_DIR}/${TARGET_BUILD_PLATFORM}/NvBlastExtAuthoring.cmake)
@@ -63,16 +62,35 @@ SET(EXT_AUTHORING_FILES
${AUTHORING_EXT_SOURCE_DIR}/NvBlastExtAuthoringMeshCleanerImpl.cpp
)
+SET(VHACD_SOURCE_FILES
+ ${AUTHORING_EXT_SOURCE_DIR}/VHACD/src/btAlignedAllocator.cpp
+ ${AUTHORING_EXT_SOURCE_DIR}/VHACD/src/btConvexHullComputer.cpp
+ ${AUTHORING_EXT_SOURCE_DIR}/VHACD/src/FloatMath.cpp
+ ${AUTHORING_EXT_SOURCE_DIR}/VHACD/src/FloatMath.inl
+ ${AUTHORING_EXT_SOURCE_DIR}/VHACD/src/VHACD.cpp
+ ${AUTHORING_EXT_SOURCE_DIR}/VHACD/src/VHACD-ASYNC.cpp
+ ${AUTHORING_EXT_SOURCE_DIR}/VHACD/src/vhacdICHull.cpp
+ ${AUTHORING_EXT_SOURCE_DIR}/VHACD/src/vhacdManifoldMesh.cpp
+ ${AUTHORING_EXT_SOURCE_DIR}/VHACD/src/vhacdMesh.cpp
+ ${AUTHORING_EXT_SOURCE_DIR}/VHACD/src/vhacdRaycastMesh.cpp
+ ${AUTHORING_EXT_SOURCE_DIR}/VHACD/src/vhacdVolume.cpp
+)
+
+SET_SOURCE_FILES_PROPERTIES(${VHACD_SOURCE_FILES} PROPERTIES COMPILE_FLAGS "${VHACD_COMPILE_FLAGS}")
+
+
ADD_LIBRARY(NvBlastExtAuthoring ${BLAST_EXT_SHARED_LIB_TYPE}
${COMMON_FILES}
${PUBLIC_FILES}
${EXT_AUTHORING_FILES}
+ ${VHACD_SOURCE_FILES}
)
SOURCE_GROUP("common" FILES ${COMMON_FILES})
SOURCE_GROUP("public" FILES ${PUBLIC_FILES})
SOURCE_GROUP("src" FILES ${EXT_AUTHORING_FILES})
+SOURCE_GROUP("VHACD/src" FILES ${VHACD_SOURCE_FILES})
# Target specific compile options
@@ -91,10 +109,15 @@ TARGET_INCLUDE_DIRECTORIES(NvBlastExtAuthoring
PRIVATE ${AUTHORING_EXT_SOURCE_DIR}
+ PRIVATE ${PROJECT_SOURCE_DIR}/extensions/assetutils/include
+
PRIVATE ${PHYSXSDK_INCLUDE_DIRS}
PRIVATE ${PXSHAREDSDK_INCLUDE_DIRS}
PRIVATE ${BOOSTMULTIPRECISION_INCLUDE_DIRS}
+
+ PRIVATE ${AUTHORING_EXT_SOURCE_DIR}/VHACD/inc
+ PRIVATE ${AUTHORING_EXT_SOURCE_DIR}/VHACD/public
)
TARGET_COMPILE_DEFINITIONS(NvBlastExtAuthoring
diff --git a/sdk/compiler/cmake/NvBlastExtShaders.cmake b/sdk/compiler/cmake/NvBlastExtShaders.cmake
index f918326..cb7599e 100644
--- a/sdk/compiler/cmake/NvBlastExtShaders.cmake
+++ b/sdk/compiler/cmake/NvBlastExtShaders.cmake
@@ -23,6 +23,10 @@ SET(PUBLIC_FILES
SET(EXT_SOURCE_FILES
${SHADERS_EXT_SOURCE_DIR}/NvBlastExtDamageShaders.cpp
+ ${SHADERS_EXT_SOURCE_DIR}/NvBlastExtDamageAcceleratorInternal.h
+ ${SHADERS_EXT_SOURCE_DIR}/NvBlastExtDamageAcceleratorAABBTree.h
+ ${SHADERS_EXT_SOURCE_DIR}/NvBlastExtDamageAcceleratorAABBTree.cpp
+ ${SHADERS_EXT_SOURCE_DIR}/NvBlastExtDamageAccelerators.cpp
)
ADD_LIBRARY(NvBlastExtShaders ${BLAST_EXT_SHARED_LIB_TYPE}
@@ -47,6 +51,8 @@ TARGET_INCLUDE_DIRECTORIES(NvBlastExtShaders
PRIVATE ${SHADERS_EXT_SOURCE_DIR}
PRIVATE ${PROJECT_SOURCE_DIR}/common
+
+ PRIVATE ${PXSHAREDSDK_INCLUDE_DIRS}
)
TARGET_COMPILE_DEFINITIONS(NvBlastExtShaders
@@ -68,6 +74,6 @@ SET_TARGET_PROPERTIES(NvBlastExtShaders PROPERTIES
# Do final direct sets after the target has been defined
#TARGET_LINK_LIBRARIES(NvBlastExtShaders NvBlast ${PHYSXSDK_LIBRARIES} ${APEXSDK_LIBRARIES} ${PXSHAREDSDK_LIBRARIES})
TARGET_LINK_LIBRARIES(NvBlastExtShaders
- PUBLIC NvBlast
+ PUBLIC NvBlast NvBlastGlobals
PUBLIC ${BLASTEXT_PLATFORM_LINKED_LIBS}
)
diff --git a/sdk/compiler/cmake/NvBlastGlobals.cmake b/sdk/compiler/cmake/NvBlastGlobals.cmake
index 4f528df..f854ccc 100644
--- a/sdk/compiler/cmake/NvBlastGlobals.cmake
+++ b/sdk/compiler/cmake/NvBlastGlobals.cmake
@@ -23,6 +23,7 @@ SET(PUBLIC_FILES
${GLOBALS_DIR}/include/NvBlastGlobals.h
${GLOBALS_DIR}/include/NvBlastAllocator.h
${GLOBALS_DIR}/include/NvBlastProfiler.h
+ ${GLOBALS_DIR}/include/NvBlastDebugRender.h
)
ADD_LIBRARY(NvBlastGlobals SHARED
diff --git a/sdk/compiler/cmake/linux/NvBlastExtAuthoring.cmake b/sdk/compiler/cmake/linux/NvBlastExtAuthoring.cmake
index 3d7bb78..b640582 100644
--- a/sdk/compiler/cmake/linux/NvBlastExtAuthoring.cmake
+++ b/sdk/compiler/cmake/linux/NvBlastExtAuthoring.cmake
@@ -24,6 +24,8 @@ SET(BLAST_EXT_SHARED_LIB_TYPE SHARED)
#This option doesn't work on Clang
IF (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
SET(BLASTEXT_PLATFORM_COMPILE_OPTIONS "-Wno-maybe-uninitialized" "-fexceptions" "-Wno-unused-parameter")
+ SET(VHACD_COMPILE_FLAGS "-Wno-type-limits -Wno-ignored-qualifiers -Wno-unused-result")
ELSE ()
SET(BLASTEXT_PLATFORM_COMPILE_OPTIONS "-Wno-return-type-c-linkage" "-pedantic" "-fexceptions" "-Wno-unused-parameter")
-ENDIF() \ No newline at end of file
+ SET(VHACD_COMPILE_FLAGS "-Wno-logical-op-parentheses -Wno-ignored-qualifiers")
+ENDIF()
diff --git a/sdk/compiler/cmake/linux/NvBlastExtShaders.cmake b/sdk/compiler/cmake/linux/NvBlastExtShaders.cmake
index a5c8c67..28cc234 100644
--- a/sdk/compiler/cmake/linux/NvBlastExtShaders.cmake
+++ b/sdk/compiler/cmake/linux/NvBlastExtShaders.cmake
@@ -20,4 +20,4 @@ SET(BLASTEXT_COMPILE_DEFS
SET(BLAST_EXT_SHARED_LIB_TYPE SHARED)
-SET(BLASTEXT_PLATFORM_COMPILE_OPTIONS "-Wno-unknown-pragmas" "-pedantic")
+SET(BLASTEXT_PLATFORM_COMPILE_OPTIONS "-Wno-unknown-pragmas" "-Wno-return-type-c-linkage" "-pedantic")
diff --git a/sdk/extensions/assetutils/include/NvBlastExtAssetUtils.h b/sdk/extensions/assetutils/include/NvBlastExtAssetUtils.h
index 3d5b3b9..3786ea3 100644
--- a/sdk/extensions/assetutils/include/NvBlastExtAssetUtils.h
+++ b/sdk/extensions/assetutils/include/NvBlastExtAssetUtils.h
@@ -77,30 +77,48 @@ New bond descriptors may be given to bond support chunks from different componen
An NvBlastAsset may appear more than once in the components array.
+This function will call NvBlastEnsureAssetExactSupportCoverage on the returned chunk descriptors. It will also
+call NvBlastReorderAssetDescChunks if the user passes in valid arrays for chunkReorderMap and chunkReorderMapSize.
+Otherwise, the user must ensure that the returned chunk descriptors are in a valid order is valid before using them.
+
NOTE: This function allocates memory using the allocator in NvBlastGlobals, to create the new chunk and bond
descriptor arrays referenced in the returned NvBlastAssetDesc. The user must free this memory after use with
NVBLAST_FREE appied to the pointers in the returned NvBlastAssetDesc.
-\param[in] components An array of assets to merge, of size componentCount.
-\param[in] rotations An array of rotations to apply to the geometric data in the chunks and bonds,
- stored quaternion format. The quaternions MUST be normalized. If NULL, no rotations are applied.
- If not NULL, the array must be of size componentCount.
-\param[in] translations An array of transforms to apply to the geometric data in the chunks and bonds.
- If NULL, no translations are applied. If not NULL, the array must be of size componentCount.
-\param[in] componentCount The size of the components and relativeTransforms arrays.
-\param[in] newBondDescs Descriptors of type NvBlastExtAssetUtilsBondDesc for new bonds between components, of size newBondCount. If NULL, newBondCount must be 0.
-\param[in] newBondCount The size of the newBondDescs array.
+\param[in] components An array of assets to merge, of size componentCount.
+\param[in] scales An array of scales to apply to the geometric data in the chunks and bonds.
+ If NULL, no scales are applied. If not NULL, the array must be of size componentCount.
+\param[in] rotations An array of rotations to apply to the geometric data in the chunks and bonds,
+ stored quaternion format. The quaternions MUST be normalized. If NULL, no rotations are applied.
+ If not NULL, the array must be of size componentCount.
+\param[in] translations An array of translations to apply to the geometric data in the chunks and bonds.
+ If NULL, no translations are applied. If not NULL, the array must be of size componentCount.
+\param[in] componentCount The size of the components and relativeTransforms arrays.
+\param[in] newBondDescs Descriptors of type NvBlastExtAssetUtilsBondDesc for new bonds between components, of size newBondCount. If NULL, newBondCount must be 0.
+\param[in] newBondCount The size of the newBondDescs array.
+\param[in] chunkIndexOffsets If not NULL, must point to a uin32_t array of size componentCount. It will be filled with the starting elements in chunkReorderMap corresponding to
+ each component.
+\param[in] chunkReorderMap If not NULL, the returned descriptor is run through NvBlastReorderAssetDescChunks, to ensure that it is a valid asset descriptor. In the process, chunks
+ may be reordered (in addition to their natural re-indexing due to them all being placed in one array). To map from the old chunk indexing for the various
+ component assets to the chunk indexing used in the returned descriptor, set chunkReorderMap to point to a uin32_t array of size equal to the total number
+ of chunks in all components, and pass in a non-NULL value to chunkIndexOffsets as described above. Then, for component index c and chunk index k within
+ that component, the new chunk index is given by: index = chunkReorderMap[ k + chunkIndexOffsets[c] ].
+\param[in] chunkReorderMapSize The size of the array passed into chunkReorderMap, if chunkReorderMap is not NULL. This is for safety, so that this function does not overwrite chunkReorderMap.
\return an asset descriptor that will build an asset which merges the components, using NvBlastCreateAsset.
*/
NVBLAST_API NvBlastAssetDesc NvBlastExtAssetUtilsMergeAssets
(
const NvBlastAsset** components,
+ const NvcVec3* scales,
const NvcQuat* rotations,
const NvcVec3* translations,
uint32_t componentCount,
const NvBlastExtAssetUtilsBondDesc* newBondDescs,
- uint32_t newBondCount
+ uint32_t newBondCount,
+ uint32_t* chunkIndexOffsets,
+ uint32_t* chunkReorderMap,
+ uint32_t chunkReorderMapSize
);
diff --git a/sdk/extensions/assetutils/source/NvBlastExtAssetUtils.cpp b/sdk/extensions/assetutils/source/NvBlastExtAssetUtils.cpp
index 956764e..0768663 100644
--- a/sdk/extensions/assetutils/source/NvBlastExtAssetUtils.cpp
+++ b/sdk/extensions/assetutils/source/NvBlastExtAssetUtils.cpp
@@ -212,11 +212,15 @@ NvBlastAsset* NvBlastExtAssetUtilsAddWorldBonds
NvBlastAssetDesc NvBlastExtAssetUtilsMergeAssets
(
const NvBlastAsset** components,
+ const NvcVec3* scales,
const NvcQuat* rotations,
const NvcVec3* translations,
uint32_t componentCount,
const NvBlastExtAssetUtilsBondDesc* newBondDescs,
- uint32_t newBondCount
+ uint32_t newBondCount,
+ uint32_t* chunkIndexOffsets,
+ uint32_t* chunkReorderMap,
+ uint32_t chunkReorderMapSize
)
{
// Count the total number of chunks and bonds in the new asset
@@ -233,7 +237,11 @@ NvBlastAssetDesc NvBlastExtAssetUtilsMergeAssets
NvBlastBondDesc* bondDescs = static_cast<NvBlastBondDesc*>(NVBLAST_ALLOC(totalBondCount * sizeof(NvBlastBondDesc)));
// Create a list of chunk index offsets per component
- uint32_t* chunkIndexOffsets = static_cast<uint32_t*>(NvBlastAlloca(componentCount * sizeof(uint32_t)));
+ uint32_t* offsetStackAlloc = static_cast<uint32_t*>(NvBlastAlloca(componentCount * sizeof(uint32_t)));
+ if (chunkIndexOffsets == nullptr)
+ {
+ chunkIndexOffsets = offsetStackAlloc; // Use local stack alloc if no array is provided
+ }
// Fill the chunk and bond descriptors from the components
uint32_t chunkCount = 0;
@@ -244,6 +252,14 @@ NvBlastAssetDesc NvBlastExtAssetUtilsMergeAssets
uint32_t componentChunkCount;
uint32_t componentBondCount;
fillChunkAndBondDescriptorsFromAsset(componentChunkCount, componentBondCount, chunkDescs + chunkCount, bondDescs + bondCount, components[c]);
+ // Fix chunks' parent indices
+ for (uint32_t i = 0; i < componentChunkCount; ++i)
+ {
+ if (!isInvalidIndex(chunkDescs[chunkCount + i].parentChunkIndex))
+ {
+ chunkDescs[chunkCount + i].parentChunkIndex += chunkCount;
+ }
+ }
// Fix bonds' chunk indices
for (uint32_t i = 0; i < componentBondCount; ++i)
{
@@ -257,6 +273,34 @@ NvBlastAssetDesc NvBlastExtAssetUtilsMergeAssets
}
}
// Transform geometric data
+ if (scales != nullptr)
+ {
+ const NvcVec3& S = scales[c];
+ NvcVec3 cofS = { S.y * S.z, S.z * S.x, S.x * S.y };
+ float absDetS = S.x * S.y * S.z;
+ const float sgnDetS = absDetS < 0.0f ? -1.0f : 1.0f;
+ absDetS *= sgnDetS;
+ for (uint32_t i = 0; i < componentChunkCount; ++i)
+ {
+ scale(reinterpret_cast<NvcVec3&>(chunkDescs[chunkCount + i].centroid), S);
+ chunkDescs[chunkCount + i].volume *= absDetS;
+ }
+ for (uint32_t i = 0; i < componentBondCount; ++i)
+ {
+ NvBlastBond& bond = bondDescs[bondCount + i].bond;
+ scale(reinterpret_cast<NvcVec3&>(bond.normal), cofS);
+ float renorm = sqrtf(bond.normal[0] * bond.normal[0] + bond.normal[1] * bond.normal[1] + bond.normal[2] * bond.normal[2]);
+ bond.area *= renorm;
+ if (renorm != 0)
+ {
+ renorm = sgnDetS / renorm;
+ bond.normal[0] *= renorm;
+ bond.normal[1] *= renorm;
+ bond.normal[2] *= renorm;
+ }
+ scale(reinterpret_cast<NvcVec3&>(bond.centroid), S);
+ }
+ }
if (rotations != nullptr)
{
for (uint32_t i = 0; i < componentChunkCount; ++i)
@@ -302,6 +346,29 @@ NvBlastAssetDesc NvBlastExtAssetUtilsMergeAssets
assetDesc.bondCount = bondCount;
assetDesc.bondDescs = bondDescs;
+ // Massage the descriptors so that they are valid for scratch creation
+ void* scratch = NVBLAST_ALLOC(chunkCount * sizeof(NvBlastChunkDesc)); // Enough for NvBlastEnsureAssetExactSupportCoverage and NvBlastReorderAssetDescChunks
+
+ NvBlastEnsureAssetExactSupportCoverage(chunkDescs, chunkCount, scratch, logLL);
+
+ if (chunkReorderMapSize < chunkCount)
+ {
+ if (chunkReorderMap != nullptr)
+ {
+ // Chunk reorder map is not large enough. Fill it with invalid indices and don't use it.
+ memset(chunkReorderMap, 0xFF, chunkReorderMapSize * sizeof(uint32_t));
+ NVBLAST_LOG_WARNING("NvBlastExtAssetUtilsMergeAssets: insufficient chunkReorderMap array passed in. NvBlastReorderAssetDescChunks will not be used.");
+ }
+ chunkReorderMap = nullptr; // Don't use
+ }
+
+ if (chunkReorderMap != nullptr)
+ {
+ NvBlastReorderAssetDescChunks(chunkDescs, chunkCount, bondDescs, bondCount, chunkReorderMap, true, scratch, logLL);
+ }
+
+ NVBLAST_FREE(scratch);
+
return assetDesc;
}
@@ -382,7 +449,7 @@ void NvBlastExtAssetTransformInPlace(NvBlastAsset* asset, const NvcVec3* scaling
cofS.y = S.z * S.x;
cofS.z = S.x * S.y;
absDetS = S.x * S.y * S.z;
- sgnDetS = absDetS < 0 ? -1 : 1;
+ sgnDetS = absDetS < 0.0f ? -1.0f : 1.0f;
absDetS *= sgnDetS;
}
diff --git a/sdk/extensions/authoring/include/NvBlastExtAuthoring.h b/sdk/extensions/authoring/include/NvBlastExtAuthoring.h
index ce21c65..dd49677 100644
--- a/sdk/extensions/authoring/include/NvBlastExtAuthoring.h
+++ b/sdk/extensions/authoring/include/NvBlastExtAuthoring.h
@@ -47,9 +47,13 @@ namespace Nv
class ConvexMeshBuilder;
class BlastBondGenerator;
class MeshCleaner;
+ struct CollisionParams;
+ struct CollisionHull;
}
}
+struct NvBlastExtAssetUtilsBondDesc;
+
/**
Constructs mesh object from array of triangles.
User should call release() after usage.
@@ -96,6 +100,37 @@ Create ConvexMeshBuilder
NVBLAST_API Nv::Blast::ConvexMeshBuilder* NvBlastExtAuthoringCreateConvexMeshBuilder(physx::PxCooking* cooking,
physx::PxPhysicsInsertionCallback* insertionCallback);
+
+/**
+Transforms collision hull in place using scale, rotation, transform.
+\param[in, out] hull Pointer to the hull to be transformed (modified).
+\param[in] scale Pointer to scale to be applied. Can be nullptr.
+\param[in] rotation Pointer to rotation to be applied. Can be nullptr.
+\param[in] translation Pointer to translation to be applied. Can be nullptr.
+*/
+NVBLAST_API void NvBlastExtAuthoringTransformCollisionHullInPlace
+(
+ Nv::Blast::CollisionHull* hull,
+ const physx::PxVec3* scaling,
+ const physx::PxQuat* rotation,
+ const physx::PxVec3* translation
+);
+
+/**
+Transforms collision hull in place using scale, rotation, transform.
+\param[in] hull Pointer to the hull to be transformed (modified).
+\param[in] scale Pointer to scale to be applied. Can be nullptr.
+\param[in] rotation Pointer to rotation to be applied. Can be nullptr.
+\param[in] translation Pointer to translation to be applied. Can be nullptr.
+*/
+NVBLAST_API Nv::Blast::CollisionHull* NvBlastExtAuthoringTransformCollisionHull
+(
+ const Nv::Blast::CollisionHull* hull,
+ const physx::PxVec3* scaling,
+ const physx::PxQuat* rotation,
+ const physx::PxVec3* translation
+);
+
/**
Performs pending fractures and generates fractured asset, render and collision geometry
@@ -104,10 +139,11 @@ Performs pending fractures and generates fractured asset, render and collision g
\param[in] collisionBuilder Collision builder created by NvBlastExtAuthoringCreateConvexMeshBuilder
\param[in] defaultSupportDepth All new chunks will be marked as support if its depth equal to defaultSupportDepth.
By default leaves (chunks without children) marked as support.
+\param[in] collisionParam Parameters of collision hulls generation.
\return Authoring result
*/
NVBLAST_API Nv::Blast::AuthoringResult* NvBlastExtAuthoringProcessFracture(Nv::Blast::FractureTool& fTool,
- Nv::Blast::BlastBondGenerator& bondGenerator, Nv::Blast::ConvexMeshBuilder& collisionBuilder, int32_t defaultSupportDepth = -1);
+ Nv::Blast::BlastBondGenerator& bondGenerator, Nv::Blast::ConvexMeshBuilder& collisionBuilder, const Nv::Blast::CollisionParams& collisionParam, int32_t defaultSupportDepth = -1);
/**
@@ -116,4 +152,40 @@ NVBLAST_API Nv::Blast::AuthoringResult* NvBlastExtAuthoringProcessFracture(Nv::B
*/
NVBLAST_API Nv::Blast::MeshCleaner* NvBlastExtAuthoringCreateMeshCleaner();
+
+
+/**
+Finds bonds connecting chunks in a list of assets
+
+New bond descriptors may be given to bond support chunks from different components.
+
+An NvBlastAsset may appear more than once in the components array.
+
+NOTE: This function allocates memory using the allocator in NvBlastGlobals, to create the new bond
+descriptor arrays returned. The user must free this memory after use with NVBLAST_FREE
+
+\param[in] components An array of assets to merge, of size componentCount.
+\param[in] scales If not NULL, an array of size componentCount of scales to apply to the geometric data in the chunks and bonds. If NULL, no scaling is applied.
+\param[in] rotations If not NULL, an array of size componentCount of rotations to apply to the geometric data in the chunks and bonds. The quaternions MUST be normalized.
+ If NULL, no rotations are applied.
+\param[in] translations If not NULL, an array of of size componentCount of translations to apply to the geometric data in the chunks and bonds. If NULL, no translations are applied.
+\param[in] convexHullOffsets For each component, an array of chunkSize+1 specifying the start of the convex hulls for that chunk inside the chunkHulls array for that component.
+\param[in] chunkHulls For each component, an array of CollisionHull* specifying the collision geometry for the chunks in that component.
+\param[in] componentCount The size of the components and relativeTransforms arrays.
+\param[out] newBondDescs Descriptors of type NvBlastExtAssetUtilsBondDesc for new bonds between components.
+
+\return the number of bonds in newBondDescs
+*/
+NVBLAST_API uint32_t NvBlastExtAuthoringFindAssetConnectingBonds
+(
+ const NvBlastAsset** components,
+ const physx::PxVec3* scales,
+ const physx::PxQuat* rotations,
+ const physx::PxVec3* translations,
+ const uint32_t** convexHullOffsets,
+ const Nv::Blast::CollisionHull*** chunkHulls,
+ uint32_t componentCount,
+ NvBlastExtAssetUtilsBondDesc*& newBondDescs
+);
+
#endif // ifndef NVBLASTAUTHORING_H
diff --git a/sdk/extensions/authoring/include/NvBlastExtAuthoringBondGenerator.h b/sdk/extensions/authoring/include/NvBlastExtAuthoringBondGenerator.h
index 4f5d0e6..0151ed9 100644
--- a/sdk/extensions/authoring/include/NvBlastExtAuthoringBondGenerator.h
+++ b/sdk/extensions/authoring/include/NvBlastExtAuthoringBondGenerator.h
@@ -146,9 +146,25 @@ public:
\return Number of created bonds
*/
virtual int32_t bondsFromPrefractured(uint32_t meshCount, const uint32_t* geometryOffset, const Triangle* geometry,
- const bool*& chunkIsSupport, NvBlastBondDesc*& resultBondDescs,
+ const bool* chunkIsSupport, NvBlastBondDesc*& resultBondDescs,
BondGenerationConfig conf = BondGenerationConfig()) = 0;
-
+
+ /**
+ Creates bond description for prefractured meshes, when there is no info about which chunks should be connected with bond.
+ This uses the same process as bondsFromPrefractured using the BondGenMode::AVERAGE mode however the existing collision data is used.
+ \note User should call NVBLAST_FREE for resultBondDescs when it not needed anymore.
+ \param[in] meshCount Number of meshes
+ \param[in] convexHullOffset Pointer to array of convex hull offsets for each mesh.
+ Containts meshCount + 1 element, last one is total number of hulls in the geometry
+ \param[in] chunkHulls Pointer to array of convex hulls.
+ Hulls from convexHullOffset[i] to convexHullOffset[i+1] correspond to i-th mesh.
+ \param[in] chunkIsSupport Pointer to array of flags, if true - chunk is support. Array size should be equal to chunk count in tool.
+ \param[in] meshGroups Pointer to array of group ids for each mesh, bonds will not be generated between meshs of the same group. If null each mesh is assumed to be in it's own group.
+ \param[out] resultBondDescs Pointer to array of result bonds.
+ \return Number of created bonds
+ */
+ virtual int32_t bondsFromPrefractured(uint32_t meshCount, const uint32_t* convexHullOffset, const CollisionHull** chunkHulls,
+ const bool* chunkIsSupport, const uint32_t* meshGroups, NvBlastBondDesc*& resultBondDescs) = 0;
};
} // namespace Blast
diff --git a/sdk/extensions/authoring/include/NvBlastExtAuthoringCollisionBuilder.h b/sdk/extensions/authoring/include/NvBlastExtAuthoringCollisionBuilder.h
index 1e851bb..5b202bd 100644
--- a/sdk/extensions/authoring/include/NvBlastExtAuthoringCollisionBuilder.h
+++ b/sdk/extensions/authoring/include/NvBlastExtAuthoringCollisionBuilder.h
@@ -46,6 +46,22 @@ namespace Blast
{
struct CollisionHull;
+struct Triangle;
+
+struct CollisionParams
+{
+ CollisionParams()
+ {
+ setDefault();
+ }
+ void setDefault()
+ {
+ maximumNumberOfHulls = 8;
+ voxelGridResolution = 1000000;
+ }
+ uint32_t maximumNumberOfHulls; // Maximum number of convex hull generated for one chunk. If equal to 1 convex decomposition is disabled.
+ uint32_t voxelGridResolution; // Voxel grid resolution used for chunk convex decomposition.
+};
/**
ConvexMeshBuilder provides routine to build collision hulls from array of vertices.
@@ -103,6 +119,12 @@ public:
*/
virtual void trimCollisionGeometry(uint32_t chunksCount, CollisionHull** in, const uint32_t* chunkDepth) = 0;
+
+ /**
+ Create mesh convex decomposition
+ */
+ virtual int32_t buildMeshConvexDecomposition(const Nv::Blast::Triangle* mesh, uint32_t triangleCount, const CollisionParams& params, CollisionHull** &convexes) = 0;
+
};
} // namespace Blast
diff --git a/sdk/extensions/authoring/include/NvBlastExtAuthoringFractureTool.h b/sdk/extensions/authoring/include/NvBlastExtAuthoringFractureTool.h
index 9cdb6c2..b23ca18 100644
--- a/sdk/extensions/authoring/include/NvBlastExtAuthoringFractureTool.h
+++ b/sdk/extensions/authoring/include/NvBlastExtAuthoringFractureTool.h
@@ -92,7 +92,7 @@ struct SlicingConfiguration
/**
Cutting surface resolution.
*/
- int32_t surfaceResolution = 1;
+ uint32_t surfaceResolution = 1;
};
@@ -355,6 +355,15 @@ public:
\return true if mesh contains open edges
*/
virtual bool isMeshContainOpenEdges(const Mesh* input) = 0;
+
+ /**
+ Delete all children for specified chunk (also recursively delete chidren of children).
+ \param[in] chunkId Chunk ID which children should be deleted
+ \return true if one or more chunks were removed
+ */
+ virtual bool deleteAllChildrenOfChunk(int32_t chunkId) = 0;
+
+ virtual void uniteChunks(uint32_t maxAtLevel, uint32_t maxGroupSize) = 0;
};
} // namespace Blast
diff --git a/sdk/extensions/authoring/include/NvBlastExtAuthoringTypes.h b/sdk/extensions/authoring/include/NvBlastExtAuthoringTypes.h
index 5f14b04..512beca 100644
--- a/sdk/extensions/authoring/include/NvBlastExtAuthoringTypes.h
+++ b/sdk/extensions/authoring/include/NvBlastExtAuthoringTypes.h
@@ -146,11 +146,11 @@ struct Facet
{
int32_t firstEdgeNumber;
uint32_t edgesCount;
- int32_t userData;
+ int64_t userData;
int32_t materialId;
int32_t smoothingGroup;
- Facet(int32_t fEdge = 0, uint32_t eCount = 0, int32_t materialId = 0, int32_t userData = 0, int32_t smoothingGroup = 0) : firstEdgeNumber(fEdge), edgesCount(eCount), userData(userData), materialId(materialId), smoothingGroup(smoothingGroup) {}
+ Facet(int32_t fEdge = 0, uint32_t eCount = 0, int32_t materialId = 0, int64_t userData = 0, int32_t smoothingGroup = 0) : firstEdgeNumber(fEdge), edgesCount(eCount), userData(userData), materialId(materialId), smoothingGroup(smoothingGroup) {}
};
/**
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp
index 787cb6b..b024bb7 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp
@@ -19,6 +19,7 @@
#include "NvBlast.h"
#include "NvBlastGlobals.h"
#include "NvBlastExtPxAsset.h"
+#include "NvBlastExtAssetUtils.h"
#include <algorithm>
#include <memory>
@@ -26,8 +27,8 @@
using namespace Nv::Blast;
using namespace physx;
-#define SAFE_ARRAY_NEW(T, x) ((x) > 0) ? new T[x] : nullptr;
-#define SAFE_ARRAY_DELETE(x) if (x != nullptr) {delete[] x; x = nullptr;}
+#define SAFE_ARRAY_NEW(T, x) ((x) > 0) ? reinterpret_cast<T*>(NVBLAST_ALLOC(sizeof(T) * (x))) : nullptr;
+#define SAFE_ARRAY_DELETE(x) if (x != nullptr) {NVBLAST_FREE(x); x = nullptr;}
Mesh* NvBlastExtAuthoringCreateMesh(const PxVec3* position, const PxVec3* normals, const PxVec2* uv, uint32_t verticesCount, const uint32_t* indices, uint32_t indicesCount)
{
@@ -59,34 +60,139 @@ ConvexMeshBuilder* NvBlastExtAuthoringCreateConvexMeshBuilder(PxCooking* cooking
return new ConvexMeshBuilderImpl(cooking, insertionCallback);
}
-void buildPhysicsChunks(ConvexMeshBuilder& collisionBuilder, AuthoringResult& result)
+
+void NvBlastExtAuthoringTransformCollisionHullInPlace(CollisionHull* hull, const physx::PxVec3* scaling, const physx::PxQuat* rotation, const physx::PxVec3* translation)
+{
+ // Local copies of scaling (S), rotation (R), and translation (T)
+ physx::PxVec3 S = { 1, 1, 1 };
+ physx::PxQuat R = { 0, 0, 0, 1 };
+ physx::PxVec3 T = { 0, 0, 0 };
+ physx::PxVec3 cofS = { 1, 1, 1 };
+ float sgnDetS = 1;
+
+ {
+ if (rotation)
+ {
+ R = *rotation;
+ }
+
+ if (scaling)
+ {
+ S = *scaling;
+ cofS.x = S.y * S.z;
+ cofS.y = S.z * S.x;
+ cofS.z = S.x * S.y;
+ sgnDetS = (S.x * S.y * S.z < 0) ? -1 : 1;
+ }
+
+ if (translation)
+ {
+ T = *translation;
+ }
+ }
+
+ const uint32_t pointCount = hull->pointsCount;
+ for (uint32_t pi = 0; pi < pointCount; pi++)
+ {
+ physx::PxVec3& p = hull->points[pi];
+ p = (R.rotate(p.multiply(S)) + T);
+ }
+
+ const uint32_t planeCount = hull->polygonDataCount;
+ for (uint32_t pi = 0; pi < planeCount; pi++)
+ {
+ float* plane = hull->polygonData[pi].mPlane;
+ physx::PxPlane pxPlane(plane[0], plane[1], plane[2], plane[3]);
+ PxVec3 transformedNormal = sgnDetS*R.rotate(pxPlane.n.multiply(cofS)).getNormalized();
+ PxVec3 transformedPt = R.rotate(pxPlane.pointInPlane().multiply(S)) + T;
+
+ physx::PxPlane transformedPlane(transformedPt, transformedNormal);
+ plane[0] = transformedPlane.n[0];
+ plane[1] = transformedPlane.n[1];
+ plane[2] = transformedPlane.n[2];
+ plane[3] = transformedPlane.d;
+ }
+}
+
+
+CollisionHull* NvBlastExtAuthoringTransformCollisionHull(const CollisionHull* hull, const physx::PxVec3* scaling, const physx::PxQuat* rotation, const physx::PxVec3* translation)
+{
+ CollisionHullImpl* ret = new CollisionHullImpl(*hull);
+ NvBlastExtAuthoringTransformCollisionHullInPlace(ret, scaling, rotation, translation);
+ return ret;
+}
+
+void buildPhysicsChunks(ConvexMeshBuilder& collisionBuilder, AuthoringResult& result, const CollisionParams& params)
{
uint32_t chunkCount = (uint32_t)result.chunkCount;
- result.collisionHullOffset = SAFE_ARRAY_NEW(uint32_t, chunkCount + 1);
- result.collisionHullOffset[0] = 0;
- result.collisionHull = SAFE_ARRAY_NEW(CollisionHull*, chunkCount);
- result.physicsSubchunks = SAFE_ARRAY_NEW(ExtPxSubchunk, chunkCount);
- result.physicsChunks = SAFE_ARRAY_NEW(ExtPxChunk, chunkCount);
- for (uint32_t i = 0; i < chunkCount; ++i)
+ if (params.maximumNumberOfHulls == 1)
{
- std::vector<physx::PxVec3> vertices;
- for (uint32_t p = result.geometryOffset[i]; p < result.geometryOffset[i+1]; ++p)
+ result.collisionHullOffset = SAFE_ARRAY_NEW(uint32_t, chunkCount + 1);
+ result.collisionHullOffset[0] = 0;
+ result.collisionHull = SAFE_ARRAY_NEW(CollisionHull*, chunkCount);
+ result.physicsSubchunks = SAFE_ARRAY_NEW(ExtPxSubchunk, chunkCount);
+ result.physicsChunks = SAFE_ARRAY_NEW(ExtPxChunk, chunkCount);
+ for (uint32_t i = 0; i < chunkCount; ++i)
{
- Nv::Blast::Triangle& tri = result.geometry[p];
- vertices.push_back(tri.a.p);
- vertices.push_back(tri.b.p);
- vertices.push_back(tri.c.p);
+ std::vector<physx::PxVec3> vertices;
+ for (uint32_t p = result.geometryOffset[i]; p < result.geometryOffset[i + 1]; ++p)
+ {
+ Nv::Blast::Triangle& tri = result.geometry[p];
+ vertices.push_back(tri.a.p);
+ vertices.push_back(tri.b.p);
+ vertices.push_back(tri.c.p);
+ }
+ result.collisionHullOffset[i + 1] = result.collisionHullOffset[i] + 1;
+ result.collisionHull[i] = collisionBuilder.buildCollisionGeometry((uint32_t)vertices.size(), vertices.data());
+ result.physicsSubchunks[i].transform = physx::PxTransform(physx::PxIdentity);
+ result.physicsSubchunks[i].geometry = physx::PxConvexMeshGeometry(collisionBuilder.buildConvexMesh(*result.collisionHull[i]));
+
+ result.physicsChunks[i].isStatic = false;
+ result.physicsChunks[i].subchunkCount = 1;
+ result.physicsChunks[i].firstSubchunkIndex = i;
+ //outPhysicsChunks.get()[i].subchunks = &outPhysicsSubchunks[i];
}
+ }
+ else
+ {
+ std::vector<std::vector<CollisionHull*> > hulls(chunkCount);
+ int32_t totalHulls = 0;
+ for (uint32_t i = 0; i < chunkCount; ++i)
+ {
+ CollisionHull** tempHull;
- result.collisionHullOffset[i + 1] = result.collisionHullOffset[i] + 1;
- result.collisionHull[i] = collisionBuilder.buildCollisionGeometry((uint32_t)vertices.size(), vertices.data());
- result.physicsSubchunks[i].transform = physx::PxTransform(physx::PxIdentity);
- result.physicsSubchunks[i].geometry = physx::PxConvexMeshGeometry(collisionBuilder.buildConvexMesh(*result.collisionHull[i]));
+ int32_t newHulls = collisionBuilder.buildMeshConvexDecomposition(result.geometry + result.geometryOffset[i],
+ result.geometryOffset[i + 1] - result.geometryOffset[i], params, tempHull);
+ totalHulls += newHulls;
+ for (int32_t h = 0; h < newHulls; ++h)
+ {
+ hulls[i].push_back(tempHull[h]);
+ }
+ SAFE_ARRAY_DELETE(tempHull);
+ }
- result.physicsChunks[i].isStatic = false;
- result.physicsChunks[i].subchunkCount = 1;
- result.physicsChunks[i].firstSubchunkIndex = i;
- //outPhysicsChunks.get()[i].subchunks = &outPhysicsSubchunks[i];
+ result.collisionHullOffset = SAFE_ARRAY_NEW(uint32_t, chunkCount + 1);
+ result.collisionHullOffset[0] = 0;
+ result.collisionHull = SAFE_ARRAY_NEW(CollisionHull*, totalHulls);
+ result.physicsSubchunks = SAFE_ARRAY_NEW(ExtPxSubchunk, totalHulls);
+ result.physicsChunks = SAFE_ARRAY_NEW(ExtPxChunk, chunkCount);
+
+ int32_t firstSubchunk = 0;
+ for (uint32_t i = 0; i < chunkCount; ++i)
+ {
+ result.collisionHullOffset[i + 1] = result.collisionHullOffset[i] + hulls[i].size();
+ int32_t off = result.collisionHullOffset[i];
+ for (uint32_t subhull = 0; subhull < hulls[i].size(); ++subhull)
+ {
+ result.collisionHull[off + subhull] = hulls[i][subhull];
+ result.physicsSubchunks[firstSubchunk + subhull].transform = physx::PxTransform(physx::PxIdentity);
+ result.physicsSubchunks[firstSubchunk + subhull].geometry = physx::PxConvexMeshGeometry(collisionBuilder.buildConvexMesh(*hulls[i][subhull]));
+ }
+ result.physicsChunks[i].isStatic = false;
+ result.physicsChunks[i].subchunkCount = static_cast<uint32_t>(hulls[i].size());
+ result.physicsChunks[i].firstSubchunkIndex = firstSubchunk;
+ firstSubchunk += result.physicsChunks[i].subchunkCount;
+ }
}
}
@@ -121,7 +227,7 @@ struct AuthoringResultImpl : public AuthoringResult
}
};
-AuthoringResult* NvBlastExtAuthoringProcessFracture(FractureTool& fTool, BlastBondGenerator& bondGenerator, ConvexMeshBuilder& collisionBuilder, int32_t defaultSupportDepth)
+AuthoringResult* NvBlastExtAuthoringProcessFracture(FractureTool& fTool, BlastBondGenerator& bondGenerator, ConvexMeshBuilder& collisionBuilder, const CollisionParams& collisionParam, int32_t defaultSupportDepth)
{
fTool.finalizeFracturing();
const uint32_t chunkCount = fTool.getChunkCount();
@@ -220,7 +326,7 @@ AuthoringResult* NvBlastExtAuthoringProcessFracture(FractureTool& fTool, BlastBo
//std::cout << "MAX: " << maxX << ", " << maxY << ", " << maxZ << std::endl;
// prepare physics data (convexes)
- buildPhysicsChunks(collisionBuilder, aResult);
+ buildPhysicsChunks(collisionBuilder, aResult, collisionParam);
// set NvBlastChunk volume from Px geometry
for (uint32_t i = 0; i < chunkCount; i++)
@@ -259,4 +365,104 @@ AuthoringResult* NvBlastExtAuthoringProcessFracture(FractureTool& fTool, BlastBo
ret->materialCount = 0;
ret->materialNames = nullptr;
return ret;
-} \ No newline at end of file
+}
+
+uint32_t NvBlastExtAuthoringFindAssetConnectingBonds
+(
+ const NvBlastAsset** components,
+ const physx::PxVec3* scales,
+ const physx::PxQuat* rotations,
+ const physx::PxVec3* translations,
+ const uint32_t** convexHullOffsets,
+ const CollisionHull*** chunkHulls,
+ uint32_t componentCount,
+ NvBlastExtAssetUtilsBondDesc*& newBondDescs
+)
+{
+ //We don't need to use any of the cooking related parts of this
+ BlastBondGeneratorImpl bondGenerator(nullptr, nullptr);
+
+ std::vector<uint32_t> componentChunkOffsets;
+ componentChunkOffsets.reserve(componentCount + 1);
+ componentChunkOffsets.push_back(0);
+
+ std::vector<uint32_t> combinedConvexHullOffsets;
+ std::vector<const CollisionHull*> combinedConvexHulls;
+ std::vector<CollisionHull*> hullsToRelease;
+ combinedConvexHullOffsets.push_back(0);
+
+ std::vector<uint32_t> originalComponentIndex;
+
+ const physx::PxVec3 identityScale(1);
+
+ //Combine our hull lists into a single combined list for bondsFromPrefractured
+ for (uint32_t c = 0; c < componentCount; c++)
+ {
+ const uint32_t chunkCount = NvBlastAssetGetChunkCount(components[c], &logLL);
+ const physx::PxVec3* scale = scales ? scales + c : nullptr;
+ const physx::PxQuat* rotation = rotations ? rotations + c : nullptr;
+ const physx::PxVec3* translation = translations ? translations + c : nullptr;
+
+ componentChunkOffsets.push_back(chunkCount + componentChunkOffsets.back());
+ for (uint32_t chunk = 0; chunk < chunkCount; chunk++)
+ {
+ const uint32_t hullsStart = convexHullOffsets[c][chunk];
+ const uint32_t hullsEnd = convexHullOffsets[c][chunk + 1];
+ for (uint32_t hull = hullsStart; hull < hullsEnd; hull++)
+ {
+ if ((scale != nullptr && *scale != identityScale) || (rotation != nullptr && !rotation->isIdentity()) || (translation != nullptr && !translation->isZero()))
+ {
+ hullsToRelease.emplace_back(NvBlastExtAuthoringTransformCollisionHull(chunkHulls[c][hull], scale, rotation, translation));
+ combinedConvexHulls.emplace_back(hullsToRelease.back());
+ }
+ else
+ {
+ //No need to transform
+ combinedConvexHulls.emplace_back(chunkHulls[c][hull]);
+ }
+ }
+ combinedConvexHullOffsets.push_back((hullsEnd - hullsStart) + combinedConvexHullOffsets.back());
+ originalComponentIndex.push_back(c);
+ }
+ }
+ const uint32_t totalChunkCount = componentChunkOffsets.back();
+ //Can't use std::vector<bool> since we need a bool* later
+ std::unique_ptr<bool[]> isSupportChunk(new bool[totalChunkCount]);
+ for (uint32_t c = 0; c < componentCount; c++)
+ {
+ const uint32_t chunkCount = componentChunkOffsets[c + 1] - componentChunkOffsets[c];
+ NvBlastSupportGraph supportGraph = NvBlastAssetGetSupportGraph(components[c], &logLL);
+ for (uint32_t chunk = 0; chunk < chunkCount; chunk++)
+ {
+ auto chunkIndiciesEnd = supportGraph.chunkIndices + supportGraph.nodeCount;
+ isSupportChunk[chunk + componentChunkOffsets[c]] = (std::find(supportGraph.chunkIndices, chunkIndiciesEnd, chunk) != chunkIndiciesEnd);
+ }
+ }
+
+ //Find the bonds
+ NvBlastBondDesc* newBonds = nullptr;
+ const int32_t newBoundCount = bondGenerator.bondsFromPrefractured(totalChunkCount, combinedConvexHullOffsets.data(), combinedConvexHulls.data(), isSupportChunk.get(), originalComponentIndex.data(), newBonds);
+
+ //Convert the bonds back to per-component chunks
+ newBondDescs = SAFE_ARRAY_NEW(NvBlastExtAssetUtilsBondDesc, newBoundCount);
+ for (int32_t nb = 0; nb < newBoundCount; ++nb)
+ {
+ newBondDescs[nb].bond = newBonds[nb].bond;
+ for (uint32_t ci = 0; ci < 2; ++ci)
+ {
+ uint32_t absChunkIdx = newBonds[nb].chunkIndices[ci];
+ uint32_t componentIdx = originalComponentIndex[absChunkIdx];
+ newBondDescs[nb].componentIndices[ci] = componentIdx;
+ newBondDescs[nb].chunkIndices[ci] = absChunkIdx - componentChunkOffsets[componentIdx];
+ }
+ }
+ //Don't need this anymore
+ NVBLAST_FREE(newBonds);
+
+ for (CollisionHull* hull : hullsToRelease)
+ {
+ hull->release();
+ }
+
+ return newBoundCount;
+}
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.cpp
index 40ddec1..4f3f4ff 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.cpp
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.cpp
@@ -594,9 +594,9 @@ void IntersectionTestingAccelerator::setState(const Vertex* pos, const Edge* ed,
}
for (uint32_t i = 0; i < mCubes.size(); ++i)
{
+ if (!mSpatialMap[i].empty())
if (testFacetUnitCubeIntersection(pos, ed, fc, mCubes[i], 0.001f))
{
- if (!mSpatialMap[i].empty())
cellList.push_back(i);
}
}
@@ -643,5 +643,203 @@ void IntersectionTestingAccelerator::setState(const PxVec3& p)
}
+#define SWEEP_RESOLUTION 2048
+
+
+void buildIndex(std::vector<SegmentToIndex>& segm, float offset, float mlt, std::vector<std::vector<uint32_t>>& blocks)
+{
+ std::set<uint32_t> currentEnabled;
+ uint32_t lastBlock = 0;
+ for (uint32_t i = 0; i < segm.size(); ++i)
+ {
+ uint32_t currentBlock = (segm[i].coord - offset) * mlt;
+ if (currentBlock >= SWEEP_RESOLUTION) break;
+ if (currentBlock != lastBlock)
+ {
+ for (uint32_t j = lastBlock + 1; j <= currentBlock; ++j)
+ {
+ for (auto id : currentEnabled)
+ blocks[j].push_back(id);
+ }
+ lastBlock = currentBlock;
+ }
+ if (segm[i].end == false)
+ {
+ blocks[lastBlock].push_back(segm[i].index);
+ currentEnabled.insert(segm[i].index);
+ }
+ else
+ {
+ currentEnabled.erase(segm[i].index);
+ }
+ }
+
+}
+
+
+SweepingAccelerator::SweepingAccelerator(Nv::Blast::Mesh* in)
+{
+ PxBounds3 bnd;
+
+ const Vertex* verts = in->getVertices();
+ const Edge* edges = in->getEdges();
+
+ facetCount = in->getFacetCount();
+
+ foundx.resize(facetCount, 0);
+ foundy.resize(facetCount, 0);
+
+
+ std::vector<SegmentToIndex> xevs;
+ std::vector<SegmentToIndex> yevs;
+ std::vector<SegmentToIndex> zevs;
+
+
+ for (uint32_t i = 0; i < in->getFacetCount(); ++i)
+ {
+ const Facet* fc = in->getFacet(i);
+ bnd.setEmpty();
+ for (uint32_t v = 0; v < fc->edgesCount; ++v)
+ {
+ bnd.include(verts[edges[v + fc->firstEdgeNumber].s].p);
+ }
+ bnd.scaleFast(1.1f);
+ xevs.push_back(SegmentToIndex(bnd.minimum.x, i, false));
+ xevs.push_back(SegmentToIndex(bnd.maximum.x, i, true));
+
+ yevs.push_back(SegmentToIndex(bnd.minimum.y, i, false));
+ yevs.push_back(SegmentToIndex(bnd.maximum.y, i, true));
+
+ zevs.push_back(SegmentToIndex(bnd.minimum.z, i, false));
+ zevs.push_back(SegmentToIndex(bnd.maximum.z, i, true));
+
+ }
+
+ std::sort(xevs.begin(), xevs.end());
+ std::sort(yevs.begin(), yevs.end());
+ std::sort(zevs.begin(), zevs.end());
+
+
+ minimal.x = xevs[0].coord;
+ minimal.y = yevs[0].coord;
+ minimal.z = zevs[0].coord;
+
+
+ maximal.x = xevs.back().coord;
+ maximal.y = yevs.back().coord;
+ maximal.z = zevs.back().coord;
+
+
+ rescale = (maximal - minimal) * 1.01f;
+ rescale.x = 1.0f / rescale.x * SWEEP_RESOLUTION;
+ rescale.y = 1.0f / rescale.y * SWEEP_RESOLUTION;
+ rescale.z = 1.0f / rescale.z * SWEEP_RESOLUTION;
+
+ xSegm.resize(SWEEP_RESOLUTION);
+ ySegm.resize(SWEEP_RESOLUTION);
+ zSegm.resize(SWEEP_RESOLUTION);
+
+
+ buildIndex(xevs, minimal.x, rescale.x, xSegm);
+ buildIndex(yevs, minimal.y, rescale.y, ySegm);
+ buildIndex(zevs, minimal.z, rescale.z, zSegm);
+
+
+ iterId = 1;
+ current = 0;
+}
+
+void SweepingAccelerator::setState(const Vertex* pos, const Edge* ed, const Facet& fc)
+{
+ current = 0;
+ indices.clear();
+
+ PxBounds3 bnd;
+ bnd.setEmpty();
+ for (uint32_t i = 0; i < fc.edgesCount; ++i)
+ {
+ bnd.include(pos[ed[fc.firstEdgeNumber + i].s].p);
+ }
+ bnd.scaleFast(1.1);
+ uint32_t start = (std::max(0.0f, bnd.minimum.x - minimal.x)) * rescale.x;
+ uint32_t end = (std::max(0.0f, bnd.maximum.x - minimal.x)) * rescale.x;
+ for (uint32_t i = start; i <= end && i < SWEEP_RESOLUTION; ++i)
+ {
+ for (auto id : xSegm[i])
+ {
+ foundx[id] = iterId;
+ }
+ }
+ start = (std::max(0.0f, bnd.minimum.y - minimal.y)) * rescale.y;
+ end = (std::max(0.0f, bnd.maximum.y - minimal.y)) * rescale.y;
+ for (uint32_t i = start; i <= end && i < SWEEP_RESOLUTION; ++i)
+ {
+ for (auto id : ySegm[i])
+ {
+ foundy[id] = iterId;
+ }
+ }
+ start = (std::max(0.0f, bnd.minimum.z - minimal.z)) * rescale.z;
+ end = (std::max(0.0f, bnd.maximum.z - minimal.z)) * rescale.z;
+ for (uint32_t i = start; i <= end && i < SWEEP_RESOLUTION; ++i)
+ {
+ for (auto id : zSegm[i])
+ {
+ if (foundy[id] == iterId && foundx[id] == iterId)
+ {
+ foundx[id] = iterId + 1;
+ foundy[id] = iterId + 1;
+ indices.push_back(id);
+ }
+ }
+ }
+
+ iterId += 2;
+}
+
+
+void SweepingAccelerator::setState(const physx::PxVec3& point) {
+
+ indices.clear();
+
+ /*for (uint32_t i = 0; i < facetCount; ++i)
+ {
+ indices.push_back(i);
+ }*/
+
+ uint32_t xIndex = (point.x - minimal.x) * rescale.x;
+ uint32_t yIndex = (point.y- minimal.y) * rescale.y;
+
+ for (uint32_t i = 0; i < xSegm[xIndex].size(); ++i)
+ {
+ foundx[xSegm[xIndex][i]] = iterId;
+ }
+ for (uint32_t i = 0; i < ySegm[yIndex].size(); ++i)
+ {
+ if (foundx[ySegm[yIndex][i]] == iterId)
+ {
+ indices.push_back(ySegm[yIndex][i]);
+ }
+ }
+ iterId++;
+ current = 0;
+ NV_UNUSED(point);
+}
+int32_t SweepingAccelerator::getNextFacet()
+{
+ if (static_cast<uint32_t>(current) < indices.size())
+ {
+ ++current;
+ return indices[current - 1];
+ }
+ else
+ return -1;
+}
+
+
+
+
+
+
} // namespace Blast
} // namespace Nv
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.h b/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.h
index 909dc86..4251d0d 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.h
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringAccelerator.h
@@ -36,59 +36,114 @@
namespace Nv
{
-namespace Blast
-{
-
-class Mesh;
-
-
-/**
- Acceleration structure interface.
-*/
-class SpatialAccelerator
-{
-public:
- /**
- Set state of accelerator to return all facets which possibly can intersect given facet.
- \param[in] pos Vertex buffer
- \param[in] ed Edge buffer
- \param[in] fc Facet which should be tested.
- */
- virtual void setState(const Vertex* pos, const Edge* ed, const Facet& fc) = 0;
- /**
- Set state of accelerator to return all facets which possibly can cover given point. Needed for testing whether point is inside mesh.
- \param[in] point Point which should be tested.
- */
- virtual void setState(const physx::PxVec3& point) = 0;
- /**
- Recieve next facet for setted state.
- \return Next facet index, or -1 if no facets left.
- */
- virtual int32_t getNextFacet() = 0;
-
- virtual ~SpatialAccelerator() {};
-};
-
-
-/**
- Dummy accelerator iterates through all facets of mesh.
-*/
-class DummyAccelerator : public SpatialAccelerator
+ namespace Blast
+ {
+
+ class Mesh;
+
+
+ /**
+ Acceleration structure interface.
+ */
+ class SpatialAccelerator
+ {
+ public:
+ /**
+ Set state of accelerator to return all facets which possibly can intersect given facet.
+ \param[in] pos Vertex buffer
+ \param[in] ed Edge buffer
+ \param[in] fc Facet which should be tested.
+ */
+ virtual void setState(const Vertex* pos, const Edge* ed, const Facet& fc) = 0;
+ /**
+ Set state of accelerator to return all facets which possibly can cover given point. Needed for testing whether point is inside mesh.
+ \param[in] point Point which should be tested.
+ */
+ virtual void setState(const physx::PxVec3& point) = 0;
+ /**
+ Recieve next facet for setted state.
+ \return Next facet index, or -1 if no facets left.
+ */
+ virtual int32_t getNextFacet() = 0;
+
+
+ virtual ~SpatialAccelerator() {};
+ };
+
+
+ /**
+ Dummy accelerator iterates through all facets of mesh.
+ */
+ class DummyAccelerator : public SpatialAccelerator
+ {
+ public:
+ /**
+ \param[in] count Mesh facets count for which accelerator should be built.
+ */
+ DummyAccelerator(int32_t count);
+ virtual void setState(const Vertex* pos, const Edge* ed, const Facet& fc);
+ virtual void setState(const physx::PxVec3& point);
+ virtual int32_t getNextFacet();
+
+ private:
+ int32_t count;
+ int32_t current;
+ };
+
+ struct SegmentToIndex
+ {
+ float coord;
+ uint32_t index;
+ bool end;
+
+ SegmentToIndex(float c, uint32_t i, bool end) : coord(c), index(i), end(end) {};
+
+ bool operator<(const SegmentToIndex& in) const
+ {
+ if (coord < in.coord) return true;
+ if (coord > in.coord) return false;
+ return end < in.end;
+ }
+ };
+
+
+class SweepingAccelerator : public SpatialAccelerator
{
public:
/**
- \param[in] count Mesh facets count for which accelerator should be built.
+ \param[in] count Mesh facets count for which accelerator should be built.
*/
- DummyAccelerator(int32_t count);
+ SweepingAccelerator(Nv::Blast::Mesh* in);
virtual void setState(const Vertex* pos, const Edge* ed, const Facet& fc);
virtual void setState(const physx::PxVec3& point);
virtual int32_t getNextFacet();
private:
- int32_t count;
+
+
+ /*
+ For fast point test.
+ */
+ std::vector<std::vector<uint32_t> > xSegm;
+ std::vector<std::vector<uint32_t> > ySegm;
+ std::vector<std::vector<uint32_t> > zSegm;
+ std::vector<uint32_t> indices;
+ std::vector<uint32_t> foundx;
+ std::vector<uint32_t> foundy;
+
+ uint32_t iterId;
int32_t current;
+ uint32_t facetCount;
+
+ physx::PxVec3 minimal;
+ physx::PxVec3 maximal;
+
+ physx::PxVec3 rescale;
+
+
};
+
/**
Accelerator which builds map from 3d grid to initial mesh facets.
To find all facets which possibly intersect given one, it return all facets which are pointed by grid cells, which intersects with bounding box of given facet.
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.cpp
index a4a2ce7..0e2ff0d 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.cpp
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.cpp
@@ -35,6 +35,7 @@
#include <NvBlastExtAuthoringBondGeneratorImpl.h>
#include <NvBlast.h>
+#include <NvBlastGlobals.h>
#include "NvBlastExtTriangleProcessor.h"
#include "NvBlastExtApexSharedParts.h"
#include "NvBlastExtAuthoringCollisionBuilderImpl.h"
@@ -50,7 +51,7 @@
using physx::PxVec3;
using physx::PxBounds3;
-#define SAFE_ARRAY_NEW(T, x) ((x) > 0) ? new T[x] : nullptr;
+#define SAFE_ARRAY_NEW(T, x) ((x) > 0) ? reinterpret_cast<T*>(NVBLAST_ALLOC(sizeof(T) * (x))) : nullptr;
//#define DEBUG_OUTPUT
#ifdef DEBUG_OUTPUT
@@ -241,56 +242,76 @@ namespace Nv
return area * 0.5f;
}
- int32_t BlastBondGeneratorImpl::createFullBondListAveraged(uint32_t meshCount, const uint32_t* geometryOffset, const Triangle* geometry,
- const bool* supportFlags, NvBlastBondDesc*& resultBondDescs, BondGenerationConfig conf)
+ int32_t BlastBondGeneratorImpl::createFullBondListAveraged(uint32_t meshCount, const uint32_t* geometryOffset, const Triangle* geometry, const CollisionHull** chunkHulls,
+ const bool* supportFlags, const uint32_t* meshGroups, NvBlastBondDesc*& resultBondDescs, BondGenerationConfig conf)
{
NV_UNUSED(conf);
std::vector<std::vector<PxVec3> > chunksPoints(meshCount);
-
- for (uint32_t i = 0; i < meshCount; ++i)
+ if (!chunkHulls)
{
- if (!supportFlags[i])
- {
- continue;
- }
- uint32_t count = geometryOffset[i + 1] - geometryOffset[i];
- for (uint32_t j = 0; j < count; ++j)
+ for (uint32_t i = 0; i < meshCount; ++i)
{
- chunksPoints[i].push_back(geometry[geometryOffset[i] + j].a.p);
- chunksPoints[i].push_back(geometry[geometryOffset[i] + j].b.p);
- chunksPoints[i].push_back(geometry[geometryOffset[i] + j].c.p);
+ if (!supportFlags[i])
+ {
+ continue;
+ }
+ uint32_t count = geometryOffset[i + 1] - geometryOffset[i];
+ for (uint32_t j = 0; j < count; ++j)
+ {
+ chunksPoints[i].push_back(geometry[geometryOffset[i] + j].a.p);
+ chunksPoints[i].push_back(geometry[geometryOffset[i] + j].b.p);
+ chunksPoints[i].push_back(geometry[geometryOffset[i] + j].c.p);
+ }
}
}
- Nv::Blast::ConvexMeshBuilderImpl builder(mPxCooking, mPxInsertionCallback);
-
- std::vector<CollisionHull*> cHulls(meshCount);
+ std::unique_ptr<Nv::Blast::ConvexMeshBuilderImpl> builder;
+ std::vector<std::vector<std::vector<PxVec3>>> hullPoints(meshCount);
- for (uint32_t i = 0; i < meshCount; ++i)
+ for (uint32_t chunk = 0; chunk < meshCount; ++chunk)
{
- if (!supportFlags[i])
+ if (!supportFlags[chunk])
{
continue;
}
- cHulls[i] = builder.buildCollisionGeometry(chunksPoints[i].size(), chunksPoints[i].data());
- }
+ CollisionHull* tempHullPtr = nullptr;
+ uint32_t hullCountForMesh = 0;
+ const CollisionHull** beginChunkHulls = nullptr;
+ if (chunkHulls)
+ {
+ hullCountForMesh = geometryOffset[chunk + 1] - geometryOffset[chunk];
+ beginChunkHulls = chunkHulls + geometryOffset[chunk];
+ }
+ else
+ {
+ //build a convex hull and store it in the temp slot
+ if (!builder)
+ {
+ builder = std::unique_ptr<Nv::Blast::ConvexMeshBuilderImpl>(new Nv::Blast::ConvexMeshBuilderImpl(mPxCooking, mPxInsertionCallback));
+ }
- std::vector<std::vector<PxVec3> > hullPoints(cHulls.size());
+ tempHullPtr = builder->buildCollisionGeometry(chunksPoints[chunk].size(), chunksPoints[chunk].data());
+ hullCountForMesh = 1;
+ beginChunkHulls = const_cast<const CollisionHull**>(&tempHullPtr);
+ }
- for (uint32_t chunk = 0; chunk < cHulls.size(); ++chunk)
- {
- if (!supportFlags[chunk])
+ hullPoints[chunk].resize(hullCountForMesh);
+ for (uint32_t hull = 0; hull < hullCountForMesh; ++hull)
{
- continue;
+ auto& curHull = hullPoints[chunk][hull];
+ const uint32_t pointCount = beginChunkHulls[hull]->pointsCount;
+ curHull.resize(pointCount);
+ for (uint32_t i = 0; i < pointCount; ++i)
+ {
+ curHull[i] = beginChunkHulls[hull]->points[i];
+ }
}
- hullPoints[chunk].resize(cHulls[chunk]->pointsCount);
- for (uint32_t i = 0; i < cHulls[chunk]->pointsCount; ++i)
+ if (tempHullPtr)
{
- hullPoints[chunk][i] = cHulls[chunk]->points[i];
+ tempHullPtr->release();
}
- cHulls[chunk]->release();
}
TriangleProcessor trProcessor;
@@ -301,33 +322,46 @@ namespace Nv
{
continue;
}
+ const uint32_t ihullCount = hullPoints[i].size();
for (uint32_t j = i + 1; j < meshCount; ++j)
{
- if (!supportFlags[i])
+ if (!supportFlags[j])
{
continue;
}
- PxVec3 normal;
- PxVec3 centroid;
-
- float area = processWithMidplanes(&trProcessor, chunksPoints[i], chunksPoints[j], hullPoints[i], hullPoints[j], normal, centroid);
- if (area > 0)
+ if (meshGroups && meshGroups[i] == meshGroups[j])
{
- NvBlastBondDesc bDesc;
- bDesc.chunkIndices[0] = i;
- bDesc.chunkIndices[1] = j;
- bDesc.bond.area = area;
- bDesc.bond.centroid[0] = centroid.x;
- bDesc.bond.centroid[1] = centroid.y;
- bDesc.bond.centroid[2] = centroid.z;
-
- bDesc.bond.normal[0] = normal.x;
- bDesc.bond.normal[1] = normal.y;
- bDesc.bond.normal[2] = normal.z;
+ //Same group no need to find bonds
+ continue;
+ }
+ const uint32_t jhullCount = hullPoints[j].size();
+ for (uint32_t ihull = 0; ihull < ihullCount; ++ihull)
+ {
+ for (uint32_t jhull = 0; jhull < jhullCount; ++jhull)
+ {
+ PxVec3 normal;
+ PxVec3 centroid;
- mResultBondDescs.push_back(bDesc);
+ float area = processWithMidplanes(&trProcessor, chunksPoints[i].empty() ? hullPoints[i][ihull] : chunksPoints[i], chunksPoints[j].empty() ? hullPoints[j][jhull] : chunksPoints[j], hullPoints[i][ihull], hullPoints[j][jhull], normal, centroid);
+ if (area > 0)
+ {
+ NvBlastBondDesc bDesc;
+ bDesc.chunkIndices[0] = i;
+ bDesc.chunkIndices[1] = j;
+ bDesc.bond.area = area;
+ bDesc.bond.centroid[0] = centroid.x;
+ bDesc.bond.centroid[1] = centroid.y;
+ bDesc.bond.centroid[2] = centroid.z;
+
+ bDesc.bond.normal[0] = normal.x;
+ bDesc.bond.normal[1] = normal.y;
+ bDesc.bond.normal[2] = normal.z;
+
+ mResultBondDescs.push_back(bDesc);
+ }
+ }
}
}
@@ -1024,13 +1058,13 @@ namespace Nv
}
int32_t BlastBondGeneratorImpl::bondsFromPrefractured(uint32_t meshCount, const uint32_t* geometryCount, const Triangle* geometry,
- const bool*& chunkIsSupport, NvBlastBondDesc*& resultBondDescs, BondGenerationConfig conf)
+ const bool* chunkIsSupport, NvBlastBondDesc*& resultBondDescs, BondGenerationConfig conf)
{
int32_t ret_val = 0;
switch (conf.bondMode)
{
case BondGenerationConfig::AVERAGE:
- ret_val = createFullBondListAveraged(meshCount, geometryCount, geometry, chunkIsSupport, resultBondDescs, conf);
+ ret_val = createFullBondListAveraged(meshCount, geometryCount, geometry, nullptr, chunkIsSupport, nullptr, resultBondDescs, conf);
break;
case BondGenerationConfig::EXACT:
ret_val = createFullBondListExact(meshCount, geometryCount, geometry, chunkIsSupport, resultBondDescs, conf);
@@ -1039,6 +1073,14 @@ namespace Nv
return ret_val;
}
+
+ int32_t BlastBondGeneratorImpl::bondsFromPrefractured(uint32_t meshCount, const uint32_t* convexHullOffset, const CollisionHull** chunkHulls, const bool* chunkIsSupport, const uint32_t* meshGroups, NvBlastBondDesc*& resultBondDescs)
+ {
+ BondGenerationConfig conf;
+ conf.bondMode = BondGenerationConfig::AVERAGE;
+ return createFullBondListAveraged(meshCount, convexHullOffset, nullptr, chunkHulls, chunkIsSupport, meshGroups, resultBondDescs, conf);
+ }
+
void BlastBondGeneratorImpl::release()
{
delete this;
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.h b/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.h
index 35c4b23..a33468d 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.h
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.h
@@ -65,8 +65,11 @@ public:
NvBlastBondDesc*& resultBond, BondGenerationConfig cfg) override;
virtual int32_t bondsFromPrefractured(uint32_t meshCount, const uint32_t* geometryOffset, const Triangle* geometry,
- const bool*& chunkIsSupport, NvBlastBondDesc*& resultBondDescs,
+ const bool* chunkIsSupport, NvBlastBondDesc*& resultBondDescs,
BondGenerationConfig conf = BondGenerationConfig()) override;
+
+ virtual int32_t bondsFromPrefractured(uint32_t meshCount, const uint32_t* convexHullOffset, const CollisionHull** chunkHulls,
+ const bool* chunkIsSupport, const uint32_t* meshGroups, NvBlastBondDesc*& resultBondDescs) override;
private:
float processWithMidplanes( TriangleProcessor* trProcessor,
@@ -74,8 +77,8 @@ private:
const std::vector<physx::PxVec3>& hull1p, const std::vector<physx::PxVec3>& hull2p,
physx::PxVec3& normal, physx::PxVec3& centroid);
- int32_t createFullBondListAveraged( uint32_t meshCount, const uint32_t* geometryOffset, const Triangle* geometry,
- const bool* supportFlags, NvBlastBondDesc*& resultBondDescs, BondGenerationConfig conf);
+ int32_t createFullBondListAveraged( uint32_t meshCount, const uint32_t* geometryOffset, const Triangle* geometry, const CollisionHull** chunkHulls,
+ const bool* supportFlags, const uint32_t* meshGroups, NvBlastBondDesc*& resultBondDescs, BondGenerationConfig conf);
int32_t createFullBondListExact( uint32_t meshCount, const uint32_t* geometryOffset, const Triangle* geometry,
const bool* supportFlags, NvBlastBondDesc*& resultBondDescs, BondGenerationConfig conf);
int32_t createFullBondListExactInternal(uint32_t meshCount, const uint32_t* geometryOffset, const Triangle* geometry,
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringBooleanTool.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoringBooleanTool.cpp
index 359b077..e265cf1 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringBooleanTool.cpp
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringBooleanTool.cpp
@@ -106,7 +106,7 @@ bool shouldSwap(const PxVec3& a, const PxVec3& b)
/**
-Vertex-edge shadowing functions
+ Vertex-edge shadowing functions
*/
int32_t shadowing01(Vertex sEdge, Vertex eEdge, const PxVec3& p, Vertex& onEdgePoint, bool& hasOnEdge)
{
@@ -147,9 +147,15 @@ int32_t shadowing01(Vertex sEdge, Vertex eEdge, const PxVec3& p, Vertex& onEdgeP
}
return 0;
}
-int32_t shadowing10(const Vertex& sEdge, const Vertex& eEdge, const PxVec3& p, Vertex& onEdgePoint, bool& hasOnEdge)
+int32_t shadowing10(Vertex sEdge, Vertex eEdge, const PxVec3& p, Vertex& onEdgePoint, bool& hasOnEdge)
{
int32_t winding = veStatus10(sEdge.p, eEdge.p, p);
+
+ if (sEdge.p.x > eEdge.p.x)
+ {
+ std::swap(sEdge, eEdge);
+ }
+
if (winding != 0)
{
float t = (p.x - sEdge.p.x) / (eEdge.p.x - sEdge.p.x);
@@ -180,11 +186,16 @@ int32_t shadowing10(const Vertex& sEdge, const Vertex& eEdge, const PxVec3& p, V
return 0;
}
-int32_t shadowing01(const PxVec3& sEdge, const PxVec3& eEdge, const PxVec3& p)
+int32_t shadowing01(PxVec3 sEdge, PxVec3 eEdge, const PxVec3& p)
{
int32_t winding = veStatus01(sEdge, eEdge, p);
+
if (winding != 0)
{
+ if (sEdge.x > eEdge.x)
+ {
+ std::swap(sEdge, eEdge);
+ }
float t = ((p.x - sEdge.x) / (eEdge.x - sEdge.x));
PxVec3 onEdgePoint;
if (t >= 1)
@@ -201,11 +212,16 @@ int32_t shadowing01(const PxVec3& sEdge, const PxVec3& eEdge, const PxVec3& p)
return 0;
}
-int32_t shadowing10(const PxVec3& sEdge, const PxVec3& eEdge, const PxVec3& p)
+int32_t shadowing10(PxVec3 sEdge, PxVec3 eEdge, const PxVec3& p)
{
int32_t winding = veStatus10(sEdge, eEdge, p);
if (winding != 0)
{
+ if (sEdge.x > eEdge.x)
+ {
+ std::swap(sEdge, eEdge);
+ }
+
float t = ((p.x - sEdge.x) / (eEdge.x - sEdge.x));
PxVec3 onEdgePoint;
if (t >= 1)
@@ -242,7 +258,7 @@ int32_t vfStatus02(const PxVec3& p, const Vertex* points, const Edge* edges, int
{
out[0] = pnt;
}
- if (p.y < pnt.p.y && pnt.p.y < out[1].p.y)
+ if (p.y <= pnt.p.y && pnt.p.y < out[1].p.y)
{
out[1] = pnt;
}
@@ -301,7 +317,7 @@ int32_t vfStatus20(const PxVec3& p, const Vertex* points, const Edge* edges, int
{
out[0] = pnt;
}
- if (p.y < pnt.p.y && pnt.p.y < out[1].p.y)
+ if (p.y <= pnt.p.y && pnt.p.y < out[1].p.y)
{
out[1] = pnt;
}
@@ -567,7 +583,14 @@ int32_t edgeFacetIntersection21(const Vertex& edSt, const Vertex& edEnd, const V
for (int32_t ed = 0; ed < edgesCount; ++ed)
{
- shadowingType = edgeEdgeShadowing(points[edges[ed].s], points[edges[ed].e], edSt, edEnd, p1, p2, hasPoint);
+ if (shouldSwap(points[edges[ed].s].p, points[edges[ed].e].p))
+ {
+ shadowingType = -edgeEdgeShadowing(points[edges[ed].e], points[edges[ed].s], edSt, edEnd, p1, p2, hasPoint);
+ }
+ else
+ {
+ shadowingType = edgeEdgeShadowing(points[edges[ed].s], points[edges[ed].e], edSt, edEnd, p1, p2, hasPoint);
+ }
status -= shadowingType;
if (shadowingType == 0)
{
@@ -762,6 +785,7 @@ void BooleanEvaluator::buildFaceFaceIntersections(BooleanConf mode)
{
statusValue = edgeFacetIntersection12(meshAPoints[fae->s], meshAPoints[fae->e], mMeshB->getVertices(), facetBEdges, facetBEdgeCount, newPointA, newPointB);
}
+
inclusionValue = -inclusionValueEdgeFace(mode, statusValue);
if (inclusionValue > 0)
{
@@ -1301,7 +1325,7 @@ Mesh* BooleanEvaluator::createNewMesh()
int32_t lastPos = 0;
int32_t lastParent = mEdgeAggregate[0].parent;
uint32_t collected = 0;
- int32_t userData = 0;
+ int64_t userData = 0;
int32_t materialId = 0;
int32_t smoothingGroup = 0;
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilderImpl.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilderImpl.cpp
index ddfa668..43f1dbf 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilderImpl.cpp
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilderImpl.cpp
@@ -25,7 +25,7 @@
//
// Copyright (c) 2016-2017 NVIDIA Corporation. All rights reserved.
-
+#include <NvBlastGlobals.h>
#include "NvBlastExtAuthoringCollisionBuilderImpl.h"
#include <PxConvexMesh.h>
#include <PxVec3.h>
@@ -38,21 +38,41 @@
#include <NvBlastExtAuthoringBooleanTool.h>
#include <NvBlastExtAuthoringMeshImpl.h>
+#include <VHACD.h>
+
using namespace physx;
-#define SAFE_ARRAY_NEW(T, x) ((x) > 0) ? new T[x] : nullptr;
-#define SAFE_ARRAY_DELETE(x) if (x != nullptr) {delete[] x; x = nullptr;}
+#define SAFE_ARRAY_NEW(T, x) ((x) > 0) ? reinterpret_cast<T*>(NVBLAST_ALLOC(sizeof(T) * (x))) : nullptr;
+#define SAFE_ARRAY_DELETE(x) if (x != nullptr) {NVBLAST_FREE(x); x = nullptr;}
namespace Nv
{
namespace Blast
{
-void CollisionHullImpl::release()
+CollisionHullImpl::~CollisionHullImpl()
{
SAFE_ARRAY_DELETE(points);
SAFE_ARRAY_DELETE(indices);
SAFE_ARRAY_DELETE(polygonData);
+}
+
+CollisionHullImpl::CollisionHullImpl(const CollisionHull& hullToCopy)
+{
+ pointsCount = hullToCopy.pointsCount;
+ indicesCount = hullToCopy.indicesCount;
+ polygonDataCount = hullToCopy.polygonDataCount;
+
+ points = SAFE_ARRAY_NEW(physx::PxVec3, pointsCount);
+ indices = SAFE_ARRAY_NEW(uint32_t, indicesCount);
+ polygonData = SAFE_ARRAY_NEW(CollisionHull::HullPolygon, polygonDataCount);
+ memcpy(points, hullToCopy.points, sizeof(points[0]) * pointsCount);
+ memcpy(indices, hullToCopy.indices, sizeof(indices[0]) * indicesCount);
+ memcpy(polygonData, hullToCopy.polygonData, sizeof(polygonData[0]) * polygonDataCount);
+}
+
+void CollisionHullImpl::release()
+{
delete this;
}
@@ -313,5 +333,75 @@ void ConvexMeshBuilderImpl::release()
delete this;
}
+int32_t ConvexMeshBuilderImpl::buildMeshConvexDecomposition(const Triangle* mesh, uint32_t triangleCount, const CollisionParams& iparams, CollisionHull**& convexes)
+{
+ std::vector<float> coords(triangleCount * 9);
+ std::vector<uint32_t> indices(triangleCount * 3);
+
+ uint32_t indx = 0;
+ uint32_t indxCoord = 0;
+
+ PxBounds3 chunkBound = PxBounds3::empty();
+ for (uint32_t i = 0; i < triangleCount; ++i)
+ {
+ for (auto& t : { mesh[i].a.p , mesh[i].b.p , mesh[i].c.p })
+ {
+
+ chunkBound.include(t);
+ coords[indxCoord] = t.x;
+ coords[indxCoord + 1] = t.y;
+ coords[indxCoord + 2] = t.z;
+ indxCoord += 3;
+ }
+ indices[indx] = indx;
+ indices[indx + 1] = indx + 1;
+ indices[indx + 2] = indx + 2;
+ indx += 3;
+ }
+
+ PxVec3 rsc = chunkBound.getDimensions();
+
+ for (uint32_t i = 0; i < coords.size(); i += 3)
+ {
+ coords[i] = (coords[i] - chunkBound.minimum.x) / rsc.x;
+ coords[i + 1] = (coords[i + 1] - chunkBound.minimum.y) / rsc.y;
+ coords[i + 2] = (coords[i + 2] - chunkBound.minimum.z) / rsc.z;
+ }
+
+ VHACD::IVHACD* decomposer = VHACD::CreateVHACD();
+
+ VHACD::IVHACD::Parameters vhacdParam;
+ vhacdParam.m_maxConvexHulls = iparams.maximumNumberOfHulls;
+ vhacdParam.m_resolution = iparams.voxelGridResolution;
+
+ decomposer->Compute(coords.data(), triangleCount * 3, indices.data(), triangleCount, vhacdParam);
+
+ const uint32_t nConvexHulls = decomposer->GetNConvexHulls();
+ convexes = SAFE_ARRAY_NEW(CollisionHull*, nConvexHulls);
+
+ for (uint32_t i = 0; i < nConvexHulls; ++i)
+ {
+ VHACD::IVHACD::ConvexHull hl;
+ decomposer->GetConvexHull(i, hl);
+ std::vector<PxVec3> vertices;
+ for (uint32_t v = 0; v < hl.m_nPoints; ++v)
+ {
+ vertices.push_back(PxVec3(hl.m_points[v * 3], hl.m_points[v * 3 + 1], hl.m_points[v * 3 + 2]));
+ vertices.back().x = vertices.back().x * rsc.x + chunkBound.minimum.x;
+ vertices.back().y = vertices.back().y * rsc.y + chunkBound.minimum.y;
+ vertices.back().z = vertices.back().z * rsc.z + chunkBound.minimum.z;
+
+ }
+ convexes[i] = buildCollisionGeometry(vertices.size(), vertices.data());
+ }
+ //VHACD::~VHACD called from release does nothign and does not call Clean()
+ decomposer->Clean();
+ decomposer->Release();
+
+ return nConvexHulls;
+}
+
+
+
} // namespace Blast
} // namespace Nv
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilderImpl.h b/sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilderImpl.h
index 709c880..38a3dfd 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilderImpl.h
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringCollisionBuilderImpl.h
@@ -39,6 +39,19 @@ namespace Blast
struct CollisionHullImpl : public CollisionHull
{
+ ~CollisionHullImpl();
+ CollisionHullImpl()
+ {
+ pointsCount = 0;
+ indicesCount = 0;
+ polygonDataCount = 0;
+ points = nullptr;
+ indices = nullptr;
+ polygonData = nullptr;
+ }
+
+ CollisionHullImpl(const CollisionHull& hullToCopy);
+
void release() override;
};
@@ -61,6 +74,8 @@ public:
virtual void trimCollisionGeometry(uint32_t chunksCount, CollisionHull** in, const uint32_t* chunkDepth) override;
+ virtual int32_t buildMeshConvexDecomposition(const Triangle* mesh, uint32_t triangleCount, const CollisionParams& params, CollisionHull**& convexes) override;
+
private:
physx::PxPhysicsInsertionCallback* mInsertionCallback;
physx::PxCooking* mCooking;
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.cpp
index 666361c..920069d 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.cpp
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.cpp
@@ -30,6 +30,7 @@
using namespace physx;
#define DEFAULT_BB_ACCELARATOR_RES 10
+#define SLICING_INDEXER_OFFSET (1ll << 32)
namespace Nv
{
@@ -279,17 +280,19 @@ void VoronoiSitesGeneratorImpl::generateInSphere(const uint32_t count, const flo
uint32_t attemptNumber = 0;
uint32_t generatedSites = 0;
std::vector<PxVec3> tempPoints;
+ float radiusSquared = radius * radius;
while (generatedSites < count && attemptNumber < MAX_VORONOI_ATTEMPT_NUMBER)
{
- float rn1 = mRnd->getRandomValue() * radius;
- float rn2 = mRnd->getRandomValue() * radius;
- float rn3 = mRnd->getRandomValue() * radius;
- if (voronoiMeshEval.isPointContainedInMesh(mMesh, PxVec3(rn1, rn2, rn3) + center) && (mStencil == nullptr
- || voronoiMeshEval.isPointContainedInMesh(mStencil, PxVec3(rn1, rn2, rn3) + center)))
+ float rn1 = (mRnd->getRandomValue() - 0.5f) * 2.f * radius;
+ float rn2 = (mRnd->getRandomValue() - 0.5f) * 2.f * radius;
+ float rn3 = (mRnd->getRandomValue() - 0.5f) * 2.f * radius;
+ PxVec3 point(rn1, rn2, rn3);
+ if (point.magnitudeSquared() < radiusSquared && voronoiMeshEval.isPointContainedInMesh(mMesh, point + center) && (mStencil == nullptr
+ || voronoiMeshEval.isPointContainedInMesh(mStencil, point + center)))
{
generatedSites++;
- mGeneratedSites.push_back(PxVec3(rn1, rn2, rn3) + center);
+ mGeneratedSites.push_back(point + center);
attemptNumber = 0;
}
else
@@ -376,7 +379,7 @@ int32_t FractureToolImpl::voronoiFracturing(uint32_t chunkId, uint32_t cellCount
}
if (!mChunkData[chunkIndex].isLeaf)
{
- deleteAllChildsOfChunk(chunkId);
+ deleteAllChildrenOfChunk(chunkId);
}
chunkIndex = getChunkIndex(chunkId);
@@ -434,7 +437,6 @@ int32_t FractureToolImpl::voronoiFracturing(uint32_t chunkId, uint32_t cellCount
}
mPlaneIndexerOffset += static_cast<int32_t>(cellPoints.size() * cellPoints.size());
-
if (mRemoveIslands)
{
for (auto chunkToCheck : newlyCreatedChunksIds)
@@ -442,6 +444,7 @@ int32_t FractureToolImpl::voronoiFracturing(uint32_t chunkId, uint32_t cellCount
islandDetectionAndRemoving(chunkToCheck);
}
}
+
return 0;
}
@@ -538,7 +541,7 @@ int32_t FractureToolImpl::voronoiFracturing(uint32_t chunkId, uint32_t cellCount
}
if (!mChunkData[chunkIndex].isLeaf)
{
- deleteAllChildsOfChunk(chunkId);
+ deleteAllChildrenOfChunk(chunkId);
}
chunkIndex = getChunkIndex(chunkId);
@@ -624,7 +627,6 @@ int32_t FractureToolImpl::voronoiFracturing(uint32_t chunkId, uint32_t cellCount
return 0;
}
-
int32_t FractureToolImpl::slicing(uint32_t chunkId, SlicingConfiguration conf, bool replaceChunk, RandomGeneratorBase* rnd)
{
if (conf.noiseAmplitude != 0)
@@ -644,7 +646,7 @@ int32_t FractureToolImpl::slicing(uint32_t chunkId, SlicingConfiguration conf, b
}
if (!mChunkData[chunkIndex].isLeaf)
{
- deleteAllChildsOfChunk(chunkId);
+ deleteAllChildrenOfChunk(chunkId);
}
chunkIndex = getChunkIndex(chunkId);
@@ -686,7 +688,7 @@ int32_t FractureToolImpl::slicing(uint32_t chunkId, SlicingConfiguration conf, b
PxVec3 randVect = PxVec3(2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1);
PxVec3 lDir = dir + randVect * conf.angle_variations;
- setCuttingBox(center, -lDir, slBox, 20, mPlaneIndexerOffset);
+ setCuttingBox(center, -lDir, slBox, 20, mPlaneIndexerOffset + SLICING_INDEXER_OFFSET);
bTool.performFastCutting(mesh, slBox, BooleanConfigurations::BOOLEAN_INTERSECION());
ch.meshData = bTool.createNewMesh();
@@ -694,7 +696,7 @@ int32_t FractureToolImpl::slicing(uint32_t chunkId, SlicingConfiguration conf, b
{
xSlicedChunks.push_back(ch);
}
- inverseNormalAndSetIndices(slBox, -mPlaneIndexerOffset);
+ inverseNormalAndSetIndices(slBox, -(mPlaneIndexerOffset + SLICING_INDEXER_OFFSET));
++mPlaneIndexerOffset;
bTool.performFastCutting(mesh, slBox, BooleanConfigurations::BOOLEAN_DIFFERENCE());
Mesh* result = bTool.createNewMesh();
@@ -726,14 +728,14 @@ int32_t FractureToolImpl::slicing(uint32_t chunkId, SlicingConfiguration conf, b
PxVec3 lDir = dir + randVect * conf.angle_variations;
- setCuttingBox(center, -lDir, slBox, 20, mPlaneIndexerOffset);
+ setCuttingBox(center, -lDir, slBox, 20, mPlaneIndexerOffset + SLICING_INDEXER_OFFSET);
bTool.performFastCutting(mesh, slBox, BooleanConfigurations::BOOLEAN_INTERSECION());
ch.meshData = bTool.createNewMesh();
if (ch.meshData != 0)
{
ySlicedChunks.push_back(ch);
}
- inverseNormalAndSetIndices(slBox, -mPlaneIndexerOffset);
+ inverseNormalAndSetIndices(slBox, -(mPlaneIndexerOffset + SLICING_INDEXER_OFFSET));
++mPlaneIndexerOffset;
bTool.performFastCutting(mesh, slBox, BooleanConfigurations::BOOLEAN_DIFFERENCE());
Mesh* result = bTool.createNewMesh();
@@ -764,7 +766,7 @@ int32_t FractureToolImpl::slicing(uint32_t chunkId, SlicingConfiguration conf, b
{
PxVec3 randVect = PxVec3(2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1);
PxVec3 lDir = dir + randVect * conf.angle_variations;
- setCuttingBox(center, -lDir, slBox, 20, mPlaneIndexerOffset);
+ setCuttingBox(center, -lDir, slBox, 20, mPlaneIndexerOffset + SLICING_INDEXER_OFFSET);
bTool.performFastCutting(mesh, slBox, BooleanConfigurations::BOOLEAN_INTERSECION());
ch.meshData = bTool.createNewMesh();
if (ch.meshData != 0)
@@ -773,7 +775,7 @@ int32_t FractureToolImpl::slicing(uint32_t chunkId, SlicingConfiguration conf, b
newlyCreatedChunksIds.push_back(ch.chunkId);
mChunkData.push_back(ch);
}
- inverseNormalAndSetIndices(slBox, -mPlaneIndexerOffset);
+ inverseNormalAndSetIndices(slBox, -(mPlaneIndexerOffset + SLICING_INDEXER_OFFSET));
++mPlaneIndexerOffset;
bTool.performFastCutting(mesh, slBox, BooleanConfigurations::BOOLEAN_DIFFERENCE());
Mesh* result = bTool.createNewMesh();
@@ -828,7 +830,7 @@ int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration co
}
if (!mChunkData[chunkIndex].isLeaf)
{
- deleteAllChildsOfChunk(chunkId);
+ deleteAllChildrenOfChunk(chunkId);
}
chunkIndex = getChunkIndex(chunkId);
@@ -862,8 +864,8 @@ int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration co
std::vector<ChunkInfo> xSlicedChunks;
std::vector<ChunkInfo> ySlicedChunks;
std::vector<uint32_t> newlyCreatedChunksIds;
- float noisyPartSize = 1.8f;
- int32_t acceleratorRes = 8;
+ float noisyPartSize = 1.2f;
+// int32_t acceleratorRes = 8;
/**
Slice along x direction
*/
@@ -873,8 +875,8 @@ int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration co
PxVec3 lDir = dir + randVect * conf.angle_variations;
slBox = getNoisyCuttingBoxPair(center, lDir, 40, noisyPartSize, conf.surfaceResolution, mPlaneIndexerOffset, conf.noiseAmplitude, conf.noiseFrequency, conf.noiseOctaveNumber, rnd->getRandomValue(), mInteriorMaterialId);
// DummyAccelerator accel(mesh->getFacetCount());
- IntersectionTestingAccelerator accel(mesh, acceleratorRes);
- DummyAccelerator dummy(slBox->getFacetCount());
+ SweepingAccelerator accel(mesh);
+ SweepingAccelerator dummy(slBox);
bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_DIFFERENCE());
ch.meshData = bTool.createNewMesh();
if (ch.meshData != 0)
@@ -915,8 +917,8 @@ int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration co
slBox = getNoisyCuttingBoxPair(center, lDir, 40, noisyPartSize, conf.surfaceResolution, mPlaneIndexerOffset, conf.noiseAmplitude, conf.noiseFrequency, conf.noiseOctaveNumber, rnd->getRandomValue(), mInteriorMaterialId);
// DummyAccelerator accel(mesh->getFacetCount());
- IntersectionTestingAccelerator accel(mesh, acceleratorRes);
- DummyAccelerator dummy(slBox->getFacetCount());
+ SweepingAccelerator accel(mesh);
+ SweepingAccelerator dummy(slBox);
bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_DIFFERENCE());
ch.meshData = bTool.createNewMesh();
if (ch.meshData != 0)
@@ -956,8 +958,8 @@ int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration co
PxVec3 lDir = dir + randVect * conf.angle_variations;
slBox = getNoisyCuttingBoxPair(center, lDir, 40, noisyPartSize, conf.surfaceResolution, mPlaneIndexerOffset, conf.noiseAmplitude, conf.noiseFrequency, conf.noiseOctaveNumber, rnd->getRandomValue(), mInteriorMaterialId);
// DummyAccelerator accel(mesh->getFacetCount());
- IntersectionTestingAccelerator accel(mesh, acceleratorRes);
- DummyAccelerator dummy(slBox->getFacetCount());
+ SweepingAccelerator accel(mesh);
+ SweepingAccelerator dummy(slBox);
bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_DIFFERENCE());
ch.meshData = bTool.createNewMesh();
if (ch.meshData != 0)
@@ -1159,7 +1161,7 @@ bool FractureToolImpl::isAncestorForChunk(int32_t ancestorId, int32_t chunkId)
void FractureToolImpl::eraseChunk(int32_t chunkId)
{
- deleteAllChildsOfChunk(chunkId);
+ deleteAllChildrenOfChunk(chunkId);
int32_t index = getChunkIndex(chunkId);
if (index != -1)
{
@@ -1170,7 +1172,7 @@ void FractureToolImpl::eraseChunk(int32_t chunkId)
}
-void FractureToolImpl::deleteAllChildsOfChunk(int32_t chunkId)
+bool FractureToolImpl::deleteAllChildrenOfChunk(int32_t chunkId)
{
std::vector<int32_t> chunkToDelete;
for (uint32_t i = 0; i < mChunkData.size(); ++i)
@@ -1187,6 +1189,7 @@ void FractureToolImpl::deleteAllChildsOfChunk(int32_t chunkId)
std::swap(mChunkData.back(), mChunkData[m]);
mChunkData.pop_back();
}
+ return chunkToDelete.size() > 0;
}
void FractureToolImpl::finalizeFracturing()
@@ -1562,5 +1565,262 @@ void FractureToolImpl::replaceMaterialId(int32_t oldMaterialId, int32_t newMater
}
}
+uint32_t FractureToolImpl::stretchGroup(const std::vector<uint32_t>& grp, std::vector<std::vector<uint32_t>>& graph)
+{
+ uint32_t parent = mChunkData[grp[0]].parent;
+ uint32_t newChunkIndex = createNewChunk(parent);
+ graph.push_back(std::vector<uint32_t>());
+
+
+
+ std::vector<Vertex> nVertices;
+ std::vector<Edge> nEdges;
+ std::vector<Facet> nFacets;
+
+ uint32_t offsetVertices = 0;
+ uint32_t offsetEdges = 0;
+
+ for (uint32_t i = 0; i < grp.size(); ++i)
+ {
+ mChunkData[grp[i]].parent = mChunkData[newChunkIndex].chunkId;
+
+ auto vr = mChunkData[grp[i]].meshData->getVertices();
+ auto ed = mChunkData[grp[i]].meshData->getEdges();
+ auto fc = mChunkData[grp[i]].meshData->getFacetsBuffer();
+
+
+ for (uint32_t v = 0; v < mChunkData[grp[i]].meshData->getVerticesCount(); ++v)
+ {
+ nVertices.push_back(vr[v]);
+ }
+ for (uint32_t v = 0; v < mChunkData[grp[i]].meshData->getEdgesCount(); ++v)
+ {
+ nEdges.push_back(ed[v]);
+ nEdges.back().s += offsetVertices;
+ nEdges.back().e += offsetVertices;
+ }
+ for (uint32_t v = 0; v < mChunkData[grp[i]].meshData->getFacetCount(); ++v)
+ {
+ nFacets.push_back(fc[v]);
+ nFacets.back().firstEdgeNumber += offsetEdges;
+ }
+ offsetEdges = nEdges.size();
+ offsetVertices = nVertices.size();
+ }
+ std::vector<Facet> finalFacets;
+ std::set<int64_t> hasCutting;
+ for (uint32_t i = 0; i < nFacets.size(); ++i)
+ {
+ if (nFacets[i].userData != 0)
+ hasCutting.insert(nFacets[i].userData);
+ }
+ for (uint32_t i = 0; i < nFacets.size(); ++i)
+ {
+ if (nFacets[i].userData == 0 || (hasCutting.find(-nFacets[i].userData) == hasCutting.end()) || std::abs(nFacets[i].userData) >= SLICING_INDEXER_OFFSET)
+ {
+ finalFacets.push_back(nFacets[i]);
+ }
+ }
+ mChunkData[newChunkIndex].meshData = new MeshImpl(nVertices.data(), nEdges.data(), finalFacets.data(), static_cast<uint32_t>(nVertices.size()), static_cast<uint32_t>(nEdges.size()), static_cast<uint32_t>(finalFacets.size()));
+ return newChunkIndex;
+}
+
+uint32_t FractureToolImpl::createNewChunk(uint32_t parent)
+{
+ mChunkData.push_back(ChunkInfo());
+ mChunkData.back().parent = parent;
+ mChunkData.back().chunkId = mChunkIdCounter++;
+ mChunkData.back().meshData = nullptr;
+ mChunkData.back().isLeaf = false;
+ return mChunkData.size() - 1;
+}
+
+void FractureToolImpl::rebuildAdjGraph(const std::vector<uint32_t>& chunks, std::vector<std::vector<uint32_t> >& chunkGraph)
+{
+ std::vector<std::pair<uint64_t, uint32_t>> planeChunkIndex;
+
+ for (uint32_t i = 0; i < chunks.size(); ++i)
+ {
+ for (uint32_t fc = 0; fc < mChunkData[chunks[i]].meshData->getFacetCount(); ++fc)
+ {
+ if (mChunkData[chunks[i]].meshData->getFacet(fc)->userData != 0)
+ {
+ planeChunkIndex.push_back(std::make_pair(std::abs(mChunkData[chunks[i]].meshData->getFacet(fc)->userData), chunks[i]));
+ }
+ }
+ }
+ {
+ std::sort(planeChunkIndex.begin(), planeChunkIndex.end());
+ auto it = std::unique(planeChunkIndex.begin(), planeChunkIndex.end());
+ planeChunkIndex.resize(it - planeChunkIndex.begin());
+ }
+
+ uint32_t a = 0;
+
+ for (uint32_t i = 1; i < planeChunkIndex.size(); ++i)
+ {
+ if (planeChunkIndex[a].first != planeChunkIndex[i].first)
+ {
+ uint32_t b = i;
+
+ for (uint32_t p1 = a; p1 < b; ++p1)
+ {
+ for (uint32_t p2 = p1 + 1; p2 < b; ++p2)
+ {
+ if (planeChunkIndex[p1].second == planeChunkIndex[p2].second || mChunkData[planeChunkIndex[p1].second].parent != mChunkData[planeChunkIndex[p2].second].parent)
+ {
+ continue;
+ }
+ bool has = false;
+ for (uint32_t k = 0; k < chunkGraph[planeChunkIndex[p1].second].size(); ++k)
+ {
+ if (chunkGraph[planeChunkIndex[p1].second][k] == planeChunkIndex[p2].second)
+ {
+ has = true;
+ break;
+ }
+ }
+ if (!has)
+ {
+ chunkGraph[planeChunkIndex[p1].second].push_back(planeChunkIndex[p2].second);
+ }
+ has = false;
+ for (uint32_t k = 0; k < chunkGraph[planeChunkIndex[p2].second].size(); ++k)
+ {
+ if (chunkGraph[planeChunkIndex[p2].second][k] == planeChunkIndex[p1].second)
+ {
+ has = true;
+ break;
+ }
+ }
+ if (!has)
+ {
+ chunkGraph[planeChunkIndex[p2].second].push_back(planeChunkIndex[p1].second);
+ }
+ }
+ }
+ a = b;
+ }
+ }
+}
+
+bool VecIntComp(const std::pair<PxVec3, uint32_t>& a, const std::pair<PxVec3, uint32_t>& b)
+{
+ if (a.first.x < b.first.x) return true;
+ if (a.first.x > b.first.x) return false;
+ if (a.first.y < b.first.y) return true;
+ if (a.first.y > b.first.y) return false;
+ if (a.first.z < b.first.z) return true;
+ if (a.first.z > b.first.z) return false;
+
+ return a.second < b.second;
+}
+
+#define MAXIMUM_DEPTH_TO_REARRANGE 255
+
+void FractureToolImpl::uniteChunks(uint32_t maxChunksAtLevel, uint32_t maxGroup)
+{
+ maxChunksAtLevel = std::max(maxChunksAtLevel, maxGroup);
+
+ std::vector<int32_t> depth(mChunkData.size(), 0);
+
+ std::vector<std::vector<uint32_t>> chunkGraph(mChunkData.size());
+
+
+ std::vector<uint32_t> atEachDepth(MAXIMUM_DEPTH_TO_REARRANGE, 0); // Probably we will never have 255 depth levels...
+ std::vector<uint32_t> childNumber(mChunkData.size(), 0);
+
+
+ for (uint32_t i = 0; i < mChunkData.size(); ++i)
+ {
+ if (mChunkData[i].parent != -1)
+ childNumber[getChunkIndex(mChunkData[i].parent)]++;
+ depth[i] = getChunkDepth(mChunkData[i].chunkId);
+ NVBLAST_ASSERT(depth[i] >= 0);
+ if (depth[i] >= 0)
+ {
+ atEachDepth[depth[i]]++;
+ }
+ }
+
+ std::vector<uint32_t> chunkUsage(mChunkData.size(), 0);
+ uint32_t chunkUsageFlag = 1;
+
+ for (int32_t level = MAXIMUM_DEPTH_TO_REARRANGE - 1; level >= 1; --level) // go from leaves to trunk and rebuild hierarchy
+ {
+ if (atEachDepth[level] < maxChunksAtLevel) continue;
+
+ std::vector<uint32_t> cGroup;
+ std::vector<uint32_t> chunksToUnify;
+
+ PxVec3 minPoint(MAXIMUM_EXTENT, MAXIMUM_EXTENT, MAXIMUM_EXTENT);
+ VrtPositionComparator posc;
+
+ for (uint32_t ch = 0; ch < depth.size(); ++ch)
+ {
+ if (depth[ch] == level && childNumber[getChunkIndex(mChunkData[ch].parent)] > maxChunksAtLevel)
+ {
+ chunksToUnify.push_back(ch);
+ PxVec3 cp = mChunkData[ch].meshData->getBoundingBox().getCenter();
+ if (posc(cp, minPoint))
+ {
+ minPoint = cp;
+ }
+ }
+ }
+
+ std::vector<std::pair<float, uint32_t> > distances;
+ for (uint32_t i = 0; i < chunksToUnify.size(); ++i)
+ {
+ float d = (minPoint - mChunkData[chunksToUnify[i]].meshData->getBoundingBox().getCenter()).magnitude();
+ distances.push_back(std::make_pair(d, chunksToUnify[i]));
+ }
+ std::sort(distances.begin(), distances.end());
+
+ for (uint32_t i = 0; i < chunksToUnify.size(); ++i)
+ {
+ chunksToUnify[i] = distances[i].second;
+ }
+ rebuildAdjGraph(chunksToUnify, chunkGraph);
+
+
+ for (uint32_t iter = 0; iter < 32 && chunksToUnify.size() > maxChunksAtLevel; ++iter)
+ {
+ std::vector<uint32_t> newChunksToUnify;
+
+ for (uint32_t c = 0; c < chunksToUnify.size(); ++c)
+ {
+ if (chunkUsage[chunksToUnify[c]] == chunkUsageFlag) continue;
+
+ chunkUsage[chunksToUnify[c]] = chunkUsageFlag;
+ cGroup.push_back(chunksToUnify[c]);
+ for (uint32_t sc = 0; sc < cGroup.size() && cGroup.size() < maxGroup; ++sc)
+ {
+ uint32_t sid = cGroup[sc];
+ for (uint32_t neighb = 0; neighb < chunkGraph[sid].size() && cGroup.size() < maxGroup; ++neighb)
+ {
+ if (chunkUsage[chunkGraph[sid][neighb]] == chunkUsageFlag) continue;
+ cGroup.push_back(chunkGraph[sid][neighb]);
+ chunkUsage[chunkGraph[sid][neighb]] = chunkUsageFlag;
+ }
+ }
+ if (cGroup.size() > 1)
+ {
+ uint32_t newChunk = stretchGroup(cGroup, chunkGraph);
+ cGroup.clear();
+ newChunksToUnify.push_back(newChunk);
+ chunkUsage.push_back(0);
+ }
+ else
+ {
+ cGroup.clear();
+ }
+ }
+ chunksToUnify = newChunksToUnify;
+ rebuildAdjGraph(chunksToUnify, chunkGraph);
+ }
+ }
+}
+
} // namespace Blast
} // namespace Nv
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.h b/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.h
index d3eed09..6411ec3 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.h
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.h
@@ -314,11 +314,23 @@ public:
*/
bool isMeshContainOpenEdges(const Mesh* input) override;
+ bool deleteAllChildrenOfChunk(int32_t chunkId) override;
+
+ void uniteChunks(uint32_t maxAtLevel, uint32_t maxGroupSize) override;
+
+
private:
void eraseChunk(int32_t chunkId);
bool isAncestorForChunk(int32_t ancestorId, int32_t chunkId);
- void deleteAllChildsOfChunk(int32_t chunkId);
int32_t slicingNoisy(uint32_t chunkId, SlicingConfiguration conf, bool replaceChunk, RandomGeneratorBase* rnd);
+ uint32_t stretchGroup(const std::vector<uint32_t>& group, std::vector<std::vector<uint32_t>>& graph);
+ void rebuildAdjGraph(const std::vector<uint32_t>& chunksToRebuild, std::vector<std::vector<uint32_t> >& chunkGraph);
+
+ /**
+ Returns newly created chunk index in mChunkData.
+ */
+ uint32_t createNewChunk(uint32_t parentId);
+
protected:
/**
@@ -332,7 +344,7 @@ protected:
- int32_t mPlaneIndexerOffset;
+ int64_t mPlaneIndexerOffset;
int32_t mChunkIdCounter;
std::vector<ChunkInfo> mChunkData;
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.cpp
index 7330598..423a2ed 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.cpp
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.cpp
@@ -226,7 +226,7 @@ void getTangents(PxVec3& normal, PxVec3& t1, PxVec3& t2)
t2 = t1.cross(normal);
}
-Mesh* getCuttingBox(const PxVec3& point, const PxVec3& normal, float size, int32_t id, int32_t interiorMaterialId)
+Mesh* getCuttingBox(const PxVec3& point, const PxVec3& normal, float size, int64_t id, int32_t interiorMaterialId)
{
PxVec3 lNormal = normal.getNormalized();
PxVec3 t1, t2;
@@ -315,7 +315,7 @@ Mesh* getCuttingBox(const PxVec3& point, const PxVec3& normal, float size, int32
return new MeshImpl(positions.data(), edges.data(), facets.data(), static_cast<uint32_t>(positions.size()), static_cast<uint32_t>(edges.size()), static_cast<uint32_t>(facets.size()));
}
-void inverseNormalAndSetIndices(Mesh* mesh, int32_t id)
+void inverseNormalAndSetIndices(Mesh* mesh, int64_t id)
{
for (uint32_t i = 0; i < mesh->getVerticesCount(); ++i)
{
@@ -364,7 +364,7 @@ void MeshImpl::setSmoothingGroup(const int32_t* smoothingGroups)
}
-void setCuttingBox(const PxVec3& point, const PxVec3& normal, Mesh* mesh, float size, int32_t id)
+void setCuttingBox(const PxVec3& point, const PxVec3& normal, Mesh* mesh, float size, int64_t id)
{
PxVec3 t1, t2;
PxVec3 lNormal = normal.getNormalized();
@@ -453,9 +453,15 @@ Mesh* getNoisyCuttingBoxPair(const physx::PxVec3& point, const physx::PxVec3& no
uint32_t start = edges.size();
edges.push_back(Edge(i * (resolution + 1) + j, i * (resolution + 1) + j + 1));
edges.push_back(Edge(i * (resolution + 1) + j + 1, (i + 1) * (resolution + 1) + j + 1));
+ edges.push_back(Edge((i + 1) * (resolution + 1) + j + 1, i * (resolution + 1) + j));
+ facets.push_back(Facet(start, 3, interiorMaterialId, id, -1));
+
+ start = edges.size();
+ edges.push_back(Edge(i * (resolution + 1) + j, (i + 1) * (resolution + 1) + j + 1));
edges.push_back(Edge((i + 1) * (resolution + 1) + j + 1, (i + 1) * (resolution + 1) + j));
- edges.push_back(Edge((i + 1) * (resolution + 1) + j, i * (resolution + 1) + j));
- facets.push_back(Facet(start, 4, interiorMaterialId, id, -1));
+ edges.push_back(Edge((i + 1) * (resolution + 1) + j, (i) * (resolution + 1) + j));
+ facets.push_back(Facet(start, 3, interiorMaterialId, id, -1));
+
}
}
uint32_t offset = (resolution + 1) * (resolution + 1);
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.h b/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.h
index 5b7b245..512c3c8 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.h
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.h
@@ -170,7 +170,7 @@ private:
\param[in] size Cutting box size
\param[in] id Cutting box ID
*/
-void setCuttingBox(const physx::PxVec3& point, const physx::PxVec3& normal, Mesh* mesh, float size, int32_t id);
+void setCuttingBox(const physx::PxVec3& point, const physx::PxVec3& normal, Mesh* mesh, float size, int64_t id);
/**
Create cutting box at some particular position.
\param[in] point Cutting face center
@@ -178,7 +178,7 @@ void setCuttingBox(const physx::PxVec3& point, const physx::PxVec3& normal, Mesh
\param[in] size Cutting box size
\param[in] id Cutting box ID
*/
-Mesh* getCuttingBox(const physx::PxVec3& point, const physx::PxVec3& normal, float size, int32_t id, int32_t interiorMaterialId);
+Mesh* getCuttingBox(const physx::PxVec3& point, const physx::PxVec3& normal, float size, int64_t id, int32_t interiorMaterialId);
/**
Create box at some particular position.
@@ -208,7 +208,7 @@ Mesh* getNoisyCuttingBoxPair(const physx::PxVec3& point, const physx::PxVec3& no
\param[in] mesh Cutting box mesh
\param[in] id Cutting box ID
*/
-void inverseNormalAndSetIndices(Mesh* mesh, int32_t id);
+void inverseNormalAndSetIndices(Mesh* mesh, int64_t id);
} // namespace Blast
} // namespace Nv
diff --git a/sdk/extensions/authoring/source/VHACD/inc/FloatMath.h b/sdk/extensions/authoring/source/VHACD/inc/FloatMath.h
new file mode 100644
index 0000000..3414732
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/inc/FloatMath.h
@@ -0,0 +1,507 @@
+#ifndef FLOAT_MATH_LIB_H
+
+#define FLOAT_MATH_LIB_H
+
+
+#include <float.h>
+#include <stdint.h>
+
+namespace FLOAT_MATH
+{
+
+enum FM_ClipState
+{
+ FMCS_XMIN = (1<<0),
+ FMCS_XMAX = (1<<1),
+ FMCS_YMIN = (1<<2),
+ FMCS_YMAX = (1<<3),
+ FMCS_ZMIN = (1<<4),
+ FMCS_ZMAX = (1<<5),
+};
+
+enum FM_Axis
+{
+ FM_XAXIS = (1<<0),
+ FM_YAXIS = (1<<1),
+ FM_ZAXIS = (1<<2)
+};
+
+enum LineSegmentType
+{
+ LS_START,
+ LS_MIDDLE,
+ LS_END
+};
+
+
+const float FM_PI = 3.1415926535897932384626433832795028841971693993751f;
+const float FM_DEG_TO_RAD = ((2.0f * FM_PI) / 360.0f);
+const float FM_RAD_TO_DEG = (360.0f / (2.0f * FM_PI));
+
+//***************** Float versions
+//***
+//*** vectors are assumed to be 3 floats or 3 doubles representing X, Y, Z
+//*** quaternions are assumed to be 4 floats or 4 doubles representing X,Y,Z,W
+//*** matrices are assumed to be 16 floats or 16 doubles representing a standard D3D or OpenGL style 4x4 matrix
+//*** bounding volumes are expressed as two sets of 3 floats/double representing bmin(x,y,z) and bmax(x,y,z)
+//*** Plane equations are assumed to be 4 floats or 4 doubles representing Ax,By,Cz,D
+
+FM_Axis fm_getDominantAxis(const float normal[3]);
+FM_Axis fm_getDominantAxis(const double normal[3]);
+
+void fm_decomposeTransform(const float local_transform[16],float trans[3],float rot[4],float scale[3]);
+void fm_decomposeTransform(const double local_transform[16],double trans[3],double rot[4],double scale[3]);
+
+void fm_multiplyTransform(const float *pA,const float *pB,float *pM);
+void fm_multiplyTransform(const double *pA,const double *pB,double *pM);
+
+void fm_inverseTransform(const float matrix[16],float inverse_matrix[16]);
+void fm_inverseTransform(const double matrix[16],double inverse_matrix[16]);
+
+void fm_identity(float matrix[16]); // set 4x4 matrix to identity.
+void fm_identity(double matrix[16]); // set 4x4 matrix to identity.
+
+void fm_inverseRT(const float matrix[16], const float pos[3], float t[3]); // inverse rotate translate the point.
+void fm_inverseRT(const double matrix[16],const double pos[3],double t[3]); // inverse rotate translate the point.
+
+void fm_transform(const float matrix[16], const float pos[3], float t[3]); // rotate and translate this point.
+void fm_transform(const double matrix[16],const double pos[3],double t[3]); // rotate and translate this point.
+
+float fm_getDeterminant(const float matrix[16]);
+double fm_getDeterminant(const double matrix[16]);
+
+void fm_getSubMatrix(int32_t ki,int32_t kj,float pDst[16],const float matrix[16]);
+void fm_getSubMatrix(int32_t ki,int32_t kj,double pDst[16],const float matrix[16]);
+
+void fm_rotate(const float matrix[16],const float pos[3],float t[3]); // only rotate the point by a 4x4 matrix, don't translate.
+void fm_rotate(const double matri[16],const double pos[3],double t[3]); // only rotate the point by a 4x4 matrix, don't translate.
+
+void fm_eulerToMatrix(float ax,float ay,float az,float matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
+void fm_eulerToMatrix(double ax,double ay,double az,double matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
+
+void fm_getAABB(uint32_t vcount,const float *points,uint32_t pstride,float bmin[3],float bmax[3]);
+void fm_getAABB(uint32_t vcount,const double *points,uint32_t pstride,double bmin[3],double bmax[3]);
+
+void fm_getAABBCenter(const float bmin[3],const float bmax[3],float center[3]);
+void fm_getAABBCenter(const double bmin[3],const double bmax[3],double center[3]);
+
+void fm_transformAABB(const float bmin[3],const float bmax[3],const float matrix[16],float tbmin[3],float tbmax[3]);
+void fm_transformAABB(const double bmin[3],const double bmax[3],const double matrix[16],double tbmin[3],double tbmax[3]);
+
+void fm_eulerToQuat(float x,float y,float z,float quat[4]); // convert euler angles to quaternion.
+void fm_eulerToQuat(double x,double y,double z,double quat[4]); // convert euler angles to quaternion.
+
+void fm_quatToEuler(const float quat[4],float &ax,float &ay,float &az);
+void fm_quatToEuler(const double quat[4],double &ax,double &ay,double &az);
+
+void fm_eulerToQuat(const float euler[3],float quat[4]); // convert euler angles to quaternion. Angles must be radians not degrees!
+void fm_eulerToQuat(const double euler[3],double quat[4]); // convert euler angles to quaternion.
+
+void fm_scale(float x,float y,float z,float matrix[16]); // apply scale to the matrix.
+void fm_scale(double x,double y,double z,double matrix[16]); // apply scale to the matrix.
+
+void fm_eulerToQuatDX(float x,float y,float z,float quat[4]); // convert euler angles to quaternion using the fucked up DirectX method
+void fm_eulerToQuatDX(double x,double y,double z,double quat[4]); // convert euler angles to quaternion using the fucked up DirectX method
+
+void fm_eulerToMatrixDX(float x,float y,float z,float matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method.
+void fm_eulerToMatrixDX(double x,double y,double z,double matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method.
+
+void fm_quatToMatrix(const float quat[4],float matrix[16]); // convert quaterinion rotation to matrix, translation set to zero.
+void fm_quatToMatrix(const double quat[4],double matrix[16]); // convert quaterinion rotation to matrix, translation set to zero.
+
+void fm_quatRotate(const float quat[4],const float v[3],float r[3]); // rotate a vector directly by a quaternion.
+void fm_quatRotate(const double quat[4],const double v[3],double r[3]); // rotate a vector directly by a quaternion.
+
+void fm_getTranslation(const float matrix[16],float t[3]);
+void fm_getTranslation(const double matrix[16],double t[3]);
+
+void fm_setTranslation(const float *translation,float matrix[16]);
+void fm_setTranslation(const double *translation,double matrix[16]);
+
+void fm_multiplyQuat(const float *qa,const float *qb,float *quat);
+void fm_multiplyQuat(const double *qa,const double *qb,double *quat);
+
+void fm_matrixToQuat(const float matrix[16],float quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w
+void fm_matrixToQuat(const double matrix[16],double quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w
+
+float fm_sphereVolume(float radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed )
+double fm_sphereVolume(double radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed )
+
+float fm_cylinderVolume(float radius,float h);
+double fm_cylinderVolume(double radius,double h);
+
+float fm_capsuleVolume(float radius,float h);
+double fm_capsuleVolume(double radius,double h);
+
+float fm_distance(const float p1[3],const float p2[3]);
+double fm_distance(const double p1[3],const double p2[3]);
+
+float fm_distanceSquared(const float p1[3],const float p2[3]);
+double fm_distanceSquared(const double p1[3],const double p2[3]);
+
+float fm_distanceSquaredXZ(const float p1[3],const float p2[3]);
+double fm_distanceSquaredXZ(const double p1[3],const double p2[3]);
+
+float fm_computePlane(const float p1[3],const float p2[3],const float p3[3],float *n); // return D
+double fm_computePlane(const double p1[3],const double p2[3],const double p3[3],double *n); // return D
+
+float fm_distToPlane(const float plane[4],const float pos[3]); // computes the distance of this point from the plane.
+double fm_distToPlane(const double plane[4],const double pos[3]); // computes the distance of this point from the plane.
+
+float fm_dot(const float p1[3],const float p2[3]);
+double fm_dot(const double p1[3],const double p2[3]);
+
+void fm_cross(float cross[3],const float a[3],const float b[3]);
+void fm_cross(double cross[3],const double a[3],const double b[3]);
+
+void fm_computeNormalVector(float n[3],const float p1[3],const float p2[3]); // as P2-P1 normalized.
+void fm_computeNormalVector(double n[3],const double p1[3],const double p2[3]); // as P2-P1 normalized.
+
+bool fm_computeWindingOrder(const float p1[3],const float p2[3],const float p3[3]); // returns true if the triangle is clockwise.
+bool fm_computeWindingOrder(const double p1[3],const double p2[3],const double p3[3]); // returns true if the triangle is clockwise.
+
+float fm_normalize(float n[3]); // normalize this vector and return the distance
+double fm_normalize(double n[3]); // normalize this vector and return the distance
+
+float fm_normalizeQuat(float n[4]); // normalize this quat
+double fm_normalizeQuat(double n[4]); // normalize this quat
+
+void fm_matrixMultiply(const float A[16],const float B[16],float dest[16]);
+void fm_matrixMultiply(const double A[16],const double B[16],double dest[16]);
+
+void fm_composeTransform(const float position[3],const float quat[4],const float scale[3],float matrix[16]);
+void fm_composeTransform(const double position[3],const double quat[4],const double scale[3],double matrix[16]);
+
+float fm_computeArea(const float p1[3],const float p2[3],const float p3[3]);
+double fm_computeArea(const double p1[3],const double p2[3],const double p3[3]);
+
+void fm_lerp(const float p1[3],const float p2[3],float dest[3],float lerpValue);
+void fm_lerp(const double p1[3],const double p2[3],double dest[3],double lerpValue);
+
+bool fm_insideTriangleXZ(const float test[3],const float p1[3],const float p2[3],const float p3[3]);
+bool fm_insideTriangleXZ(const double test[3],const double p1[3],const double p2[3],const double p3[3]);
+
+bool fm_insideAABB(const float pos[3],const float bmin[3],const float bmax[3]);
+bool fm_insideAABB(const double pos[3],const double bmin[3],const double bmax[3]);
+
+bool fm_insideAABB(const float obmin[3],const float obmax[3],const float tbmin[3],const float tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax
+bool fm_insideAABB(const double obmin[3],const double obmax[3],const double tbmin[3],const double tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax
+
+uint32_t fm_clipTestPoint(const float bmin[3],const float bmax[3],const float pos[3]);
+uint32_t fm_clipTestPoint(const double bmin[3],const double bmax[3],const double pos[3]);
+
+uint32_t fm_clipTestPointXZ(const float bmin[3],const float bmax[3],const float pos[3]); // only tests X and Z, not Y
+uint32_t fm_clipTestPointXZ(const double bmin[3],const double bmax[3],const double pos[3]); // only tests X and Z, not Y
+
+
+uint32_t fm_clipTestAABB(const float bmin[3],const float bmax[3],const float p1[3],const float p2[3],const float p3[3],uint32_t &andCode);
+uint32_t fm_clipTestAABB(const double bmin[3],const double bmax[3],const double p1[3],const double p2[3],const double p3[3],uint32_t &andCode);
+
+
+bool fm_lineTestAABBXZ(const float p1[3],const float p2[3],const float bmin[3],const float bmax[3],float &time);
+bool fm_lineTestAABBXZ(const double p1[3],const double p2[3],const double bmin[3],const double bmax[3],double &time);
+
+bool fm_lineTestAABB(const float p1[3],const float p2[3],const float bmin[3],const float bmax[3],float &time);
+bool fm_lineTestAABB(const double p1[3],const double p2[3],const double bmin[3],const double bmax[3],double &time);
+
+
+void fm_initMinMax(const float p[3],float bmin[3],float bmax[3]);
+void fm_initMinMax(const double p[3],double bmin[3],double bmax[3]);
+
+void fm_initMinMax(float bmin[3],float bmax[3]);
+void fm_initMinMax(double bmin[3],double bmax[3]);
+
+void fm_minmax(const float p[3],float bmin[3],float bmax[3]); // accumulate to a min-max value
+void fm_minmax(const double p[3],double bmin[3],double bmax[3]); // accumulate to a min-max value
+
+// Computes the diagonal length of the bounding box and then inflates the bounding box on all sides
+// by the ratio provided.
+void fm_inflateMinMax(float bmin[3], float bmax[3], float ratio);
+void fm_inflateMinMax(double bmin[3], double bmax[3], double ratio);
+
+float fm_solveX(const float plane[4],float y,float z); // solve for X given this plane equation and the other two components.
+double fm_solveX(const double plane[4],double y,double z); // solve for X given this plane equation and the other two components.
+
+float fm_solveY(const float plane[4],float x,float z); // solve for Y given this plane equation and the other two components.
+double fm_solveY(const double plane[4],double x,double z); // solve for Y given this plane equation and the other two components.
+
+float fm_solveZ(const float plane[4],float x,float y); // solve for Z given this plane equation and the other two components.
+double fm_solveZ(const double plane[4],double x,double y); // solve for Z given this plane equation and the other two components.
+
+bool fm_computeBestFitPlane(uint32_t vcount, // number of input data points
+ const float *points, // starting address of points array.
+ uint32_t vstride, // stride between input points.
+ const float *weights, // *optional point weighting values.
+ uint32_t wstride, // weight stride for each vertex.
+ float plane[4]);
+
+bool fm_computeBestFitPlane(uint32_t vcount, // number of input data points
+ const double *points, // starting address of points array.
+ uint32_t vstride, // stride between input points.
+ const double *weights, // *optional point weighting values.
+ uint32_t wstride, // weight stride for each vertex.
+ double plane[4]);
+
+bool fm_computeCentroid(uint32_t vcount, // number of input data points
+ const float *points, // starting address of points array.
+ uint32_t vstride, // stride between input points.
+ float *center);
+
+bool fm_computeCentroid(uint32_t vcount, // number of input data points
+ const double *points, // starting address of points array.
+ uint32_t vstride, // stride between input points.
+ double *center);
+
+
+float fm_computeBestFitAABB(uint32_t vcount,const float *points,uint32_t pstride,float bmin[3],float bmax[3]); // returns the diagonal distance
+double fm_computeBestFitAABB(uint32_t vcount,const double *points,uint32_t pstride,double bmin[3],double bmax[3]); // returns the diagonal distance
+
+float fm_computeBestFitSphere(uint32_t vcount,const float *points,uint32_t pstride,float center[3]);
+double fm_computeBestFitSphere(uint32_t vcount,const double *points,uint32_t pstride,double center[3]);
+
+bool fm_lineSphereIntersect(const float center[3],float radius,const float p1[3],const float p2[3],float intersect[3]);
+bool fm_lineSphereIntersect(const double center[3],double radius,const double p1[3],const double p2[3],double intersect[3]);
+
+bool fm_intersectRayAABB(const float bmin[3],const float bmax[3],const float pos[3],const float dir[3],float intersect[3]);
+bool fm_intersectLineSegmentAABB(const float bmin[3],const float bmax[3],const float p1[3],const float p2[3],float intersect[3]);
+
+bool fm_lineIntersectsTriangle(const float rayStart[3],const float rayEnd[3],const float p1[3],const float p2[3],const float p3[3],float sect[3]);
+bool fm_lineIntersectsTriangle(const double rayStart[3],const double rayEnd[3],const double p1[3],const double p2[3],const double p3[3],double sect[3]);
+
+bool fm_rayIntersectsTriangle(const float origin[3],const float dir[3],const float v0[3],const float v1[3],const float v2[3],float &t);
+bool fm_rayIntersectsTriangle(const double origin[3],const double dir[3],const double v0[3],const double v1[3],const double v2[3],double &t);
+
+bool fm_raySphereIntersect(const float center[3],float radius,const float pos[3],const float dir[3],float distance,float intersect[3]);
+bool fm_raySphereIntersect(const double center[3],double radius,const double pos[3],const double dir[3],double distance,double intersect[3]);
+
+void fm_catmullRom(float out_vector[3],const float p1[3],const float p2[3],const float p3[3],const float *p4, const float s);
+void fm_catmullRom(double out_vector[3],const double p1[3],const double p2[3],const double p3[3],const double *p4, const double s);
+
+bool fm_intersectAABB(const float bmin1[3],const float bmax1[3],const float bmin2[3],const float bmax2[3]);
+bool fm_intersectAABB(const double bmin1[3],const double bmax1[3],const double bmin2[3],const double bmax2[3]);
+
+
+// computes the rotation quaternion to go from unit-vector v0 to unit-vector v1
+void fm_rotationArc(const float v0[3],const float v1[3],float quat[4]);
+void fm_rotationArc(const double v0[3],const double v1[3],double quat[4]);
+
+float fm_distancePointLineSegment(const float Point[3],const float LineStart[3],const float LineEnd[3],float intersection[3],LineSegmentType &type,float epsilon);
+double fm_distancePointLineSegment(const double Point[3],const double LineStart[3],const double LineEnd[3],double intersection[3],LineSegmentType &type,double epsilon);
+
+
+bool fm_colinear(const double p1[3],const double p2[3],const double p3[3],double epsilon=0.999); // true if these three points in a row are co-linear
+bool fm_colinear(const float p1[3],const float p2[3],const float p3[3],float epsilon=0.999f);
+
+bool fm_colinear(const float a1[3],const float a2[3],const float b1[3],const float b2[3],float epsilon=0.999f); // true if these two line segments are co-linear.
+bool fm_colinear(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double epsilon=0.999); // true if these two line segments are co-linear.
+
+enum IntersectResult
+{
+ IR_DONT_INTERSECT,
+ IR_DO_INTERSECT,
+ IR_COINCIDENT,
+ IR_PARALLEL,
+};
+
+IntersectResult fm_intersectLineSegments2d(const float a1[3], const float a2[3], const float b1[3], const float b2[3], float intersectionPoint[3]);
+IntersectResult fm_intersectLineSegments2d(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double intersectionPoint[3]);
+
+IntersectResult fm_intersectLineSegments2dTime(const float a1[3], const float a2[3], const float b1[3], const float b2[3],float &t1,float &t2);
+IntersectResult fm_intersectLineSegments2dTime(const double a1[3],const double a2[3],const double b1[3],const double b2[3],double &t1,double &t2);
+
+// Plane-Triangle splitting
+
+enum PlaneTriResult
+{
+ PTR_ON_PLANE,
+ PTR_FRONT,
+ PTR_BACK,
+ PTR_SPLIT,
+};
+
+PlaneTriResult fm_planeTriIntersection(const float plane[4], // the plane equation in Ax+By+Cz+D format
+ const float *triangle, // the source triangle.
+ uint32_t tstride, // stride in bytes of the input and output *vertices*
+ float epsilon, // the co-planer epsilon value.
+ float *front, // the triangle in front of the
+ uint32_t &fcount, // number of vertices in the 'front' triangle
+ float *back, // the triangle in back of the plane
+ uint32_t &bcount); // the number of vertices in the 'back' triangle.
+
+
+PlaneTriResult fm_planeTriIntersection(const double plane[4], // the plane equation in Ax+By+Cz+D format
+ const double *triangle, // the source triangle.
+ uint32_t tstride, // stride in bytes of the input and output *vertices*
+ double epsilon, // the co-planer epsilon value.
+ double *front, // the triangle in front of the
+ uint32_t &fcount, // number of vertices in the 'front' triangle
+ double *back, // the triangle in back of the plane
+ uint32_t &bcount); // the number of vertices in the 'back' triangle.
+
+
+void fm_intersectPointPlane(const float p1[3],const float p2[3],float *split,const float plane[4]);
+void fm_intersectPointPlane(const double p1[3],const double p2[3],double *split,const double plane[4]);
+
+PlaneTriResult fm_getSidePlane(const float p[3],const float plane[4],float epsilon);
+PlaneTriResult fm_getSidePlane(const double p[3],const double plane[4],double epsilon);
+
+
+void fm_computeBestFitOBB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float matrix[16],bool bruteForce=true);
+void fm_computeBestFitOBB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double matrix[16],bool bruteForce=true);
+
+void fm_computeBestFitOBB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float pos[3],float quat[4],bool bruteForce=true);
+void fm_computeBestFitOBB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double pos[3],double quat[4],bool bruteForce=true);
+
+void fm_computeBestFitABB(uint32_t vcount,const float *points,uint32_t pstride,float *sides,float pos[3]);
+void fm_computeBestFitABB(uint32_t vcount,const double *points,uint32_t pstride,double *sides,double pos[3]);
+
+
+//** Note, if the returned capsule height is less than zero, then you must represent it is a sphere of size radius.
+void fm_computeBestFitCapsule(uint32_t vcount,const float *points,uint32_t pstride,float &radius,float &height,float matrix[16],bool bruteForce=true);
+void fm_computeBestFitCapsule(uint32_t vcount,const double *points,uint32_t pstride,float &radius,float &height,double matrix[16],bool bruteForce=true);
+
+
+void fm_planeToMatrix(const float plane[4],float matrix[16]); // convert a plane equation to a 4x4 rotation matrix. Reference vector is 0,1,0
+void fm_planeToQuat(const float plane[4],float quat[4],float pos[3]); // convert a plane equation to a quaternion and translation
+
+void fm_planeToMatrix(const double plane[4],double matrix[16]); // convert a plane equation to a 4x4 rotation matrix
+void fm_planeToQuat(const double plane[4],double quat[4],double pos[3]); // convert a plane equation to a quaternion and translation
+
+inline void fm_doubleToFloat3(const double p[3],float t[3]) { t[0] = (float) p[0]; t[1] = (float)p[1]; t[2] = (float)p[2]; };
+inline void fm_floatToDouble3(const float p[3],double t[3]) { t[0] = (double)p[0]; t[1] = (double)p[1]; t[2] = (double)p[2]; };
+
+
+void fm_eulerMatrix(float ax,float ay,float az,float matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
+void fm_eulerMatrix(double ax,double ay,double az,double matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero)
+
+
+float fm_computeMeshVolume(const float *vertices,uint32_t tcount,const uint32_t *indices);
+double fm_computeMeshVolume(const double *vertices,uint32_t tcount,const uint32_t *indices);
+
+
+#define FM_DEFAULT_GRANULARITY 0.001f // 1 millimeter is the default granularity
+
+class fm_VertexIndex
+{
+public:
+ virtual uint32_t getIndex(const float pos[3],bool &newPos) = 0; // get welded index for this float vector[3]
+ virtual uint32_t getIndex(const double pos[3],bool &newPos) = 0; // get welded index for this double vector[3]
+ virtual const float * getVerticesFloat(void) const = 0;
+ virtual const double * getVerticesDouble(void) const = 0;
+ virtual const float * getVertexFloat(uint32_t index) const = 0;
+ virtual const double * getVertexDouble(uint32_t index) const = 0;
+ virtual uint32_t getVcount(void) const = 0;
+ virtual bool isDouble(void) const = 0;
+ virtual bool saveAsObj(const char *fname,uint32_t tcount,uint32_t *indices) = 0;
+};
+
+fm_VertexIndex * fm_createVertexIndex(double granularity,bool snapToGrid); // create an indexed vertex system for doubles
+fm_VertexIndex * fm_createVertexIndex(float granularity,bool snapToGrid); // create an indexed vertext system for floats
+void fm_releaseVertexIndex(fm_VertexIndex *vindex);
+
+
+class fm_Triangulate
+{
+public:
+ virtual const double * triangulate3d(uint32_t pcount,
+ const double *points,
+ uint32_t vstride,
+ uint32_t &tcount,
+ bool consolidate,
+ double epsilon) = 0;
+
+ virtual const float * triangulate3d(uint32_t pcount,
+ const float *points,
+ uint32_t vstride,
+ uint32_t &tcount,
+ bool consolidate,
+ float epsilon) = 0;
+};
+
+fm_Triangulate * fm_createTriangulate(void);
+void fm_releaseTriangulate(fm_Triangulate *t);
+
+
+const float * fm_getPoint(const float *points,uint32_t pstride,uint32_t index);
+const double * fm_getPoint(const double *points,uint32_t pstride,uint32_t index);
+
+bool fm_insideTriangle(float Ax, float Ay,float Bx, float By,float Cx, float Cy,float Px, float Py);
+bool fm_insideTriangle(double Ax, double Ay,double Bx, double By,double Cx, double Cy,double Px, double Py);
+float fm_areaPolygon2d(uint32_t pcount,const float *points,uint32_t pstride);
+double fm_areaPolygon2d(uint32_t pcount,const double *points,uint32_t pstride);
+
+bool fm_pointInsidePolygon2d(uint32_t pcount,const float *points,uint32_t pstride,const float *point,uint32_t xindex=0,uint32_t yindex=1);
+bool fm_pointInsidePolygon2d(uint32_t pcount,const double *points,uint32_t pstride,const double *point,uint32_t xindex=0,uint32_t yindex=1);
+
+uint32_t fm_consolidatePolygon(uint32_t pcount,const float *points,uint32_t pstride,float *dest,float epsilon=0.999999f); // collapses co-linear edges.
+uint32_t fm_consolidatePolygon(uint32_t pcount,const double *points,uint32_t pstride,double *dest,double epsilon=0.999999); // collapses co-linear edges.
+
+
+bool fm_computeSplitPlane(uint32_t vcount,const double *vertices,uint32_t tcount,const uint32_t *indices,double *plane);
+bool fm_computeSplitPlane(uint32_t vcount,const float *vertices,uint32_t tcount,const uint32_t *indices,float *plane);
+
+void fm_nearestPointInTriangle(const float *pos,const float *p1,const float *p2,const float *p3,float *nearest);
+void fm_nearestPointInTriangle(const double *pos,const double *p1,const double *p2,const double *p3,double *nearest);
+
+float fm_areaTriangle(const float *p1,const float *p2,const float *p3);
+double fm_areaTriangle(const double *p1,const double *p2,const double *p3);
+
+void fm_subtract(const float *A,const float *B,float *diff); // compute A-B and store the result in 'diff'
+void fm_subtract(const double *A,const double *B,double *diff); // compute A-B and store the result in 'diff'
+
+void fm_multiply(float *A,float scaler);
+void fm_multiply(double *A,double scaler);
+
+void fm_add(const float *A,const float *B,float *sum);
+void fm_add(const double *A,const double *B,double *sum);
+
+void fm_copy3(const float *source,float *dest);
+void fm_copy3(const double *source,double *dest);
+
+// re-indexes an indexed triangle mesh but drops unused vertices. The output_indices can be the same pointer as the input indices.
+// the output_vertices can point to the input vertices if you desire. The output_vertices buffer should be at least the same size
+// is the input buffer. The routine returns the new vertex count after re-indexing.
+uint32_t fm_copyUniqueVertices(uint32_t vcount,const float *input_vertices,float *output_vertices,uint32_t tcount,const uint32_t *input_indices,uint32_t *output_indices);
+uint32_t fm_copyUniqueVertices(uint32_t vcount,const double *input_vertices,double *output_vertices,uint32_t tcount,const uint32_t *input_indices,uint32_t *output_indices);
+
+bool fm_isMeshCoplanar(uint32_t tcount,const uint32_t *indices,const float *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar!
+bool fm_isMeshCoplanar(uint32_t tcount,const uint32_t *indices,const double *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar!
+
+bool fm_samePlane(const float p1[4],const float p2[4],float normalEpsilon=0.01f,float dEpsilon=0.001f,bool doubleSided=false); // returns true if these two plane equations are identical within an epsilon
+bool fm_samePlane(const double p1[4],const double p2[4],double normalEpsilon=0.01,double dEpsilon=0.001,bool doubleSided=false);
+
+void fm_OBBtoAABB(const float obmin[3],const float obmax[3],const float matrix[16],float abmin[3],float abmax[3]);
+
+// a utility class that will tessellate a mesh.
+class fm_Tesselate
+{
+public:
+ virtual const uint32_t * tesselate(fm_VertexIndex *vindex,uint32_t tcount,const uint32_t *indices,float longEdge,uint32_t maxDepth,uint32_t &outcount) = 0;
+};
+
+fm_Tesselate * fm_createTesselate(void);
+void fm_releaseTesselate(fm_Tesselate *t);
+
+void fm_computeMeanNormals(uint32_t vcount, // the number of vertices
+ const float *vertices, // the base address of the vertex position data.
+ uint32_t vstride, // the stride between position data.
+ float *normals, // the base address of the destination for mean vector normals
+ uint32_t nstride, // the stride between normals
+ uint32_t tcount, // the number of triangles
+ const uint32_t *indices); // the triangle indices
+
+void fm_computeMeanNormals(uint32_t vcount, // the number of vertices
+ const double *vertices, // the base address of the vertex position data.
+ uint32_t vstride, // the stride between position data.
+ double *normals, // the base address of the destination for mean vector normals
+ uint32_t nstride, // the stride between normals
+ uint32_t tcount, // the number of triangles
+ const uint32_t *indices); // the triangle indices
+
+
+bool fm_isValidTriangle(const float *p1,const float *p2,const float *p3,float epsilon=0.00001f);
+bool fm_isValidTriangle(const double *p1,const double *p2,const double *p3,double epsilon=0.00001f);
+
+}; // end of namespace
+
+#endif
diff --git a/sdk/extensions/authoring/source/VHACD/inc/btAlignedAllocator.h b/sdk/extensions/authoring/source/VHACD/inc/btAlignedAllocator.h
new file mode 100644
index 0000000..11f6e12
--- /dev/null
+++ b/sdk/extensions/authoring/source/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, int32_t alignment, int32_t line, char* filename);
+
+void btAlignedFreeInternal(void* ptr, int32_t line, char* filename);
+
+#else
+void* btAlignedAllocInternal(size_t size, int32_t alignment);
+void btAlignedFreeInternal(void* ptr);
+
+#define btAlignedAlloc(size, alignment) btAlignedAllocInternal(size, alignment)
+#define btAlignedFree(ptr) btAlignedFreeInternal(ptr)
+
+#endif
+typedef int32_t size_type;
+
+typedef void*(btAlignedAllocFunc)(size_t size, int32_t alignment);
+typedef void(btAlignedFreeFunc)(void* memblock);
+typedef void*(btAllocFunc)(size_t size);
+typedef void(btFreeFunc)(void* memblock);
+
+///The developer can let all Bullet memory allocations go through a custom memory allocator, using btAlignedAllocSetCustom
+void btAlignedAllocSetCustom(btAllocFunc* allocFunc, btFreeFunc* freeFunc);
+///If the developer has already an custom aligned allocator, then btAlignedAllocSetCustomAligned can be used. The default aligned allocator pre-allocates extra memory using the non-aligned allocator, and instruments it.
+void btAlignedAllocSetCustomAligned(btAlignedAllocFunc* allocFunc, btAlignedFreeFunc* freeFunc);
+
+///The btAlignedAllocator is a portable class for aligned memory allocations.
+///Default implementations for unaligned and aligned allocations can be overridden by a custom allocator using btAlignedAllocSetCustom and btAlignedAllocSetCustomAligned.
+template <typename T, unsigned Alignment>
+class btAlignedAllocator {
+
+ typedef btAlignedAllocator<T, Alignment> self_type;
+
+public:
+ //just going down a list:
+ btAlignedAllocator() {}
+ /*
+ btAlignedAllocator( const self_type & ) {}
+ */
+
+ template <typename Other>
+ btAlignedAllocator(const btAlignedAllocator<Other, Alignment>&) {}
+
+ 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<pointer>(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<void*>(ptr));
+ }
+ void destroy(pointer ptr) { ptr->~value_type(); }
+
+ template <typename O>
+ struct rebind {
+ typedef btAlignedAllocator<O, Alignment> other;
+ };
+ template <typename O>
+ self_type& operator=(const btAlignedAllocator<O, Alignment>&) { return *this; }
+
+ friend bool operator==(const self_type&, const self_type&) { return true; }
+};
+
+#endif //BT_ALIGNED_ALLOCATOR
diff --git a/sdk/extensions/authoring/source/VHACD/inc/btAlignedObjectArray.h b/sdk/extensions/authoring/source/VHACD/inc/btAlignedObjectArray.h
new file mode 100644
index 0000000..e6620ad
--- /dev/null
+++ b/sdk/extensions/authoring/source/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 <memory.h> or <string.h> 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 <memory.h>
+#include <string.h>
+#endif //BT_USE_MEMCPY
+
+#ifdef BT_USE_PLACEMENT_NEW
+#include <new> //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 <typename T>
+//template <class T>
+class btAlignedObjectArray {
+ btAlignedAllocator<T, 16> m_allocator;
+
+ int32_t m_size;
+ int32_t m_capacity;
+ T* m_data;
+ //PCK: added this line
+ bool m_ownsMemory;
+
+#ifdef BT_ALLOW_ARRAY_COPY_OPERATOR
+public:
+ SIMD_FORCE_INLINE btAlignedObjectArray<T>& operator=(const btAlignedObjectArray<T>& other)
+ {
+ copyFromArray(other);
+ return *this;
+ }
+#else //BT_ALLOW_ARRAY_COPY_OPERATOR
+private:
+ SIMD_FORCE_INLINE btAlignedObjectArray<T>& operator=(const btAlignedObjectArray<T>& other);
+#endif //BT_ALLOW_ARRAY_COPY_OPERATOR
+
+protected:
+ SIMD_FORCE_INLINE int32_t allocSize(int32_t size)
+ {
+ return (size ? size * 2 : 1);
+ }
+ SIMD_FORCE_INLINE void copy(int32_t start, int32_t end, T* dest) const
+ {
+ int32_t i;
+ for (i = start; i < end; ++i)
+#ifdef BT_USE_PLACEMENT_NEW
+ new (&dest[i]) T(m_data[i]);
+#else
+ dest[i] = m_data[i];
+#endif //BT_USE_PLACEMENT_NEW
+ }
+
+ SIMD_FORCE_INLINE void init()
+ {
+ //PCK: added this line
+ m_ownsMemory = true;
+ m_data = 0;
+ m_size = 0;
+ m_capacity = 0;
+ }
+ SIMD_FORCE_INLINE void destroy(int32_t first, int32_t last)
+ {
+ int32_t i;
+ for (i = first; i < last; i++) {
+ m_data[i].~T();
+ }
+ }
+
+ SIMD_FORCE_INLINE void* allocate(int32_t size)
+ {
+ if (size)
+ return m_allocator.allocate(size);
+ return 0;
+ }
+
+ SIMD_FORCE_INLINE void deallocate()
+ {
+ if (m_data) {
+ //PCK: enclosed the deallocation in this block
+ if (m_ownsMemory) {
+ m_allocator.deallocate(m_data);
+ }
+ m_data = 0;
+ }
+ }
+
+public:
+ btAlignedObjectArray()
+ {
+ init();
+ }
+
+ ~btAlignedObjectArray()
+ {
+ clear();
+ }
+
+ ///Generally it is best to avoid using the copy constructor of an btAlignedObjectArray, and use a (const) reference to the array instead.
+ btAlignedObjectArray(const btAlignedObjectArray& otherArray)
+ {
+ init();
+
+ int32_t otherSize = otherArray.size();
+ resize(otherSize);
+ otherArray.copy(0, otherSize, m_data);
+ }
+
+ /// return the number of elements in the array
+ SIMD_FORCE_INLINE int32_t size() const
+ {
+ return m_size;
+ }
+
+ SIMD_FORCE_INLINE const T& at(int32_t n) const
+ {
+ btAssert(n >= 0);
+ btAssert(n < size());
+ return m_data[n];
+ }
+
+ SIMD_FORCE_INLINE T& at(int32_t n)
+ {
+ btAssert(n >= 0);
+ btAssert(n < size());
+ return m_data[n];
+ }
+
+ SIMD_FORCE_INLINE const T& operator[](int32_t n) const
+ {
+ btAssert(n >= 0);
+ btAssert(n < size());
+ return m_data[n];
+ }
+
+ SIMD_FORCE_INLINE T& operator[](int32_t n)
+ {
+ btAssert(n >= 0);
+ btAssert(n < size());
+ return m_data[n];
+ }
+
+ ///clear the array, deallocated memory. Generally it is better to use array.resize(0), to reduce performance overhead of run-time memory (de)allocations.
+ SIMD_FORCE_INLINE void clear()
+ {
+ destroy(0, size());
+
+ deallocate();
+
+ init();
+ }
+
+ SIMD_FORCE_INLINE void pop_back()
+ {
+ btAssert(m_size > 0);
+ m_size--;
+ m_data[m_size].~T();
+ }
+
+ ///resize changes the number of elements in the array. If the new size is larger, the new elements will be constructed using the optional second argument.
+ ///when the new number of elements is smaller, the destructor will be called, but memory will not be freed, to reduce performance overhead of run-time memory (de)allocations.
+ SIMD_FORCE_INLINE void resize(int32_t newsize, const T& fillData = T())
+ {
+ int32_t curSize = size();
+
+ if (newsize < curSize) {
+ for (int32_t i = newsize; i < curSize; i++) {
+ m_data[i].~T();
+ }
+ }
+ else {
+ if (newsize > size()) {
+ reserve(newsize);
+ }
+#ifdef BT_USE_PLACEMENT_NEW
+ for (int32_t i = curSize; i < newsize; i++) {
+ new (&m_data[i]) T(fillData);
+ }
+#endif //BT_USE_PLACEMENT_NEW
+ }
+
+ m_size = newsize;
+ }
+
+ SIMD_FORCE_INLINE T& expandNonInitializing()
+ {
+ int32_t sz = size();
+ if (sz == capacity()) {
+ reserve(allocSize(size()));
+ }
+ m_size++;
+
+ return m_data[sz];
+ }
+
+ SIMD_FORCE_INLINE T& expand(const T& fillValue = T())
+ {
+ int32_t sz = size();
+ if (sz == capacity()) {
+ reserve(allocSize(size()));
+ }
+ m_size++;
+#ifdef BT_USE_PLACEMENT_NEW
+ new (&m_data[sz]) T(fillValue); //use the in-place new (not really allocating heap memory)
+#endif
+
+ return m_data[sz];
+ }
+
+ SIMD_FORCE_INLINE void push_back(const T& _Val)
+ {
+ int32_t sz = size();
+ if (sz == capacity()) {
+ reserve(allocSize(size()));
+ }
+
+#ifdef BT_USE_PLACEMENT_NEW
+ new (&m_data[m_size]) T(_Val);
+#else
+ m_data[size()] = _Val;
+#endif //BT_USE_PLACEMENT_NEW
+
+ m_size++;
+ }
+
+ /// return the pre-allocated (reserved) elements, this is at least as large as the total number of elements,see size() and reserve()
+ SIMD_FORCE_INLINE int32_t capacity() const
+ {
+ return m_capacity;
+ }
+
+ SIMD_FORCE_INLINE void reserve(int32_t _Count)
+ { // determine new minimum length of allocated storage
+ if (capacity() < _Count) { // not enough room, reallocate
+ T* s = (T*)allocate(_Count);
+
+ copy(0, size(), s);
+
+ destroy(0, size());
+
+ deallocate();
+
+ //PCK: added this line
+ m_ownsMemory = true;
+
+ m_data = s;
+
+ m_capacity = _Count;
+ }
+ }
+
+ class less {
+ public:
+ bool operator()(const T& a, const T& b)
+ {
+ return (a < b);
+ }
+ };
+
+ template <typename L>
+ void quickSortInternal(const L& CompareFunc, int32_t lo, int32_t hi)
+ {
+ // lo is the lower index, hi is the upper index
+ // of the region of array a that is to be sorted
+ int32_t i = lo, j = hi;
+ T x = m_data[(lo + hi) / 2];
+
+ // partition
+ do {
+ while (CompareFunc(m_data[i], x))
+ i++;
+ while (CompareFunc(x, m_data[j]))
+ j--;
+ if (i <= j) {
+ swap(i, j);
+ i++;
+ j--;
+ }
+ } while (i <= j);
+
+ // recursion
+ if (lo < j)
+ quickSortInternal(CompareFunc, lo, j);
+ if (i < hi)
+ quickSortInternal(CompareFunc, i, hi);
+ }
+
+ template <typename L>
+ 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 <typename L>
+ void downHeap(T* pArr, int32_t k, int32_t n, const L& CompareFunc)
+ {
+ /* PRE: a[k+1..N] is a heap */
+ /* POST: a[k..N] is a heap */
+
+ T temp = pArr[k - 1];
+ /* k has child(s) */
+ while (k <= n / 2) {
+ int32_t child = 2 * k;
+
+ if ((child < n) && CompareFunc(pArr[child - 1], pArr[child])) {
+ child++;
+ }
+ /* pick larger child */
+ if (CompareFunc(temp, pArr[child - 1])) {
+ /* move child up */
+ pArr[k - 1] = pArr[child - 1];
+ k = child;
+ }
+ else {
+ break;
+ }
+ }
+ pArr[k - 1] = temp;
+ } /*downHeap*/
+
+ void swap(int32_t index0, int32_t index1)
+ {
+#ifdef BT_USE_MEMCPY
+ char temp[sizeof(T)];
+ memcpy(temp, &m_data[index0], sizeof(T));
+ memcpy(&m_data[index0], &m_data[index1], sizeof(T));
+ memcpy(&m_data[index1], temp, sizeof(T));
+#else
+ T temp = m_data[index0];
+ m_data[index0] = m_data[index1];
+ m_data[index1] = temp;
+#endif //BT_USE_PLACEMENT_NEW
+ }
+
+ template <typename L>
+ void heapSort(const L& CompareFunc)
+ {
+ /* sort a[0..N-1], N.B. 0 to N-1 */
+ int32_t k;
+ int32_t n = m_size;
+ for (k = n / 2; k > 0; k--) {
+ downHeap(m_data, k, n, CompareFunc);
+ }
+
+ /* a[1..N] is now a heap */
+ while (n >= 1) {
+ swap(0, n - 1); /* largest of a[0..n-1] */
+
+ n = n - 1;
+ /* restore a[1..i-1] heap */
+ downHeap(m_data, 1, n, CompareFunc);
+ }
+ }
+
+ ///non-recursive binary search, assumes sorted array
+ int32_t findBinarySearch(const T& key) const
+ {
+ int32_t first = 0;
+ int32_t last = size() - 1;
+
+ //assume sorted array
+ while (first <= last) {
+ int32_t mid = (first + last) / 2; // compute mid point.
+ if (key > m_data[mid])
+ first = mid + 1; // repeat search in top half.
+ else if (key < m_data[mid])
+ last = mid - 1; // repeat search in bottom half.
+ else
+ return mid; // found it. return position /////
+ }
+ return size(); // failed to find key
+ }
+
+ int32_t findLinearSearch(const T& key) const
+ {
+ int32_t index = size();
+ int32_t i;
+
+ for (i = 0; i < size(); i++) {
+ if (m_data[i] == key) {
+ index = i;
+ break;
+ }
+ }
+ return index;
+ }
+
+ void remove(const T& key)
+ {
+
+ int32_t findIndex = findLinearSearch(key);
+ if (findIndex < size()) {
+ swap(findIndex, size() - 1);
+ pop_back();
+ }
+ }
+
+ //PCK: whole function
+ void initializeFromBuffer(void* buffer, int32_t size, int32_t capacity)
+ {
+ clear();
+ m_ownsMemory = false;
+ m_data = (T*)buffer;
+ m_size = size;
+ m_capacity = capacity;
+ }
+
+ void copyFromArray(const btAlignedObjectArray& otherArray)
+ {
+ int32_t otherSize = otherArray.size();
+ resize(otherSize);
+ otherArray.copy(0, otherSize, m_data);
+ }
+};
+
+#endif //BT_OBJECT_ARRAY__
diff --git a/sdk/extensions/authoring/source/VHACD/inc/btConvexHullComputer.h b/sdk/extensions/authoring/source/VHACD/inc/btConvexHullComputer.h
new file mode 100644
index 0000000..3c5075c
--- /dev/null
+++ b/sdk/extensions/authoring/source/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, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp);
+
+public:
+ class Edge {
+ private:
+ int32_t next;
+ int32_t reverse;
+ int32_t targetVertex;
+
+ friend class btConvexHullComputer;
+
+ public:
+ int32_t getSourceVertex() const
+ {
+ return (this + reverse)->targetVertex;
+ }
+
+ int32_t getTargetVertex() const
+ {
+ return targetVertex;
+ }
+
+ const Edge* getNextEdgeOfVertex() const // clockwise list of all edges of a vertex
+ {
+ return this + next;
+ }
+
+ const Edge* getNextEdgeOfFace() const // counter-clockwise list of all edges of a face
+ {
+ return (this + reverse)->getNextEdgeOfVertex();
+ }
+
+ const Edge* getReverseEdge() const
+ {
+ return this + reverse;
+ }
+ };
+
+ // Vertices of the output hull
+ btAlignedObjectArray<btVector3> vertices;
+
+ // Edges of the output hull
+ btAlignedObjectArray<Edge> 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<int32_t> faces;
+
+ /*
+ Compute convex hull of "count" vertices stored in "coords". "stride" is the difference in bytes
+ between the addresses of consecutive vertices. If "shrink" is positive, the convex hull is shrunken
+ by that amount (each face is moved by "shrink" length units towards the center along its normal).
+ If "shrinkClamp" is positive, "shrink" is clamped to not exceed "shrinkClamp * innerRadius", where "innerRadius"
+ is the minimum distance of a face to the center of the convex hull.
+
+ The returned value is the amount by which the hull has been shrunken. If it is negative, the amount was so large
+ that the resulting convex hull is empty.
+
+ The output convex hull can be found in the member variables "vertices", "edges", "faces".
+ */
+ btScalar compute(const float* coords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp)
+ {
+ return compute(coords, false, stride, count, shrink, shrinkClamp);
+ }
+
+ // same as above, but double precision
+ btScalar compute(const double* coords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp)
+ {
+ return compute(coords, true, stride, count, shrink, shrinkClamp);
+ }
+};
+
+#endif //BT_CONVEX_HULL_COMPUTER_H
diff --git a/sdk/extensions/authoring/source/VHACD/inc/btMinMax.h b/sdk/extensions/authoring/source/VHACD/inc/btMinMax.h
new file mode 100644
index 0000000..40b0ceb
--- /dev/null
+++ b/sdk/extensions/authoring/source/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 <class T>
+SIMD_FORCE_INLINE const T& btMin(const T& a, const T& b)
+{
+ return a < b ? a : b;
+}
+
+template <class T>
+SIMD_FORCE_INLINE const T& btMax(const T& a, const T& b)
+{
+ return a > b ? a : b;
+}
+
+template <class T>
+SIMD_FORCE_INLINE const T& btClamped(const T& a, const T& lb, const T& ub)
+{
+ return a < lb ? lb : (ub < a ? ub : a);
+}
+
+template <class T>
+SIMD_FORCE_INLINE void btSetMin(T& a, const T& b)
+{
+ if (b < a) {
+ a = b;
+ }
+}
+
+template <class T>
+SIMD_FORCE_INLINE void btSetMax(T& a, const T& b)
+{
+ if (a < b) {
+ a = b;
+ }
+}
+
+template <class T>
+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/sdk/extensions/authoring/source/VHACD/inc/btScalar.h b/sdk/extensions/authoring/source/VHACD/inc/btScalar.h
new file mode 100644
index 0000000..b814474
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/inc/btScalar.h
@@ -0,0 +1,533 @@
+/*
+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 <float.h>
+#include <math.h>
+#include <stdlib.h> //size_t for MSVC 6.0
+#include <stdint.h>
+
+/* SVN $Revision$ on $Date$ from http://bullet.googlecode.com*/
+#define BT_BULLET_VERSION 279
+
+inline int32_t btGetVersion()
+{
+ return BT_BULLET_VERSION;
+}
+
+#if defined(DEBUG) || defined(_DEBUG)
+#define BT_DEBUG
+#endif
+
+#ifdef _WIN32
+
+#if defined(__MINGW32__) || defined(__CYGWIN__) || (defined(_MSC_VER) && _MSC_VER < 1300)
+
+#define SIMD_FORCE_INLINE inline
+#define ATTRIBUTE_ALIGNED16(a) a
+#define ATTRIBUTE_ALIGNED64(a) a
+#define ATTRIBUTE_ALIGNED128(a) a
+#else
+//#define BT_HAS_ALIGNED_ALLOCATOR
+#pragma warning(disable : 4324) // disable padding warning
+// #pragma warning(disable:4530) // Disable the exception disable but used in MSCV Stl warning.
+// #pragma warning(disable:4996) //Turn off warnings about deprecated C routines
+// #pragma warning(disable:4786) // Disable the "debug name too long" warning
+
+#define SIMD_FORCE_INLINE __forceinline
+#define ATTRIBUTE_ALIGNED16(a) __declspec(align(16)) a
+#define ATTRIBUTE_ALIGNED64(a) __declspec(align(64)) a
+#define ATTRIBUTE_ALIGNED128(a) __declspec(align(128)) a
+#ifdef _XBOX
+#define BT_USE_VMX128
+
+#include <ppcintrinsics.h>
+#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 <emmintrin.h>
+#endif
+
+#endif //_XBOX
+
+#endif //__MINGW32__
+
+#include <assert.h>
+#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 <assert.h>
+#endif
+#ifdef BT_DEBUG
+#ifdef __SPU__
+#include <spu_printf.h>
+#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 <assert.h>
+#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 <emmintrin.h>
+
+#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 <assert.h>
+#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 <assert.h>
+#endif
+
+#if defined(DEBUG) || defined(_DEBUG)
+#define btAssert assert
+#else
+#define btAssert(x)
+#endif
+
+//btFullAssert is optional, slows down a lot
+#define btFullAssert(x)
+#define btLikely(_c) _c
+#define btUnlikely(_c) _c
+#endif //__APPLE__
+
+#endif // LIBSPE2
+
+#endif //__CELLOS_LV2__
+#endif
+
+///The btScalar type abstracts floating point numbers, to easily switch between double and single floating point precision.
+#if defined(BT_USE_DOUBLE_PRECISION)
+typedef double btScalar;
+//this number could be bigger in double precision
+#define BT_LARGE_FLOAT 1e30
+#else
+typedef float btScalar;
+//keep BT_LARGE_FLOAT*BT_LARGE_FLOAT < FLT_MAX
+#define BT_LARGE_FLOAT 1e18f
+#endif
+
+#define BT_DECLARE_ALIGNED_ALLOCATOR() \
+ SIMD_FORCE_INLINE void* operator new(size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes, 16); } \
+ SIMD_FORCE_INLINE void operator delete(void* ptr) { btAlignedFree(ptr); } \
+ SIMD_FORCE_INLINE void* operator new(size_t, void* ptr) { return ptr; } \
+ SIMD_FORCE_INLINE void operator delete(void*, void*) {} \
+ SIMD_FORCE_INLINE void* operator new[](size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes, 16); } \
+ SIMD_FORCE_INLINE void operator delete[](void* ptr) { btAlignedFree(ptr); } \
+ SIMD_FORCE_INLINE void* operator new[](size_t, void* ptr) { return ptr; } \
+ SIMD_FORCE_INLINE void operator delete[](void*, void*) {}
+
+#if defined(BT_USE_DOUBLE_PRECISION) || defined(BT_FORCE_DOUBLE_FUNCTIONS)
+
+SIMD_FORCE_INLINE btScalar btSqrt(btScalar x)
+{
+ return sqrt(x);
+}
+SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabs(x); }
+SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cos(x); }
+SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sin(x); }
+SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tan(x); }
+SIMD_FORCE_INLINE btScalar btAcos(btScalar x)
+{
+ if (x < btScalar(-1))
+ x = btScalar(-1);
+ if (x > btScalar(1))
+ x = btScalar(1);
+ return acos(x);
+}
+SIMD_FORCE_INLINE btScalar btAsin(btScalar x)
+{
+ if (x < btScalar(-1))
+ x = btScalar(-1);
+ if (x > btScalar(1))
+ x = btScalar(1);
+ return asin(x);
+}
+SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atan(x); }
+SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2(x, y); }
+SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return exp(x); }
+SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return log(x); }
+SIMD_FORCE_INLINE btScalar btPow(btScalar x, btScalar y) { return pow(x, y); }
+SIMD_FORCE_INLINE btScalar btFmod(btScalar x, btScalar y) { return fmod(x, y); }
+
+#else
+
+SIMD_FORCE_INLINE btScalar btSqrt(btScalar y)
+{
+#ifdef USE_APPROXIMATION
+ double x, z, tempf;
+ unsigned long* tfptr = ((unsigned long*)&tempf) + 1;
+
+ tempf = y;
+ *tfptr = (0xbfcdd90a - *tfptr) >> 1; /* estimate of 1/sqrt(y) */
+ x = tempf;
+ z = y * btScalar(0.5);
+ x = (btScalar(1.5) * x) - (x * x) * (x * z); /* iteration formula */
+ x = (btScalar(1.5) * x) - (x * x) * (x * z);
+ x = (btScalar(1.5) * x) - (x * x) * (x * z);
+ x = (btScalar(1.5) * x) - (x * x) * (x * z);
+ x = (btScalar(1.5) * x) - (x * x) * (x * z);
+ return x * y;
+#else
+ return sqrtf(y);
+#endif
+}
+SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabsf(x); }
+SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cosf(x); }
+SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sinf(x); }
+SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tanf(x); }
+SIMD_FORCE_INLINE btScalar btAcos(btScalar x)
+{
+ if (x < btScalar(-1))
+ x = btScalar(-1);
+ if (x > btScalar(1))
+ x = btScalar(1);
+ return acosf(x);
+}
+SIMD_FORCE_INLINE btScalar btAsin(btScalar x)
+{
+ if (x < btScalar(-1))
+ x = btScalar(-1);
+ if (x > btScalar(1))
+ x = btScalar(1);
+ return asinf(x);
+}
+SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atanf(x); }
+SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2f(x, y); }
+SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return expf(x); }
+SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return logf(x); }
+SIMD_FORCE_INLINE btScalar btPow(btScalar x, btScalar y) { return powf(x, y); }
+SIMD_FORCE_INLINE btScalar btFmod(btScalar x, btScalar y) { return fmodf(x, y); }
+
+#endif
+
+#define SIMD_2_PI btScalar(6.283185307179586232)
+#define SIMD_PI (SIMD_2_PI * btScalar(0.5))
+#define SIMD_HALF_PI (SIMD_2_PI * btScalar(0.25))
+#define SIMD_RADS_PER_DEG (SIMD_2_PI / btScalar(360.0))
+#define SIMD_DEGS_PER_RAD (btScalar(360.0) / SIMD_2_PI)
+#define SIMDSQRT12 btScalar(0.7071067811865475244008443621048490)
+
+#define btRecipSqrt(x) ((btScalar)(btScalar(1.0) / btSqrt(btScalar(x)))) /* reciprocal square root */
+
+#ifdef BT_USE_DOUBLE_PRECISION
+#define SIMD_EPSILON DBL_EPSILON
+#define SIMD_INFINITY DBL_MAX
+#else
+#define SIMD_EPSILON FLT_EPSILON
+#define SIMD_INFINITY FLT_MAX
+#endif
+
+SIMD_FORCE_INLINE btScalar btAtan2Fast(btScalar y, btScalar x)
+{
+ btScalar coeff_1 = SIMD_PI / 4.0f;
+ btScalar coeff_2 = 3.0f * coeff_1;
+ btScalar abs_y = btFabs(y);
+ btScalar angle;
+ if (x >= 0.0f) {
+ btScalar r = (x - abs_y) / (x + abs_y);
+ angle = coeff_1 - coeff_1 * r;
+ }
+ else {
+ btScalar r = (x + abs_y) / (abs_y - x);
+ angle = coeff_2 - coeff_1 * r;
+ }
+ return (y < 0.0f) ? -angle : angle;
+}
+
+SIMD_FORCE_INLINE bool btFuzzyZero(btScalar x) { return btFabs(x) < SIMD_EPSILON; }
+
+SIMD_FORCE_INLINE bool btEqual(btScalar a, btScalar eps)
+{
+ return (((a) <= eps) && !((a) < -eps));
+}
+SIMD_FORCE_INLINE bool btGreaterEqual(btScalar a, btScalar eps)
+{
+ return (!((a) <= eps));
+}
+
+SIMD_FORCE_INLINE int32_t btIsNegative(btScalar x)
+{
+ return x < btScalar(0.0) ? 1 : 0;
+}
+
+SIMD_FORCE_INLINE btScalar btRadians(btScalar x) { return x * SIMD_RADS_PER_DEG; }
+SIMD_FORCE_INLINE btScalar btDegrees(btScalar x) { return x * SIMD_DEGS_PER_RAD; }
+
+#define BT_DECLARE_HANDLE(name) \
+ typedef struct name##__ { \
+ int32_t unused; \
+ } * name
+
+#ifndef btFsel
+SIMD_FORCE_INLINE btScalar btFsel(btScalar a, btScalar b, btScalar c)
+{
+ return a >= 0 ? b : c;
+}
+#endif
+#define btFsels(a, b, c) (btScalar) btFsel(a, b, c)
+
+SIMD_FORCE_INLINE bool btMachineIsLittleEndian()
+{
+ long int i = 1;
+ const char* p = (const char*)&i;
+ if (p[0] == 1) // Lowest address contains the least significant byte
+ return true;
+ else
+ return false;
+}
+
+///btSelect avoids branches, which makes performance much better for consoles like Playstation 3 and XBox 360
+///Thanks Phil Knight. See also http://www.cellperformance.com/articles/2006/04/more_techniques_for_eliminatin_1.html
+SIMD_FORCE_INLINE unsigned btSelect(unsigned condition, unsigned valueIfConditionNonZero, unsigned valueIfConditionZero)
+{
+ // Set testNz to 0xFFFFFFFF if condition is nonzero, 0x00000000 if condition is zero
+ // Rely on positive value or'ed with its negative having sign bit on
+ // and zero value or'ed with its negative (which is still zero) having sign bit off
+ // Use arithmetic shift right, shifting the sign bit through all 32 bits
+ unsigned testNz = (unsigned)(((int32_t)condition | -(int32_t)condition) >> 31);
+ unsigned testEqz = ~testNz;
+ return ((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz));
+}
+SIMD_FORCE_INLINE int32_t btSelect(unsigned condition, int32_t valueIfConditionNonZero, int32_t valueIfConditionZero)
+{
+ unsigned testNz = (unsigned)(((int32_t)condition | -(int32_t)condition) >> 31);
+ unsigned testEqz = ~testNz;
+ return static_cast<int32_t>((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 <typename T>
+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<unsigned short>(((val & 0xff00) >> 8) | ((val & 0x00ff) << 8));
+}
+
+SIMD_FORCE_INLINE unsigned btSwapEndian(int32_t val)
+{
+ return btSwapEndian((unsigned)val);
+}
+
+SIMD_FORCE_INLINE unsigned short btSwapEndian(short val)
+{
+ return btSwapEndian((unsigned short)val);
+}
+
+///btSwapFloat uses using char pointers to swap the endianness
+////btSwapFloat/btSwapDouble will NOT return a float, because the machine might 'correct' invalid floating point values
+///Not all values of sign/exponent/mantissa are valid floating point numbers according to IEEE 754.
+///When a floating point unit is faced with an invalid value, it may actually change the value, or worse, throw an exception.
+///In most systems, running user mode code, you wouldn't get an exception, but instead the hardware/os/runtime will 'fix' the number for you.
+///so instead of returning a float/double, we return integer/long long integer
+SIMD_FORCE_INLINE uint32_t btSwapEndianFloat(float d)
+{
+ uint32_t a = 0;
+ unsigned char* dst = (unsigned char*)&a;
+ unsigned char* src = (unsigned char*)&d;
+
+ dst[0] = src[3];
+ dst[1] = src[2];
+ dst[2] = src[1];
+ dst[3] = src[0];
+ return a;
+}
+
+// unswap using char pointers
+SIMD_FORCE_INLINE float btUnswapEndianFloat(uint32_t a)
+{
+ float d = 0.0f;
+ unsigned char* src = (unsigned char*)&a;
+ unsigned char* dst = (unsigned char*)&d;
+
+ dst[0] = src[3];
+ dst[1] = src[2];
+ dst[2] = src[1];
+ dst[3] = src[0];
+
+ return d;
+}
+
+// swap using char pointers
+SIMD_FORCE_INLINE void btSwapEndianDouble(double d, unsigned char* dst)
+{
+ unsigned char* src = (unsigned char*)&d;
+
+ dst[0] = src[7];
+ dst[1] = src[6];
+ dst[2] = src[5];
+ dst[3] = src[4];
+ dst[4] = src[3];
+ dst[5] = src[2];
+ dst[6] = src[1];
+ dst[7] = src[0];
+}
+
+// unswap using char pointers
+SIMD_FORCE_INLINE double btUnswapEndianDouble(const unsigned char* src)
+{
+ double d = 0.0;
+ unsigned char* dst = (unsigned char*)&d;
+
+ dst[0] = src[7];
+ dst[1] = src[6];
+ dst[2] = src[5];
+ dst[3] = src[4];
+ dst[4] = src[3];
+ dst[5] = src[2];
+ dst[6] = src[1];
+ dst[7] = src[0];
+
+ return d;
+}
+
+// returns normalized value in range [-SIMD_PI, SIMD_PI]
+SIMD_FORCE_INLINE btScalar btNormalizeAngle(btScalar angleInRadians)
+{
+ angleInRadians = btFmod(angleInRadians, SIMD_2_PI);
+ if (angleInRadians < -SIMD_PI) {
+ return angleInRadians + SIMD_2_PI;
+ }
+ else if (angleInRadians > SIMD_PI) {
+ return angleInRadians - SIMD_2_PI;
+ }
+ else {
+ return angleInRadians;
+ }
+}
+
+///rudimentary class to provide type info
+struct btTypedObject {
+ btTypedObject(int32_t objectType)
+ : m_objectType(objectType)
+ {
+ }
+ int32_t m_objectType;
+ inline int32_t getObjectType() const
+ {
+ return m_objectType;
+ }
+};
+#endif //BT_SCALAR_H
diff --git a/sdk/extensions/authoring/source/VHACD/inc/btVector3.h b/sdk/extensions/authoring/source/VHACD/inc/btVector3.h
new file mode 100644
index 0000000..0f2fefb
--- /dev/null
+++ b/sdk/extensions/authoring/source/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();
+ int32_t maxIndex = absVec.maxAxis();
+ if (absVec[maxIndex] > 0) {
+ *this /= absVec[maxIndex];
+ return * this /= length();
+ }
+ setValue(1, 0, 0);
+ return *this;
+ }
+
+ /**@brief Normalize this vector
+ * x^2 + y^2 + z^2 = 1 */
+ SIMD_FORCE_INLINE btVector3& normalize()
+ {
+ return * this /= length();
+ }
+
+ /**@brief Return a normalized version of this vector */
+ SIMD_FORCE_INLINE btVector3 normalized() const;
+
+ /**@brief Return a rotated version of this vector
+ * @param wAxis The axis to rotate about
+ * @param angle The angle to rotate by */
+ SIMD_FORCE_INLINE btVector3 rotate(const btVector3& wAxis, const btScalar angle) const;
+
+ /**@brief Return the angle between this and another vector
+ * @param v The other vector */
+ SIMD_FORCE_INLINE btScalar angle(const btVector3& v) const
+ {
+ btScalar s = btSqrt(length2() * v.length2());
+ btFullAssert(s != btScalar(0.0));
+ return btAcos(dot(v) / s);
+ }
+ /**@brief Return a vector will the absolute values of each element */
+ SIMD_FORCE_INLINE btVector3 absolute() const
+ {
+ return btVector3(
+ btFabs(m_floats[0]),
+ btFabs(m_floats[1]),
+ btFabs(m_floats[2]));
+ }
+ /**@brief Return the cross product between this and another vector
+ * @param v The other vector */
+ SIMD_FORCE_INLINE btVector3 cross(const btVector3& v) const
+ {
+ return btVector3(
+ m_floats[1] * v.m_floats[2] - m_floats[2] * v.m_floats[1],
+ m_floats[2] * v.m_floats[0] - m_floats[0] * v.m_floats[2],
+ m_floats[0] * v.m_floats[1] - m_floats[1] * v.m_floats[0]);
+ }
+
+ SIMD_FORCE_INLINE btScalar triple(const btVector3& v1, const btVector3& v2) const
+ {
+ return m_floats[0] * (v1.m_floats[1] * v2.m_floats[2] - v1.m_floats[2] * v2.m_floats[1]) + m_floats[1] * (v1.m_floats[2] * v2.m_floats[0] - v1.m_floats[0] * v2.m_floats[2]) + m_floats[2] * (v1.m_floats[0] * v2.m_floats[1] - v1.m_floats[1] * v2.m_floats[0]);
+ }
+
+ /**@brief Return the axis with the smallest value
+ * Note return values are 0,1,2 for x, y, or z */
+ SIMD_FORCE_INLINE int32_t minAxis() const
+ {
+ return m_floats[0] < m_floats[1] ? (m_floats[0] < m_floats[2] ? 0 : 2) : (m_floats[1] < m_floats[2] ? 1 : 2);
+ }
+
+ /**@brief Return the axis with the largest value
+ * Note return values are 0,1,2 for x, y, or z */
+ SIMD_FORCE_INLINE int32_t maxAxis() const
+ {
+ return m_floats[0] < m_floats[1] ? (m_floats[1] < m_floats[2] ? 2 : 1) : (m_floats[0] < m_floats[2] ? 2 : 0);
+ }
+
+ SIMD_FORCE_INLINE int32_t furthestAxis() const
+ {
+ return absolute().minAxis();
+ }
+
+ SIMD_FORCE_INLINE int32_t closestAxis() const
+ {
+ return absolute().maxAxis();
+ }
+
+ SIMD_FORCE_INLINE void setInterpolate3(const btVector3& v0, const btVector3& v1, btScalar rt)
+ {
+ btScalar s = btScalar(1.0) - rt;
+ m_floats[0] = s * v0.m_floats[0] + rt * v1.m_floats[0];
+ m_floats[1] = s * v0.m_floats[1] + rt * v1.m_floats[1];
+ m_floats[2] = s * v0.m_floats[2] + rt * v1.m_floats[2];
+ //don't do the unused w component
+ // m_co[3] = s * v0[3] + rt * v1[3];
+ }
+
+ /**@brief Return the linear interpolation between this and another vector
+ * @param v The other vector
+ * @param t The ration of this to v (t = 0 => return this, t=1 => return other) */
+ SIMD_FORCE_INLINE btVector3 lerp(const btVector3& v, const btScalar& t) const
+ {
+ return btVector3(m_floats[0] + (v.m_floats[0] - m_floats[0]) * t,
+ m_floats[1] + (v.m_floats[1] - m_floats[1]) * t,
+ m_floats[2] + (v.m_floats[2] - m_floats[2]) * t);
+ }
+
+ /**@brief Elementwise multiply this vector by the other
+ * @param v The other vector */
+ SIMD_FORCE_INLINE btVector3& operator*=(const btVector3& v)
+ {
+ m_floats[0] *= v.m_floats[0];
+ m_floats[1] *= v.m_floats[1];
+ m_floats[2] *= v.m_floats[2];
+ return *this;
+ }
+
+ /**@brief Return the x value */
+ SIMD_FORCE_INLINE const btScalar& getX() const { return m_floats[0]; }
+ /**@brief Return the y value */
+ SIMD_FORCE_INLINE const btScalar& getY() const { return m_floats[1]; }
+ /**@brief Return the z value */
+ SIMD_FORCE_INLINE const btScalar& getZ() const { return m_floats[2]; }
+ /**@brief Set the x value */
+ SIMD_FORCE_INLINE void setX(btScalar x) { m_floats[0] = x; };
+ /**@brief Set the y value */
+ SIMD_FORCE_INLINE void setY(btScalar y) { m_floats[1] = y; };
+ /**@brief Set the z value */
+ SIMD_FORCE_INLINE void setZ(btScalar z) { m_floats[2] = z; };
+ /**@brief Set the w value */
+ SIMD_FORCE_INLINE void setW(btScalar w) { m_floats[3] = w; };
+ /**@brief Return the x value */
+ SIMD_FORCE_INLINE const btScalar& x() const { return m_floats[0]; }
+ /**@brief Return the y value */
+ SIMD_FORCE_INLINE const btScalar& y() const { return m_floats[1]; }
+ /**@brief Return the z value */
+ SIMD_FORCE_INLINE const btScalar& z() const { return m_floats[2]; }
+ /**@brief Return the w value */
+ SIMD_FORCE_INLINE const btScalar& w() const { return m_floats[3]; }
+
+ //SIMD_FORCE_INLINE btScalar& operator[](int32_t i) { return (&m_floats[0])[i]; }
+ //SIMD_FORCE_INLINE const btScalar& operator[](int32_t i) const { return (&m_floats[0])[i]; }
+ ///operator btScalar*() replaces operator[], using implicit conversion. We added operator != and operator == to avoid pointer comparisons.
+ SIMD_FORCE_INLINE operator btScalar*() { return &m_floats[0]; }
+ SIMD_FORCE_INLINE operator const btScalar*() const { return &m_floats[0]; }
+
+ SIMD_FORCE_INLINE bool operator==(const btVector3& other) const
+ {
+ return ((m_floats[3] == other.m_floats[3]) && (m_floats[2] == other.m_floats[2]) && (m_floats[1] == other.m_floats[1]) && (m_floats[0] == other.m_floats[0]));
+ }
+
+ SIMD_FORCE_INLINE bool operator!=(const btVector3& other) const
+ {
+ return !(*this == other);
+ }
+
+ /**@brief Set each element to the max of the current values and the values of another btVector3
+ * @param other The other btVector3 to compare with
+ */
+ SIMD_FORCE_INLINE void setMax(const btVector3& other)
+ {
+ btSetMax(m_floats[0], other.m_floats[0]);
+ btSetMax(m_floats[1], other.m_floats[1]);
+ btSetMax(m_floats[2], other.m_floats[2]);
+ btSetMax(m_floats[3], other.w());
+ }
+ /**@brief Set each element to the min of the current values and the values of another btVector3
+ * @param other The other btVector3 to compare with
+ */
+ SIMD_FORCE_INLINE void setMin(const btVector3& other)
+ {
+ btSetMin(m_floats[0], other.m_floats[0]);
+ btSetMin(m_floats[1], other.m_floats[1]);
+ btSetMin(m_floats[2], other.m_floats[2]);
+ btSetMin(m_floats[3], other.w());
+ }
+
+ SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z)
+ {
+ m_floats[0] = x;
+ m_floats[1] = y;
+ m_floats[2] = z;
+ m_floats[3] = btScalar(0.);
+ }
+
+ void getSkewSymmetricMatrix(btVector3 * v0, btVector3 * v1, btVector3 * v2) const
+ {
+ v0->setValue(0., -z(), y());
+ v1->setValue(z(), 0., -x());
+ v2->setValue(-y(), x(), 0.);
+ }
+
+ void setZero()
+ {
+ setValue(btScalar(0.), btScalar(0.), btScalar(0.));
+ }
+
+ SIMD_FORCE_INLINE bool isZero() const
+ {
+ return m_floats[0] == btScalar(0) && m_floats[1] == btScalar(0) && m_floats[2] == btScalar(0);
+ }
+
+ SIMD_FORCE_INLINE bool fuzzyZero() const
+ {
+ return length2() < SIMD_EPSILON;
+ }
+
+ SIMD_FORCE_INLINE void serialize(struct btVector3Data & dataOut) const;
+
+ SIMD_FORCE_INLINE void deSerialize(const struct btVector3Data& dataIn);
+
+ SIMD_FORCE_INLINE void serializeFloat(struct btVector3FloatData & dataOut) const;
+
+ SIMD_FORCE_INLINE void deSerializeFloat(const struct btVector3FloatData& dataIn);
+
+ SIMD_FORCE_INLINE void serializeDouble(struct btVector3DoubleData & dataOut) const;
+
+ SIMD_FORCE_INLINE void deSerializeDouble(const struct btVector3DoubleData& dataIn);
+};
+
+/**@brief Return the sum of two vectors (Point symantics)*/
+SIMD_FORCE_INLINE btVector3
+operator+(const btVector3& v1, const btVector3& v2)
+{
+ return btVector3(v1.m_floats[0] + v2.m_floats[0], v1.m_floats[1] + v2.m_floats[1], v1.m_floats[2] + v2.m_floats[2]);
+}
+
+/**@brief Return the elementwise product of two vectors */
+SIMD_FORCE_INLINE btVector3
+operator*(const btVector3& v1, const btVector3& v2)
+{
+ return btVector3(v1.m_floats[0] * v2.m_floats[0], v1.m_floats[1] * v2.m_floats[1], v1.m_floats[2] * v2.m_floats[2]);
+}
+
+/**@brief Return the difference between two vectors */
+SIMD_FORCE_INLINE btVector3
+operator-(const btVector3& v1, const btVector3& v2)
+{
+ return btVector3(v1.m_floats[0] - v2.m_floats[0], v1.m_floats[1] - v2.m_floats[1], v1.m_floats[2] - v2.m_floats[2]);
+}
+/**@brief Return the negative of the vector */
+SIMD_FORCE_INLINE btVector3
+operator-(const btVector3& v)
+{
+ return btVector3(-v.m_floats[0], -v.m_floats[1], -v.m_floats[2]);
+}
+
+/**@brief Return the vector scaled by s */
+SIMD_FORCE_INLINE btVector3
+operator*(const btVector3& v, const btScalar& s)
+{
+ return btVector3(v.m_floats[0] * s, v.m_floats[1] * s, v.m_floats[2] * s);
+}
+
+/**@brief Return the vector scaled by s */
+SIMD_FORCE_INLINE btVector3
+operator*(const btScalar& s, const btVector3& v)
+{
+ return v * s;
+}
+
+/**@brief Return the vector inversely scaled by s */
+SIMD_FORCE_INLINE btVector3
+operator/(const btVector3& v, const btScalar& s)
+{
+ btFullAssert(s != btScalar(0.0));
+ return v * (btScalar(1.0) / s);
+}
+
+/**@brief Return the vector inversely scaled by s */
+SIMD_FORCE_INLINE btVector3
+operator/(const btVector3& v1, const btVector3& v2)
+{
+ return btVector3(v1.m_floats[0] / v2.m_floats[0], v1.m_floats[1] / v2.m_floats[1], v1.m_floats[2] / v2.m_floats[2]);
+}
+
+/**@brief Return the dot product between two vectors */
+SIMD_FORCE_INLINE btScalar
+btDot(const btVector3& v1, const btVector3& v2)
+{
+ return v1.dot(v2);
+}
+
+/**@brief Return the distance squared between two vectors */
+SIMD_FORCE_INLINE btScalar
+btDistance2(const btVector3& v1, const btVector3& v2)
+{
+ return v1.distance2(v2);
+}
+
+/**@brief Return the distance between two vectors */
+SIMD_FORCE_INLINE btScalar
+btDistance(const btVector3& v1, const btVector3& v2)
+{
+ return v1.distance(v2);
+}
+
+/**@brief Return the angle between two vectors */
+SIMD_FORCE_INLINE btScalar
+btAngle(const btVector3& v1, const btVector3& v2)
+{
+ return v1.angle(v2);
+}
+
+/**@brief Return the cross product of two vectors */
+SIMD_FORCE_INLINE btVector3
+btCross(const btVector3& v1, const btVector3& v2)
+{
+ return v1.cross(v2);
+}
+
+SIMD_FORCE_INLINE btScalar
+btTriple(const btVector3& v1, const btVector3& v2, const btVector3& v3)
+{
+ return v1.triple(v2, v3);
+}
+
+/**@brief Return the linear interpolation between two vectors
+ * @param v1 One vector
+ * @param v2 The other vector
+ * @param t The ration of this to v (t = 0 => return v1, t=1 => return v2) */
+SIMD_FORCE_INLINE btVector3
+lerp(const btVector3& v1, const btVector3& v2, const btScalar& t)
+{
+ return v1.lerp(v2, t);
+}
+
+SIMD_FORCE_INLINE btScalar btVector3::distance2(const btVector3& v) const
+{
+ return (v - *this).length2();
+}
+
+SIMD_FORCE_INLINE btScalar btVector3::distance(const btVector3& v) const
+{
+ return (v - *this).length();
+}
+
+SIMD_FORCE_INLINE btVector3 btVector3::normalized() const
+{
+ return *this / length();
+}
+
+SIMD_FORCE_INLINE btVector3 btVector3::rotate(const btVector3& wAxis, const btScalar angle) const
+{
+ // wAxis must be a unit lenght vector
+
+ btVector3 o = wAxis * wAxis.dot(*this);
+ btVector3 x = *this - o;
+ btVector3 y;
+
+ y = wAxis.cross(*this);
+
+ return (o + x * btCos(angle) + y * btSin(angle));
+}
+
+class btVector4 : public btVector3 {
+public:
+ SIMD_FORCE_INLINE btVector4() {}
+
+ SIMD_FORCE_INLINE btVector4(const btScalar& x, const btScalar& y, const btScalar& z, const btScalar& w)
+ : btVector3(x, y, z)
+ {
+ m_floats[3] = w;
+ }
+
+ SIMD_FORCE_INLINE btVector4 absolute4() const
+ {
+ return btVector4(
+ btFabs(m_floats[0]),
+ btFabs(m_floats[1]),
+ btFabs(m_floats[2]),
+ btFabs(m_floats[3]));
+ }
+
+ btScalar getW() const { return m_floats[3]; }
+
+ SIMD_FORCE_INLINE int32_t maxAxis4() const
+ {
+ int32_t maxIndex = -1;
+ btScalar maxVal = btScalar(-BT_LARGE_FLOAT);
+ if (m_floats[0] > maxVal) {
+ maxIndex = 0;
+ maxVal = m_floats[0];
+ }
+ if (m_floats[1] > maxVal) {
+ maxIndex = 1;
+ maxVal = m_floats[1];
+ }
+ if (m_floats[2] > maxVal) {
+ maxIndex = 2;
+ maxVal = m_floats[2];
+ }
+ if (m_floats[3] > maxVal) {
+ maxIndex = 3;
+ }
+ return maxIndex;
+ }
+
+ SIMD_FORCE_INLINE int32_t minAxis4() const
+ {
+ int32_t minIndex = -1;
+ btScalar minVal = btScalar(BT_LARGE_FLOAT);
+ if (m_floats[0] < minVal) {
+ minIndex = 0;
+ minVal = m_floats[0];
+ }
+ if (m_floats[1] < minVal) {
+ minIndex = 1;
+ minVal = m_floats[1];
+ }
+ if (m_floats[2] < minVal) {
+ minIndex = 2;
+ minVal = m_floats[2];
+ }
+ if (m_floats[3] < minVal) {
+ minIndex = 3;
+ }
+
+ return minIndex;
+ }
+
+ SIMD_FORCE_INLINE int32_t closestAxis4() const
+ {
+ return absolute4().maxAxis4();
+ }
+
+ /**@brief Set x,y,z and zero w
+ * @param x Value of x
+ * @param y Value of y
+ * @param z Value of z
+ */
+
+ /* void getValue(btScalar *m) const
+ {
+ m[0] = m_floats[0];
+ m[1] = m_floats[1];
+ m[2] =m_floats[2];
+ }
+*/
+ /**@brief Set the values
+ * @param x Value of x
+ * @param y Value of y
+ * @param z Value of z
+ * @param w Value of w
+ */
+ SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z, const btScalar& w)
+ {
+ m_floats[0] = x;
+ m_floats[1] = y;
+ m_floats[2] = z;
+ m_floats[3] = w;
+ }
+};
+
+///btSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization
+SIMD_FORCE_INLINE void btSwapScalarEndian(const btScalar& sourceVal, btScalar& destVal)
+{
+#ifdef BT_USE_DOUBLE_PRECISION
+ unsigned char* dest = (unsigned char*)&destVal;
+ unsigned char* src = (unsigned char*)&sourceVal;
+ dest[0] = src[7];
+ dest[1] = src[6];
+ dest[2] = src[5];
+ dest[3] = src[4];
+ dest[4] = src[3];
+ dest[5] = src[2];
+ dest[6] = src[1];
+ dest[7] = src[0];
+#else
+ unsigned char* dest = (unsigned char*)&destVal;
+ unsigned char* src = (unsigned char*)&sourceVal;
+ dest[0] = src[3];
+ dest[1] = src[2];
+ dest[2] = src[1];
+ dest[3] = src[0];
+#endif //BT_USE_DOUBLE_PRECISION
+}
+///btSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization
+SIMD_FORCE_INLINE void btSwapVector3Endian(const btVector3& sourceVec, btVector3& destVec)
+{
+ for (int32_t i = 0; i < 4; i++) {
+ btSwapScalarEndian(sourceVec[i], destVec[i]);
+ }
+}
+
+///btUnSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization
+SIMD_FORCE_INLINE void btUnSwapVector3Endian(btVector3& vector)
+{
+
+ btVector3 swappedVec;
+ for (int32_t i = 0; i < 4; i++) {
+ btSwapScalarEndian(vector[i], swappedVec[i]);
+ }
+ vector = swappedVec;
+}
+
+template <class T>
+SIMD_FORCE_INLINE void btPlaneSpace1(const T& n, T& p, T& q)
+{
+ if (btFabs(n[2]) > SIMDSQRT12) {
+ // choose p in y-z plane
+ btScalar a = n[1] * n[1] + n[2] * n[2];
+ btScalar k = btRecipSqrt(a);
+ p[0] = 0;
+ p[1] = -n[2] * k;
+ p[2] = n[1] * k;
+ // set q = n x p
+ q[0] = a * k;
+ q[1] = -n[0] * p[2];
+ q[2] = n[0] * p[1];
+ }
+ else {
+ // choose p in x-y plane
+ btScalar a = n[0] * n[0] + n[1] * n[1];
+ btScalar k = btRecipSqrt(a);
+ p[0] = -n[1] * k;
+ p[1] = n[0] * k;
+ p[2] = 0;
+ // set q = n x p
+ q[0] = -n[2] * p[1];
+ q[1] = n[2] * p[0];
+ q[2] = a * k;
+ }
+}
+
+struct btVector3FloatData {
+ float m_floats[4];
+};
+
+struct btVector3DoubleData {
+ double m_floats[4];
+};
+
+SIMD_FORCE_INLINE void btVector3::serializeFloat(struct btVector3FloatData& dataOut) const
+{
+ ///could also do a memcpy, check if it is worth it
+ for (int32_t i = 0; i < 4; i++)
+ dataOut.m_floats[i] = float(m_floats[i]);
+}
+
+SIMD_FORCE_INLINE void btVector3::deSerializeFloat(const struct btVector3FloatData& dataIn)
+{
+ for (int32_t i = 0; i < 4; i++)
+ m_floats[i] = btScalar(dataIn.m_floats[i]);
+}
+
+SIMD_FORCE_INLINE void btVector3::serializeDouble(struct btVector3DoubleData& dataOut) const
+{
+ ///could also do a memcpy, check if it is worth it
+ for (int32_t i = 0; i < 4; i++)
+ dataOut.m_floats[i] = double(m_floats[i]);
+}
+
+SIMD_FORCE_INLINE void btVector3::deSerializeDouble(const struct btVector3DoubleData& dataIn)
+{
+ for (int32_t i = 0; i < 4; i++)
+ m_floats[i] = btScalar(dataIn.m_floats[i]);
+}
+
+SIMD_FORCE_INLINE void btVector3::serialize(struct btVector3Data& dataOut) const
+{
+ ///could also do a memcpy, check if it is worth it
+ for (int32_t i = 0; i < 4; i++)
+ dataOut.m_floats[i] = m_floats[i];
+}
+
+SIMD_FORCE_INLINE void btVector3::deSerialize(const struct btVector3Data& dataIn)
+{
+ for (int32_t i = 0; i < 4; i++)
+ m_floats[i] = dataIn.m_floats[i];
+}
+
+#endif //BT_VECTOR3_H
diff --git a/sdk/extensions/authoring/source/VHACD/inc/vhacdCircularList.h b/sdk/extensions/authoring/source/VHACD/inc/vhacdCircularList.h
new file mode 100644
index 0000000..0f5ddf9
--- /dev/null
+++ b/sdk/extensions/authoring/source/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 <stdlib.h>
+namespace VHACD {
+//! CircularListElement class.
+template <typename T>
+class CircularListElement {
+public:
+ T& GetData() { return m_data; }
+ const T& GetData() const { return m_data; }
+ CircularListElement<T>*& GetNext() { return m_next; }
+ CircularListElement<T>*& GetPrev() { return m_prev; }
+ const CircularListElement<T>*& GetNext() const { return m_next; }
+ const CircularListElement<T>*& GetPrev() const { return m_prev; }
+ //! Constructor
+ CircularListElement(const T& data) { m_data = data; }
+ CircularListElement(void) {}
+ //! Destructor
+ ~CircularListElement(void) {}
+private:
+ T m_data;
+ CircularListElement<T>* m_next;
+ CircularListElement<T>* m_prev;
+
+ CircularListElement(const CircularListElement& rhs);
+};
+//! CircularList class.
+template <typename T>
+class CircularList {
+public:
+ CircularListElement<T>*& GetHead() { return m_head; }
+ const CircularListElement<T>* 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<T>* element);
+ CircularListElement<T>* Add(const T* data = 0);
+ CircularListElement<T>* 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<T>* 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/sdk/extensions/authoring/source/VHACD/inc/vhacdICHull.h b/sdk/extensions/authoring/source/VHACD/inc/vhacdICHull.h
new file mode 100644
index 0000000..132bdcf
--- /dev/null
+++ b/sdk/extensions/authoring/source/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<double>& point) { return AddPoints(&point, 1); }
+ //! Add one point to the convex-hull
+ bool AddPoint(const Vec3<double>& point, int32_t id);
+ //! Add points to the convex-hull
+ bool AddPoints(const Vec3<double>* points, size_t nPoints);
+ //!
+ ICHullError Process();
+ //!
+ ICHullError Process(const uint32_t nPointsCH, const double minVolume = 0.0);
+ //!
+ bool IsInside(const Vec3<double>& 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<TMMTriangle>* MakeFace(CircularListElement<TMMVertex>* v0,
+ CircularListElement<TMMVertex>* v1,
+ CircularListElement<TMMVertex>* v2,
+ CircularListElement<TMMTriangle>* fold);
+ //!
+ CircularListElement<TMMTriangle>* MakeConeFace(CircularListElement<TMMEdge>* e, CircularListElement<TMMVertex>* v);
+ //!
+ bool ProcessPoint();
+ //!
+ bool ComputePointVolume(double& totalVolume, bool markVisibleFaces);
+ //!
+ bool FindMaxVolumePoint(const double minVolume = 0.0);
+ //!
+ bool CleanEdges();
+ //!
+ bool CleanVertices(uint32_t& addedPoints);
+ //!
+ bool CleanTriangles();
+ //!
+ bool CleanUp(uint32_t& addedPoints);
+ //!
+ bool MakeCCW(CircularListElement<TMMTriangle>* f,
+ CircularListElement<TMMEdge>* e,
+ CircularListElement<TMMVertex>* v);
+ void Clear();
+
+private:
+ static const int32_t sc_dummyIndex;
+ TMMesh m_mesh;
+ SArray<CircularListElement<TMMEdge>*> m_edgesToDelete;
+ SArray<CircularListElement<TMMEdge>*> m_edgesToUpdate;
+ SArray<CircularListElement<TMMTriangle>*> m_trianglesToDelete;
+ Vec3<double> m_normal;
+ bool m_isFlat;
+ ICHull(const ICHull& rhs);
+};
+}
+#endif // VHACD_ICHULL_H
diff --git a/sdk/extensions/authoring/source/VHACD/inc/vhacdManifoldMesh.h b/sdk/extensions/authoring/source/VHACD/inc/vhacdManifoldMesh.h
new file mode 100644
index 0000000..a48f53c
--- /dev/null
+++ b/sdk/extensions/authoring/source/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<double> m_pos;
+ int32_t m_name;
+ size_t m_id;
+ CircularListElement<TMMEdge>* 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<TMMTriangle>* m_triangles[2];
+ CircularListElement<TMMVertex>* m_vertices[2];
+ CircularListElement<TMMTriangle>* 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<TMMEdge>* m_edges[3];
+ CircularListElement<TMMVertex>* 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<TMMVertex>& GetVertices() const { return m_vertices; }
+ //! Returns the edges circular list
+ inline const CircularList<TMMEdge>& GetEdges() const { return m_edges; }
+ //! Returns the triangles circular list
+ inline const CircularList<TMMTriangle>& GetTriangles() const { return m_triangles; }
+ //! Returns the vertices circular list
+ inline CircularList<TMMVertex>& GetVertices() { return m_vertices; }
+ //! Returns the edges circular list
+ inline CircularList<TMMEdge>& GetEdges() { return m_edges; }
+ //! Returns the triangles circular list
+ inline CircularList<TMMTriangle>& GetTriangles() { return m_triangles; }
+ //! Add vertex to the mesh
+ CircularListElement<TMMVertex>* AddVertex() { return m_vertices.Add(); }
+ //! Add vertex to the mesh
+ CircularListElement<TMMEdge>* AddEdge() { return m_edges.Add(); }
+ //! Add vertex to the mesh
+ CircularListElement<TMMTriangle>* AddTriangle() { return m_triangles.Add(); }
+ //! Print mesh information
+ void Print();
+ //!
+ void GetIFS(Vec3<double>* const points, Vec3<int32_t>* const triangles);
+ //!
+ void Clear();
+ //!
+ void Copy(TMMesh& mesh);
+ //!
+ bool CheckConsistancy();
+ //!
+ bool Normalize();
+ //!
+ bool Denormalize();
+ //! Constructor
+ TMMesh();
+ //! Destructor
+ virtual ~TMMesh(void);
+
+private:
+ CircularList<TMMVertex> m_vertices;
+ CircularList<TMMEdge> m_edges;
+ CircularList<TMMTriangle> 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/sdk/extensions/authoring/source/VHACD/inc/vhacdMesh.h b/sdk/extensions/authoring/source/VHACD/inc/vhacdMesh.h
new file mode 100644
index 0000000..a282e2b
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/inc/vhacdMesh.h
@@ -0,0 +1,130 @@
+/* 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<double> m_diffuseColor;
+ double m_ambientIntensity;
+ Vec3<double> m_specularColor;
+ Vec3<double> 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<double>& pt) { m_points.PushBack(pt); };
+ void SetPoint(size_t index, const Vec3<double>& pt) { m_points[index] = pt; };
+ const Vec3<double>& GetPoint(size_t index) const { return m_points[index]; };
+ Vec3<double>& 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<double>* const GetPointsBuffer() const { return m_points.Data(); } //
+ Vec3<double>* const GetPointsBuffer() { return m_points.Data(); } //
+ void AddTriangle(const Vec3<int32_t>& tri) { m_triangles.PushBack(tri); };
+ void SetTriangle(size_t index, const Vec3<int32_t>& tri) { m_triangles[index] = tri; };
+ const Vec3<int32_t>& GetTriangle(size_t index) const { return m_triangles[index]; };
+ Vec3<int32_t>& GetTriangle(size_t index) { return m_triangles[index]; };
+ size_t GetNTriangles() const { return m_triangles.Size(); };
+ int32_t* GetTriangles() { return (int32_t*)m_triangles.Data(); } // ugly
+ const int32_t* const GetTriangles() const { return (int32_t*)m_triangles.Data(); } // ugly
+ const Vec3<int32_t>* const GetTrianglesBuffer() const { return m_triangles.Data(); }
+ Vec3<int32_t>* const GetTrianglesBuffer() { return m_triangles.Data(); }
+ const Vec3<double>& GetCenter() const { return m_center; }
+ const Vec3<double>& GetMinBB() const { return m_minBB; }
+ const Vec3<double>& 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<Vec3<double> >& 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<Vec3<double> >& positivePart,
+ SArray<Vec3<double> >& negativePart) const;
+ bool IsInside(const Vec3<double>& pt) const;
+ double ComputeDiagBB();
+ Vec3<double> &ComputeCenter(void);
+
+#ifdef VHACD_DEBUG_MESH
+ bool LoadOFF(const std::string& fileName, bool invert);
+ bool SaveVRML2(const std::string& fileName) const;
+ bool SaveVRML2(std::ofstream& fout, const Material& material) const;
+ bool SaveOFF(const std::string& fileName) const;
+#endif // VHACD_DEBUG_MESH
+
+ //! Constructor.
+ Mesh();
+ //! Destructor.
+ ~Mesh(void);
+
+private:
+ SArray<Vec3<double> > m_points;
+ SArray<Vec3<int32_t> > m_triangles;
+ Vec3<double> m_minBB;
+ Vec3<double> m_maxBB;
+ Vec3<double> m_center;
+ double m_diag;
+};
+}
+#endif \ No newline at end of file
diff --git a/sdk/extensions/authoring/source/VHACD/inc/vhacdMutex.h b/sdk/extensions/authoring/source/VHACD/inc/vhacdMutex.h
new file mode 100644
index 0000000..6b09259
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/inc/vhacdMutex.h
@@ -0,0 +1,148 @@
+/*!
+**
+** Copyright (c) 2009 by John W. Ratcliff mailto:[email protected]
+**
+** 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
+**
+**
+** The MIT license:
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+** copies of the Software, and to permit persons to whom the Software is furnished
+** to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in all
+** copies or substantial portions of the Software.
+
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+#pragma once
+#ifndef VHACD_MUTEX_H
+#define VHACD_MUTEX_H
+
+#if defined(WIN32)
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x400
+#endif
+#include <windows.h>
+#pragma comment(lib, "winmm.lib")
+#endif
+
+#if defined(__linux__)
+//#include <sys/time.h>
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#define __stdcall
+#endif
+
+#if defined(__APPLE__) || defined(__linux__)
+#include <pthread.h>
+#endif
+
+#if defined(__APPLE__)
+#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
+#endif
+
+#define VHACD_DEBUG
+
+//#define VHACD_NDEBUG
+#ifdef VHACD_NDEBUG
+#define VHACD_VERIFY(x) (x)
+#else
+#define VHACD_VERIFY(x) assert((x))
+#endif
+
+namespace VHACD {
+class Mutex {
+public:
+ Mutex(void)
+ {
+#if defined(WIN32) || defined(_XBOX)
+ InitializeCriticalSection(&m_mutex);
+#elif defined(__APPLE__) || defined(__linux__)
+ pthread_mutexattr_t mutexAttr; // Mutex Attribute
+ VHACD_VERIFY(pthread_mutexattr_init(&mutexAttr) == 0);
+ VHACD_VERIFY(pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE_NP) == 0);
+ VHACD_VERIFY(pthread_mutex_init(&m_mutex, &mutexAttr) == 0);
+ VHACD_VERIFY(pthread_mutexattr_destroy(&mutexAttr) == 0);
+#endif
+ }
+ ~Mutex(void)
+ {
+#if defined(WIN32) || defined(_XBOX)
+ DeleteCriticalSection(&m_mutex);
+#elif defined(__APPLE__) || defined(__linux__)
+ VHACD_VERIFY(pthread_mutex_destroy(&m_mutex) == 0);
+#endif
+ }
+ void Lock(void)
+ {
+#if defined(WIN32) || defined(_XBOX)
+ EnterCriticalSection(&m_mutex);
+#elif defined(__APPLE__) || defined(__linux__)
+ VHACD_VERIFY(pthread_mutex_lock(&m_mutex) == 0);
+#endif
+ }
+ bool TryLock(void)
+ {
+#if defined(WIN32) || defined(_XBOX)
+ bool bRet = false;
+ //assert(("TryEnterCriticalSection seems to not work on XP???", 0));
+ bRet = TryEnterCriticalSection(&m_mutex) ? true : false;
+ return bRet;
+#elif defined(__APPLE__) || defined(__linux__)
+ int32_t result = pthread_mutex_trylock(&m_mutex);
+ return (result == 0);
+#endif
+ }
+
+ void Unlock(void)
+ {
+#if defined(WIN32) || defined(_XBOX)
+ LeaveCriticalSection(&m_mutex);
+#elif defined(__APPLE__) || defined(__linux__)
+ VHACD_VERIFY(pthread_mutex_unlock(&m_mutex) == 0);
+#endif
+ }
+
+private:
+#if defined(WIN32) || defined(_XBOX)
+ CRITICAL_SECTION m_mutex;
+#elif defined(__APPLE__) || defined(__linux__)
+ pthread_mutex_t m_mutex;
+#endif
+};
+}
+#endif // VHACD_MUTEX_H
diff --git a/sdk/extensions/authoring/source/VHACD/inc/vhacdRaycastMesh.h b/sdk/extensions/authoring/source/VHACD/inc/vhacdRaycastMesh.h
new file mode 100644
index 0000000..93bf299
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/inc/vhacdRaycastMesh.h
@@ -0,0 +1,39 @@
+#ifndef RAYCAST_MESH_H
+
+#define RAYCAST_MESH_H
+
+#include <stdint.h>
+
+namespace VHACD
+{
+
+ // Very simple brute force raycast against a triangle mesh. Tests every triangle; no hierachy.
+ // Does a deep copy, always does calculations with full double float precision
+ class RaycastMesh
+ {
+ public:
+ static RaycastMesh * createRaycastMesh(uint32_t vcount, // The number of vertices in the source triangle mesh
+ const double *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc.
+ uint32_t tcount, // The number of triangles in the source triangle mesh
+ const uint32_t *indices); // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ...
+
+ static RaycastMesh * createRaycastMesh(uint32_t vcount, // The number of vertices in the source triangle mesh
+ const float *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc.
+ uint32_t tcount, // The number of triangles in the source triangle mesh
+ const uint32_t *indices); // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ...
+
+
+ virtual bool raycast(const double *from, // The starting point of the raycast
+ const double *to, // The ending point of the raycast
+ const double *closestToPoint, // The point to match the nearest hit location (can just be the 'from' location of no specific point)
+ double *hitLocation, // The point where the ray hit nearest to the 'closestToPoint' location
+ double *hitDistance) = 0; // The distance the ray traveled to the hit location
+
+ virtual void release(void) = 0;
+ protected:
+ virtual ~RaycastMesh(void) { };
+ };
+
+} // end of VHACD namespace
+
+#endif
diff --git a/sdk/extensions/authoring/source/VHACD/inc/vhacdSArray.h b/sdk/extensions/authoring/source/VHACD/inc/vhacdSArray.h
new file mode 100644
index 0000000..4d84d1a
--- /dev/null
+++ b/sdk/extensions/authoring/source/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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define SARRAY_DEFAULT_MIN_SIZE 16
+
+namespace VHACD {
+//! SArray.
+template <typename T, size_t N = 64>
+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/sdk/extensions/authoring/source/VHACD/inc/vhacdTimer.h b/sdk/extensions/authoring/source/VHACD/inc/vhacdTimer.h
new file mode 100644
index 0000000..ba0e2c3
--- /dev/null
+++ b/sdk/extensions/authoring/source/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 <windows.h>
+#elif __MACH__
+#include <mach/clock.h>
+#include <mach/mach.h>
+#else
+#include <sys/time.h>
+#include <time.h>
+#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/sdk/extensions/authoring/source/VHACD/inc/vhacdVHACD.h b/sdk/extensions/authoring/source/VHACD/inc/vhacdVHACD.h
new file mode 100644
index 0000000..1a3ff19
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/inc/vhacdVHACD.h
@@ -0,0 +1,383 @@
+/* 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 <OpenCL/cl.h>
+#else
+#include <CL/cl.h>
+#endif
+#endif //OPENCL_FOUND
+
+#include "vhacdMutex.h"
+#include "vhacdVolume.h"
+#include "vhacdRaycastMesh.h"
+#include <vector>
+
+typedef std::vector< VHACD::IVHACD::Constraint > ConstraintVector;
+
+#define USE_THREAD 1
+#define OCL_MIN_NUM_PRIMITIVES 4096
+#define CH_APP_MIN_NUM_PRIMITIVES 64000
+namespace VHACD {
+class VHACD : public IVHACD {
+public:
+ //! Constructor.
+ VHACD()
+ {
+#if USE_THREAD == 1 && _OPENMP
+ m_ompNumProcessors = 2 * omp_get_num_procs();
+ omp_set_num_threads(m_ompNumProcessors);
+#else //USE_THREAD == 1 && _OPENMP
+ m_ompNumProcessors = 1;
+#endif //USE_THREAD == 1 && _OPENMP
+#ifdef CL_VERSION_1_1
+ m_oclWorkGroupSize = 0;
+ m_oclDevice = 0;
+ m_oclQueue = 0;
+ m_oclKernelComputePartialVolumes = 0;
+ m_oclKernelComputeSum = 0;
+#endif //CL_VERSION_1_1
+ Init();
+ }
+ //! Destructor.
+ ~VHACD(void)
+ {
+ }
+ uint32_t GetNConvexHulls() const
+ {
+ return (uint32_t)m_convexHulls.Size();
+ }
+ void Cancel()
+ {
+ SetCancel(true);
+ }
+ void GetConvexHull(const uint32_t index, ConvexHull& ch) const
+ {
+ Mesh* mesh = m_convexHulls[index];
+ ch.m_nPoints = (uint32_t)mesh->GetNPoints();
+ ch.m_nTriangles = (uint32_t)mesh->GetNTriangles();
+ ch.m_points = mesh->GetPoints();
+ ch.m_triangles = (uint32_t *)mesh->GetTriangles();
+ ch.m_volume = mesh->ComputeVolume();
+ Vec3<double> &center = mesh->ComputeCenter();
+ ch.m_center[0] = center.X();
+ ch.m_center[1] = center.Y();
+ ch.m_center[2] = center.Z();
+ }
+ void Clean(void)
+ {
+ if (mRaycastMesh)
+ {
+ mRaycastMesh->release();
+ mRaycastMesh = nullptr;
+ }
+ delete m_volume;
+ delete m_pset;
+ size_t nCH = m_convexHulls.Size();
+ for (size_t p = 0; p < nCH; ++p) {
+ delete m_convexHulls[p];
+ }
+ m_convexHulls.Clear();
+ Init();
+ }
+ void Release(void)
+ {
+ delete this;
+ }
+ bool Compute(const float* const points,
+ const uint32_t nPoints,
+ const uint32_t* const triangles,
+ const uint32_t nTriangles,
+ const Parameters& params);
+ bool Compute(const double* const points,
+ const uint32_t nPoints,
+ const uint32_t* const triangles,
+ const uint32_t nTriangles,
+ const Parameters& params);
+ bool OCLInit(void* const oclDevice,
+ IUserLogger* const logger = 0);
+ bool OCLRelease(IUserLogger* const logger = 0);
+
+ virtual bool ComputeCenterOfMass(double centerOfMass[3]) const;
+
+ // Will analyze the HACD results and compute the constraints solutions.
+ // It will analyze the point at which any two convex hulls touch each other and
+ // return the total number of constraint pairs found
+ virtual uint32_t ComputeConstraints(void);
+
+ // Returns a pointer to the constraint index; null if the index is not valid or
+ // the user did not previously call 'ComputeConstraints'
+ virtual const Constraint *GetConstraint(uint32_t index) const;
+
+private:
+ void SetCancel(bool cancel)
+ {
+ m_cancelMutex.Lock();
+ m_cancel = cancel;
+ m_cancelMutex.Unlock();
+ }
+ bool GetCancel()
+ {
+
+ m_cancelMutex.Lock();
+ bool cancel = m_cancel;
+ m_cancelMutex.Unlock();
+ return cancel;
+ }
+ void Update(const double stageProgress,
+ const double operationProgress,
+ const Parameters& params)
+ {
+ m_stageProgress = stageProgress;
+ m_operationProgress = operationProgress;
+ if (params.m_callback) {
+ params.m_callback->Update(m_overallProgress,
+ m_stageProgress,
+ m_operationProgress,
+ m_stage.c_str(),
+ m_operation.c_str());
+ }
+ }
+ void Init()
+ {
+ if (mRaycastMesh)
+ {
+ mRaycastMesh->release();
+ mRaycastMesh = nullptr;
+ }
+ memset(m_rot, 0, sizeof(double) * 9);
+ m_dim = 64;
+ m_volume = 0;
+ m_volumeCH0 = 0.0;
+ m_pset = 0;
+ m_overallProgress = 0.0;
+ m_stageProgress = 0.0;
+ m_operationProgress = 0.0;
+ m_stage = "";
+ m_operation = "";
+ m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0.0;
+ m_rot[0][0] = m_rot[1][1] = m_rot[2][2] = 1.0;
+ SetCancel(false);
+ }
+ void ComputePrimitiveSet(const Parameters& params);
+ void ComputeACD(const Parameters& params);
+ void MergeConvexHulls(const Parameters& params);
+ void SimplifyConvexHull(Mesh* const ch, const size_t nvertices, const double minVolume);
+ void SimplifyConvexHulls(const Parameters& params);
+ void ComputeBestClippingPlane(const PrimitiveSet* inputPSet,
+ const double volume,
+ const SArray<Plane>& planes,
+ const Vec3<double>& preferredCuttingDirection,
+ const double w,
+ const double alpha,
+ const double beta,
+ const int32_t convexhullDownsampling,
+ const double progress0,
+ const double progress1,
+ Plane& bestPlane,
+ double& minConcavity,
+ const Parameters& params);
+ template <class T>
+ void AlignMesh(const T* const points,
+ const uint32_t stridePoints,
+ const uint32_t nPoints,
+ const int32_t* const triangles,
+ const uint32_t strideTriangles,
+ const uint32_t nTriangles,
+ const Parameters& params)
+ {
+ if (GetCancel() || !params.m_pca) {
+ return;
+ }
+ m_timer.Tic();
+
+ m_stage = "Align mesh";
+ m_operation = "Voxelization";
+
+ std::ostringstream msg;
+ if (params.m_logger) {
+ msg << "+ " << m_stage << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+
+ Update(0.0, 0.0, params);
+ if (GetCancel()) {
+ return;
+ }
+ m_dim = (size_t)(pow((double)params.m_resolution, 1.0 / 3.0) + 0.5);
+ Volume volume;
+ volume.Voxelize(points, stridePoints, nPoints,
+ triangles, strideTriangles, nTriangles,
+ m_dim, m_barycenter, m_rot);
+ size_t n = volume.GetNPrimitivesOnSurf() + volume.GetNPrimitivesInsideSurf();
+ Update(50.0, 100.0, params);
+
+ if (params.m_logger) {
+ msg.str("");
+ msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+ if (GetCancel()) {
+ return;
+ }
+ m_operation = "PCA";
+ Update(50.0, 0.0, params);
+ volume.AlignToPrincipalAxes(m_rot);
+ m_overallProgress = 1.0;
+ Update(100.0, 100.0, params);
+
+ m_timer.Toc();
+ if (params.m_logger) {
+ msg.str("");
+ msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+ }
+ template <class T>
+ void VoxelizeMesh(const T* const points,
+ const uint32_t stridePoints,
+ const uint32_t nPoints,
+ const int32_t* const triangles,
+ const uint32_t strideTriangles,
+ const uint32_t nTriangles,
+ const Parameters& params)
+ {
+ if (GetCancel()) {
+ return;
+ }
+
+ m_timer.Tic();
+ m_stage = "Voxelization";
+
+ std::ostringstream msg;
+ if (params.m_logger) {
+ msg << "+ " << m_stage << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+
+ delete m_volume;
+ m_volume = 0;
+ int32_t iteration = 0;
+ const int32_t maxIteration = 5;
+ double progress = 0.0;
+ while (iteration++ < maxIteration && !m_cancel) {
+ msg.str("");
+ msg << "Iteration " << iteration;
+ m_operation = msg.str();
+
+ progress = iteration * 100.0 / maxIteration;
+ Update(progress, 0.0, params);
+
+ m_volume = new Volume;
+ m_volume->Voxelize(points, stridePoints, nPoints,
+ triangles, strideTriangles, nTriangles,
+ m_dim, m_barycenter, m_rot);
+
+ Update(progress, 100.0, params);
+
+ size_t n = m_volume->GetNPrimitivesOnSurf() + m_volume->GetNPrimitivesInsideSurf();
+ if (params.m_logger) {
+ msg.str("");
+ msg << "\t dim = " << m_dim << "\t-> " << n << " voxels" << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+
+ double a = pow((double)(params.m_resolution) / n, 0.33);
+ size_t dim_next = (size_t)(m_dim * a + 0.5);
+ if (n < params.m_resolution && iteration < maxIteration && m_volume->GetNPrimitivesOnSurf() < params.m_resolution / 8 && m_dim != dim_next) {
+ delete m_volume;
+ m_volume = 0;
+ m_dim = dim_next;
+ }
+ else {
+ break;
+ }
+ }
+ m_overallProgress = 10.0;
+ Update(100.0, 100.0, params);
+
+ m_timer.Toc();
+ if (params.m_logger) {
+ msg.str("");
+ msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+ }
+ template <class T>
+ bool ComputeACD(const T* const points,
+ const uint32_t nPoints,
+ const uint32_t* const triangles,
+ const uint32_t nTriangles,
+ const Parameters& params)
+ {
+ Init();
+ if (params.m_projectHullVertices)
+ {
+ mRaycastMesh = RaycastMesh::createRaycastMesh(nPoints, points, nTriangles, (const uint32_t *)triangles);
+ }
+ if (params.m_oclAcceleration) {
+ // build kernels
+ }
+ AlignMesh(points, 3, nPoints, (int32_t *)triangles, 3, nTriangles, params);
+ VoxelizeMesh(points, 3, nPoints, (int32_t *)triangles, 3, nTriangles, params);
+ ComputePrimitiveSet(params);
+ ComputeACD(params);
+ MergeConvexHulls(params);
+ SimplifyConvexHulls(params);
+ if (params.m_oclAcceleration) {
+ // Release kernels
+ }
+ if (GetCancel()) {
+ Clean();
+ return false;
+ }
+ return true;
+ }
+
+private:
+ RaycastMesh *mRaycastMesh{ nullptr };
+ SArray<Mesh*> 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<double> m_barycenter;
+ Timer m_timer;
+ size_t m_dim;
+ Volume* m_volume;
+ PrimitiveSet* m_pset;
+ Mutex m_cancelMutex;
+ bool m_cancel;
+ int32_t m_ompNumProcessors;
+#ifdef CL_VERSION_1_1
+ cl_device_id* m_oclDevice;
+ cl_context m_oclContext;
+ cl_program m_oclProgram;
+ cl_command_queue* m_oclQueue;
+ cl_kernel* m_oclKernelComputePartialVolumes;
+ cl_kernel* m_oclKernelComputeSum;
+ size_t m_oclWorkGroupSize;
+#endif //CL_VERSION_1_1
+ ConstraintVector mConstraints;
+};
+}
+#endif // VHACD_VHACD_H
diff --git a/sdk/extensions/authoring/source/VHACD/inc/vhacdVector.h b/sdk/extensions/authoring/source/VHACD/inc/vhacdVector.h
new file mode 100644
index 0000000..efcfcf6
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/inc/vhacdVector.h
@@ -0,0 +1,168 @@
+/* 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 <iostream>
+#include <math.h>
+
+namespace VHACD {
+//! Vector dim 3.
+template <typename T>
+class Vec3 {
+public:
+ T& operator[](size_t i) { return m_data[i]; }
+ const T& operator[](size_t i) const { return m_data[i]; }
+ T& X();
+ T& Y();
+ T& Z();
+ const T& X() const;
+ const T& Y() const;
+ const T& Z() const;
+ void Normalize();
+ T GetNorm() const;
+ void operator=(const Vec3& rhs);
+ void operator+=(const Vec3& rhs);
+ void operator-=(const Vec3& rhs);
+ void operator-=(T a);
+ void operator+=(T a);
+ void operator/=(T a);
+ void operator*=(T a);
+ Vec3 operator^(const Vec3& rhs) const;
+ T operator*(const Vec3& rhs) const;
+ Vec3 operator+(const Vec3& rhs) const;
+ Vec3 operator-(const Vec3& rhs) const;
+ Vec3 operator-() const;
+ Vec3 operator*(T rhs) const;
+ Vec3 operator/(T rhs) const;
+ bool operator<(const Vec3& rhs) const;
+ bool operator>(const Vec3& rhs) const;
+ Vec3();
+ Vec3(T a);
+ Vec3(T x, T y, T z);
+ Vec3(const Vec3& rhs);
+ /*virtual*/ ~Vec3(void);
+
+ // Compute the center of this bounding box and return the diagonal length
+ T GetCenter(const Vec3 &bmin, const Vec3 &bmax)
+ {
+ X() = (bmin.X() + bmax.X())*0.5;
+ Y() = (bmin.Y() + bmax.Y())*0.5;
+ Z() = (bmin.Z() + bmax.Z())*0.5;
+ T dx = bmax.X() - bmin.X();
+ T dy = bmax.Y() - bmin.Y();
+ T dz = bmax.Z() - bmin.Z();
+ T diagonal = T(sqrt(dx*dx + dy*dy + dz*dz));
+ return diagonal;
+ }
+
+ // Update the min/max values relative to this point
+ void UpdateMinMax(Vec3 &bmin,Vec3 &bmax) const
+ {
+ if (X() < bmin.X())
+ {
+ bmin.X() = X();
+ }
+ if (Y() < bmin.Y())
+ {
+ bmin.Y() = Y();
+ }
+ if (Z() < bmin.Z())
+ {
+ bmin.Z() = Z();
+ }
+ if (X() > bmax.X())
+ {
+ bmax.X() = X();
+ }
+ if (X() > bmax.X())
+ {
+ bmax.X() = X();
+ }
+ if (Y() > bmax.Y())
+ {
+ bmax.Y() = Y();
+ }
+ if (Z() > bmax.Z())
+ {
+ bmax.Z() = Z();
+ }
+ }
+
+ // Returns the squared distance between these two points
+ T GetDistanceSquared(const Vec3 &p) const
+ {
+ T dx = X() - p.X();
+ T dy = Y() - p.Y();
+ T dz = Z() - p.Z();
+ return dx*dx + dy*dy + dz*dz;
+ }
+
+ T GetDistance(const Vec3 &p) const
+ {
+ return sqrt(GetDistanceSquared(p));
+ }
+
+ // Returns the raw vector data as a pointer
+ T* GetData(void)
+ {
+ return m_data;
+ }
+private:
+ T m_data[3];
+};
+//! Vector dim 2.
+template <typename T>
+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 <typename T>
+const bool Colinear(const Vec3<T>& a, const Vec3<T>& b, const Vec3<T>& c);
+template <typename T>
+const T ComputeVolume4(const Vec3<T>& a, const Vec3<T>& b, const Vec3<T>& c, const Vec3<T>& d);
+}
+#include "vhacdVector.inl" // template implementation
+#endif \ No newline at end of file
diff --git a/sdk/extensions/authoring/source/VHACD/inc/vhacdVolume.h b/sdk/extensions/authoring/source/VHACD/inc/vhacdVolume.h
new file mode 100644
index 0000000..8c47fa1
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/inc/vhacdVolume.h
@@ -0,0 +1,430 @@
+/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
+ All rights reserved.
+
+
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#pragma once
+#ifndef VHACD_VOLUME_H
+#define VHACD_VOLUME_H
+#include "vhacdMesh.h"
+#include "vhacdVector.h"
+#include <assert.h>
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4456 4701)
+#endif
+
+namespace VHACD {
+
+enum VOXEL_VALUE {
+ PRIMITIVE_UNDEFINED = 0,
+ PRIMITIVE_OUTSIDE_SURFACE = 1,
+ PRIMITIVE_INSIDE_SURFACE = 2,
+ PRIMITIVE_ON_SURFACE = 3
+};
+
+struct Voxel {
+public:
+ short m_coord[3];
+ short m_data;
+};
+
+class PrimitiveSet {
+public:
+ virtual ~PrimitiveSet(){};
+ virtual PrimitiveSet* Create() const = 0;
+ virtual const size_t GetNPrimitives() const = 0;
+ virtual const size_t GetNPrimitivesOnSurf() const = 0;
+ virtual const size_t GetNPrimitivesInsideSurf() const = 0;
+ virtual const double GetEigenValue(AXIS axis) const = 0;
+ virtual const double ComputeMaxVolumeError() const = 0;
+ virtual const double ComputeVolume() const = 0;
+ virtual void Clip(const Plane& plane, PrimitiveSet* const positivePart,
+ PrimitiveSet* const negativePart) const = 0;
+ virtual void Intersect(const Plane& plane, SArray<Vec3<double> >* const positivePts,
+ SArray<Vec3<double> >* const negativePts, const size_t sampling) const = 0;
+ virtual void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh,
+ SArray<Vec3<double> >* 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<short>& GetMinBBVoxels() const { return m_minBBVoxels; }
+ const Vec3<short>& GetMaxBBVoxels() const { return m_maxBBVoxels; }
+ const Vec3<double>& GetMinBB() const { return m_minBB; }
+ const double& GetScale() const { return m_scale; }
+ const double& GetUnitVolume() const { return m_unitVolume; }
+ Vec3<double> GetPoint(Vec3<short> voxel) const
+ {
+ return Vec3<double>(voxel[0] * m_scale + m_minBB[0],
+ voxel[1] * m_scale + m_minBB[1],
+ voxel[2] * m_scale + m_minBB[2]);
+ }
+ Vec3<double> GetPoint(const Voxel& voxel) const
+ {
+ return Vec3<double>(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<double> GetPoint(Vec3<double> voxel) const
+ {
+ return Vec3<double>(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<double>* 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<Vec3<double> >* const positivePts,
+ SArray<Vec3<double> >* const negativePts, const size_t sampling) const;
+ void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh,
+ SArray<Vec3<double> >* 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<double> m_minBB;
+ double m_scale;
+ SArray<Voxel, 8> m_voxels;
+ double m_unitVolume;
+ Vec3<double> m_minBBPts;
+ Vec3<double> m_maxBBPts;
+ Vec3<short> m_minBBVoxels;
+ Vec3<short> m_maxBBVoxels;
+ Vec3<short> m_barycenter;
+ double m_Q[3][3];
+ double m_D[3][3];
+ Vec3<double> m_barycenterPCA;
+};
+
+struct Tetrahedron {
+public:
+ Vec3<double> 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<double>& GetMinBB() const { return m_minBB; }
+ const Vec3<double>& GetMaxBB() const { return m_maxBB; }
+ const Vec3<double>& 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<Vec3<double> >* const positivePts,
+ SArray<Vec3<double> >* const negativePts, const size_t sampling) const;
+ void ComputeExteriorPoints(const Plane& plane, const Mesh& mesh,
+ SArray<Vec3<double> >* 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<double> (&pts)[10], const int32_t nPts);
+
+ size_t m_numTetrahedraOnSurface;
+ size_t m_numTetrahedraInsideSurface;
+ double m_scale;
+ Vec3<double> m_minBB;
+ Vec3<double> m_maxBB;
+ Vec3<double> m_barycenter;
+ SArray<Tetrahedron, 8> m_tetrahedra;
+ double m_Q[3][3];
+ double m_D[3][3];
+};
+
+//!
+class Volume {
+public:
+ //! Destructor.
+ ~Volume(void);
+
+ //! Constructor.
+ Volume();
+
+ //! Voxelize
+ template <class T>
+ void Voxelize(const T* const points, const uint32_t stridePoints, const uint32_t nPoints,
+ const int32_t* const triangles, const uint32_t strideTriangles, const uint32_t nTriangles,
+ const size_t dim, const Vec3<double>& 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 <class T>
+ void ComputeBB(const T* const points, const uint32_t stridePoints, const uint32_t nPoints,
+ const Vec3<double>& barycenter, const double (&rot)[3][3]);
+ void Allocate();
+ void Free();
+
+ Vec3<double> m_minBB;
+ Vec3<double> m_maxBB;
+ double m_scale;
+ size_t m_dim[3]; //>! dim
+ size_t m_numVoxelsOnSurface;
+ size_t m_numVoxelsInsideSurface;
+ size_t m_numVoxelsOutsideSurface;
+ unsigned char* m_data;
+};
+int32_t TriBoxOverlap(const Vec3<double>& boxcenter, const Vec3<double>& boxhalfsize, const Vec3<double>& triver0,
+ const Vec3<double>& triver1, const Vec3<double>& triver2);
+template <class T>
+inline void ComputeAlignedPoint(const T* const points, const uint32_t idx, const Vec3<double>& barycenter,
+ const double (&rot)[3][3], Vec3<double>& pt){};
+template <>
+inline void ComputeAlignedPoint<float>(const float* const points, const uint32_t idx, const Vec3<double>& barycenter, const double (&rot)[3][3], Vec3<double>& 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<double>(const double* const points, const uint32_t idx, const Vec3<double>& barycenter, const double (&rot)[3][3], Vec3<double>& 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 <class T>
+void Volume::ComputeBB(const T* const points, const uint32_t stridePoints, const uint32_t nPoints,
+ const Vec3<double>& barycenter, const double (&rot)[3][3])
+{
+ Vec3<double> pt;
+ ComputeAlignedPoint(points, 0, barycenter, rot, pt);
+ m_maxBB = pt;
+ m_minBB = pt;
+ for (uint32_t v = 1; v < nPoints; ++v) {
+ ComputeAlignedPoint(points, v * stridePoints, barycenter, rot, pt);
+ for (int32_t i = 0; i < 3; ++i) {
+ if (pt[i] < m_minBB[i])
+ m_minBB[i] = pt[i];
+ else if (pt[i] > m_maxBB[i])
+ m_maxBB[i] = pt[i];
+ }
+ }
+}
+template <class T>
+void Volume::Voxelize(const T* const points, const uint32_t stridePoints, const uint32_t nPoints,
+ const int32_t* const triangles, const uint32_t strideTriangles, const uint32_t nTriangles,
+ const size_t dim, const Vec3<double>& 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<size_t>(dim * d[1] / d[0]);
+ m_dim[2] = 2 + static_cast<size_t>(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<size_t>(dim * d[0] / d[1]);
+ m_dim[2] = 2 + static_cast<size_t>(dim * d[2] / d[1]);
+ }
+ else {
+ r = d[2];
+ m_dim[2] = dim;
+ m_dim[0] = 2 + static_cast<size_t>(dim * d[0] / d[2]);
+ m_dim[1] = 2 + static_cast<size_t>(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<double> p[3];
+ size_t i, j, k;
+ size_t i0, j0, k0;
+ size_t i1, j1, k1;
+ Vec3<double> boxcenter;
+ Vec3<double> pt;
+ const Vec3<double> boxhalfsize(0.5, 0.5, 0.5);
+ for (size_t t = 0, ti = 0; t < nTriangles; ++t, ti += strideTriangles) {
+ Vec3<int32_t> tri(triangles[ti + 0],
+ triangles[ti + 1],
+ triangles[ti + 2]);
+ for (int32_t c = 0; c < 3; ++c) {
+ ComputeAlignedPoint(points, tri[c] * stridePoints, barycenter, rot, pt);
+ p[c][0] = (pt[0] - m_minBB[0]) * invScale;
+ p[c][1] = (pt[1] - m_minBB[1]) * invScale;
+ p[c][2] = (pt[2] - m_minBB[2]) * invScale;
+ i = static_cast<size_t>(p[c][0] + 0.5);
+ j = static_cast<size_t>(p[c][1] + 0.5);
+ k = static_cast<size_t>(p[c][2] + 0.5);
+ assert(i < m_dim[0] && i >= 0 && j < m_dim[1] && j >= 0 && k < m_dim[2] && k >= 0);
+
+ if (c == 0) {
+ i0 = i1 = i;
+ j0 = j1 = j;
+ k0 = k1 = k;
+ }
+ else {
+ if (i < i0)
+ i0 = i;
+ if (j < j0)
+ j0 = j;
+ if (k < k0)
+ k0 = k;
+ if (i > i1)
+ i1 = i;
+ if (j > j1)
+ j1 = j;
+ if (k > k1)
+ k1 = k;
+ }
+ }
+ if (i0 > 0)
+ --i0;
+ if (j0 > 0)
+ --j0;
+ if (k0 > 0)
+ --k0;
+ if (i1 < m_dim[0])
+ ++i1;
+ if (j1 < m_dim[1])
+ ++j1;
+ if (k1 < m_dim[2])
+ ++k1;
+ for (size_t i = i0; i < i1; ++i) {
+ boxcenter[0] = (double)i;
+ for (size_t j = j0; j < j1; ++j) {
+ boxcenter[1] = (double)j;
+ for (size_t k = k0; k < k1; ++k) {
+ boxcenter[2] = (double)k;
+ int32_t res = TriBoxOverlap(boxcenter, boxhalfsize, p[0], p[1], p[2]);
+ unsigned char& value = GetVoxel(i, j, k);
+ if (res == 1 && value == PRIMITIVE_UNDEFINED) {
+ value = PRIMITIVE_ON_SURFACE;
+ ++m_numVoxelsOnSurface;
+ }
+ }
+ }
+ }
+ }
+ FillOutsideSurface(0, 0, 0, m_dim[0], m_dim[1], 1);
+ FillOutsideSurface(0, 0, m_dim[2] - 1, m_dim[0], m_dim[1], m_dim[2]);
+ FillOutsideSurface(0, 0, 0, m_dim[0], 1, m_dim[2]);
+ FillOutsideSurface(0, m_dim[1] - 1, 0, m_dim[0], m_dim[1], m_dim[2]);
+ FillOutsideSurface(0, 0, 0, 1, m_dim[1], m_dim[2]);
+ FillOutsideSurface(m_dim[0] - 1, 0, 0, m_dim[0], m_dim[1], m_dim[2]);
+ FillInsideSurface();
+}
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+
+#endif // VHACD_VOLUME_H
diff --git a/sdk/extensions/authoring/source/VHACD/public/VHACD.h b/sdk/extensions/authoring/source/VHACD/public/VHACD.h
new file mode 100644
index 0000000..9b3f496
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/public/VHACD.h
@@ -0,0 +1,170 @@
+/* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
+ All rights reserved.
+
+
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#pragma once
+#ifndef VHACD_H
+#define VHACD_H
+
+#define VHACD_VERSION_MAJOR 2
+#define VHACD_VERSION_MINOR 3
+
+// Changes for version 2.3
+//
+// m_gamma : Has been removed. This used to control the error metric to merge convex hulls. Now it uses the 'm_maxConvexHulls' value instead.
+// m_maxConvexHulls : This is the maximum number of convex hulls to produce from the merge operation; replaces 'm_gamma'.
+//
+// Note that decomposition depth is no longer a user provided value. It is now derived from the
+// maximum number of hulls requested.
+//
+// As a convenience to the user, each convex hull produced now includes the volume of the hull as well as it's center.
+//
+// This version supports a convenience method to automatically make V-HACD run asynchronously in a background thread.
+// To get a fully asynchronous version, call 'CreateVHACD_ASYNC' instead of 'CreateVHACD'. You get the same interface however,
+// now when computing convex hulls, it is no longer a blocking operation. All callback messages are still returned
+// in the application's thread so you don't need to worry about mutex locks or anything in that case.
+// To tell if the operation is complete, the application should call 'IsReady'. This will return true if
+// the last approximation operation is complete and will dispatch any pending messages.
+// If you call 'Compute' while a previous operation was still running, it will automatically cancel the last request
+// and begin a new one. To cancel a currently running approximation just call 'Cancel'.
+#include <stdint.h>
+
+namespace VHACD {
+class IVHACD {
+public:
+ class IUserCallback {
+ public:
+ virtual ~IUserCallback(){};
+ virtual void Update(const double overallProgress,
+ const double stageProgress,
+ const double operationProgress,
+ const char* const stage,
+ const char* const operation)
+ = 0;
+ };
+
+ class IUserLogger {
+ public:
+ virtual ~IUserLogger(){};
+ virtual void Log(const char* const msg) = 0;
+ };
+
+ class ConvexHull {
+ public:
+ double* m_points;
+ uint32_t* m_triangles;
+ uint32_t m_nPoints;
+ uint32_t m_nTriangles;
+ double m_volume;
+ double m_center[3];
+ };
+
+ class Parameters {
+ public:
+ Parameters(void) { Init(); }
+ void Init(void)
+ {
+ m_resolution = 100000;
+ m_concavity = 0.001;
+ m_planeDownsampling = 4;
+ m_convexhullDownsampling = 4;
+ m_alpha = 0.05;
+ m_beta = 0.05;
+ m_pca = 0;
+ m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based
+ m_maxNumVerticesPerCH = 64;
+ m_minVolumePerCH = 0.0001;
+ m_callback = 0;
+ m_logger = 0;
+ m_convexhullApproximation = true;
+ m_oclAcceleration = true;
+ m_maxConvexHulls = 1024;
+ m_projectHullVertices = true; // This will project the output convex hull vertices onto the original source mesh to increase the floating point accuracy of the results
+ }
+ double m_concavity;
+ double m_alpha;
+ double m_beta;
+ double m_minVolumePerCH;
+ IUserCallback* m_callback;
+ IUserLogger* m_logger;
+ uint32_t m_resolution;
+ uint32_t m_maxNumVerticesPerCH;
+ uint32_t m_planeDownsampling;
+ uint32_t m_convexhullDownsampling;
+ uint32_t m_pca;
+ uint32_t m_mode;
+ uint32_t m_convexhullApproximation;
+ uint32_t m_oclAcceleration;
+ uint32_t m_maxConvexHulls;
+ bool m_projectHullVertices;
+ };
+
+ class Constraint
+ {
+ public:
+ uint32_t mHullA; // Convex Hull A index
+ uint32_t mHullB; // Convex Hull B index
+ double mConstraintPoint[3]; // The point of intersection between the two convex hulls
+ };
+
+ virtual void Cancel() = 0;
+ virtual bool Compute(const float* const points,
+ const uint32_t countPoints,
+ const uint32_t* const triangles,
+ const uint32_t countTriangles,
+ const Parameters& params)
+ = 0;
+ virtual bool Compute(const double* const points,
+ const uint32_t countPoints,
+ const uint32_t* const triangles,
+ const uint32_t countTriangles,
+ const Parameters& params)
+ = 0;
+ virtual uint32_t GetNConvexHulls() const = 0;
+ virtual void GetConvexHull(const uint32_t index, ConvexHull& ch) const = 0;
+ virtual void Clean(void) = 0; // release internally allocated memory
+ virtual void Release(void) = 0; // release IVHACD
+ virtual bool OCLInit(void* const oclDevice,
+ IUserLogger* const logger = 0)
+ = 0;
+ virtual bool OCLRelease(IUserLogger* const logger = 0) = 0;
+
+ // Will compute the center of mass of the convex hull decomposition results and return it
+ // in 'centerOfMass'. Returns false if the center of mass could not be computed.
+ virtual bool ComputeCenterOfMass(double centerOfMass[3]) const = 0;
+
+ // Will analyze the HACD results and compute the constraints solutions.
+ // It will analyze the point at which any two convex hulls touch each other and
+ // return the total number of constraint pairs found
+ virtual uint32_t ComputeConstraints(void) = 0;
+
+ // Returns a pointer to the constraint index; null if the index is not valid or
+ // the user did not previously call 'ComputeConstraints'
+ virtual const Constraint *GetConstraint(uint32_t index) const = 0;
+
+ // In synchronous mode (non-multi-threaded) the state is always 'ready'
+ // In asynchronous mode, this returns true if the background thread is not still actively computing
+ // a new solution. In an asynchronous config the 'IsReady' call will report any update or log
+ // messages in the caller's current thread.
+ virtual bool IsReady(void) const
+ {
+ return true;
+ }
+
+protected:
+ virtual ~IVHACD(void) {}
+};
+IVHACD* CreateVHACD(void);
+IVHACD* CreateVHACD_ASYNC(void);
+}
+#endif // VHACD_H
diff --git a/sdk/extensions/authoring/source/VHACD/src/FloatMath.cpp b/sdk/extensions/authoring/source/VHACD/src/FloatMath.cpp
new file mode 100644
index 0000000..ecc985e
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/src/FloatMath.cpp
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+#include <float.h>
+#include "FloatMath.h"
+#include <vector>
+#include <malloc.h>
+
+#define REAL float
+
+#include "FloatMath.inl"
+
+#undef REAL
+#define REAL double
+
+#include "FloatMath.inl"
diff --git a/sdk/extensions/authoring/source/VHACD/src/VHACD-ASYNC.cpp b/sdk/extensions/authoring/source/VHACD/src/VHACD-ASYNC.cpp
new file mode 100644
index 0000000..ea01ab5
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/src/VHACD-ASYNC.cpp
@@ -0,0 +1,360 @@
+#include "../public/VHACD.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <thread>
+#include <atomic>
+#include <mutex>
+#include <string>
+#include <float.h>
+
+#define ENABLE_ASYNC 1
+
+#define HACD_ALLOC(x) malloc(x)
+#define HACD_FREE(x) free(x)
+#define HACD_ASSERT(x) assert(x)
+
+namespace VHACD
+{
+
+class MyHACD_API : public VHACD::IVHACD, public VHACD::IVHACD::IUserCallback, VHACD::IVHACD::IUserLogger
+{
+public:
+ MyHACD_API(void)
+ {
+ mVHACD = VHACD::CreateVHACD();
+ }
+
+ virtual ~MyHACD_API(void)
+ {
+ releaseHACD();
+ Cancel();
+ mVHACD->Release();
+ }
+
+
+ virtual bool Compute(const double* const _points,
+ const uint32_t countPoints,
+ const uint32_t* const _triangles,
+ const uint32_t countTriangles,
+ const Parameters& _desc) final
+ {
+#if ENABLE_ASYNC
+ Cancel(); // if we previously had a solution running; cancel it.
+ releaseHACD();
+
+ // We need to copy the input vertices and triangles into our own buffers so we can operate
+ // on them safely from the background thread.
+ mVertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3);
+ mIndices = (uint32_t *)HACD_ALLOC(sizeof(uint32_t)*countTriangles * 3);
+ memcpy(mVertices, _points, sizeof(double)*countPoints * 3);
+ memcpy(mIndices, _triangles, sizeof(uint32_t)*countTriangles * 3);
+ mRunning = true;
+ mThread = new std::thread([this, countPoints, countTriangles, _desc]()
+ {
+ ComputeNow(mVertices, countPoints, mIndices, countTriangles, _desc);
+ mRunning = false;
+ });
+#else
+ releaseHACD();
+ ComputeNow(_points, countPoints, _triangles, countTriangles, _desc);
+#endif
+ return true;
+ }
+
+ bool ComputeNow(const double* const points,
+ const uint32_t countPoints,
+ const uint32_t* const triangles,
+ const uint32_t countTriangles,
+ const Parameters& _desc)
+ {
+ uint32_t ret = 0;
+
+ mHullCount = 0;
+ mCallback = _desc.m_callback;
+ mLogger = _desc.m_logger;
+
+ IVHACD::Parameters desc = _desc;
+ // Set our intercepting callback interfaces if non-null
+ desc.m_callback = desc.m_callback ? this : nullptr;
+ desc.m_logger = desc.m_logger ? this : nullptr;
+
+ if ( countPoints )
+ {
+ bool ok = mVHACD->Compute(points, countPoints, triangles, countTriangles, desc);
+ if (ok)
+ {
+ ret = mVHACD->GetNConvexHulls();
+ mHulls = new IVHACD::ConvexHull[ret];
+ for (uint32_t i = 0; i < ret; i++)
+ {
+ VHACD::IVHACD::ConvexHull vhull;
+ mVHACD->GetConvexHull(i, vhull);
+ VHACD::IVHACD::ConvexHull h;
+ h.m_nPoints = vhull.m_nPoints;
+ h.m_points = (double *)HACD_ALLOC(sizeof(double) * 3 * h.m_nPoints);
+ memcpy(h.m_points, vhull.m_points, sizeof(double) * 3 * h.m_nPoints);
+ h.m_nTriangles = vhull.m_nTriangles;
+ h.m_triangles = (uint32_t *)HACD_ALLOC(sizeof(uint32_t) * 3 * h.m_nTriangles);
+ memcpy(h.m_triangles, vhull.m_triangles, sizeof(uint32_t) * 3 * h.m_nTriangles);
+ h.m_volume = vhull.m_volume;
+ h.m_center[0] = vhull.m_center[0];
+ h.m_center[1] = vhull.m_center[1];
+ h.m_center[2] = vhull.m_center[2];
+ mHulls[i] = h;
+ if (mCancel)
+ {
+ ret = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ mHullCount = ret;
+ return ret ? true : false;
+ }
+
+ void releaseHull(VHACD::IVHACD::ConvexHull &h)
+ {
+ HACD_FREE((void *)h.m_triangles);
+ HACD_FREE((void *)h.m_points);
+ h.m_triangles = nullptr;
+ h.m_points = nullptr;
+ }
+
+ virtual void GetConvexHull(const uint32_t index, VHACD::IVHACD::ConvexHull& ch) const final
+ {
+ if ( index < mHullCount )
+ {
+ ch = mHulls[index];
+ }
+ }
+
+ void releaseHACD(void) // release memory associated with the last HACD request
+ {
+ for (uint32_t i=0; i<mHullCount; i++)
+ {
+ releaseHull(mHulls[i]);
+ }
+ delete[]mHulls;
+ mHulls = nullptr;
+ mHullCount = 0;
+ HACD_FREE(mVertices);
+ mVertices = nullptr;
+ HACD_FREE(mIndices);
+ mIndices = nullptr;
+ }
+
+
+ virtual void release(void) // release the HACD_API interface
+ {
+ delete this;
+ }
+
+ virtual uint32_t getHullCount(void)
+ {
+ return mHullCount;
+ }
+
+ virtual void Cancel() final
+ {
+ if (mRunning)
+ {
+ mVHACD->Cancel(); // Set the cancel signal to the base VHACD
+ }
+ if (mThread)
+ {
+ mThread->join(); // Wait for the thread to fully exit before we delete the instance
+ delete mThread;
+ mThread = nullptr;
+ Log("Convex Decomposition thread canceled\n");
+ }
+ mCancel = false; // clear the cancel semaphore
+ }
+
+ virtual bool Compute(const float* const points,
+ const uint32_t countPoints,
+ const uint32_t* const triangles,
+ const uint32_t countTriangles,
+ const Parameters& params) final
+ {
+
+ double *vertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3);
+ const float *source = points;
+ double *dest = vertices;
+ for (uint32_t i = 0; i < countPoints; i++)
+ {
+ dest[0] = source[0];
+ dest[1] = source[1];
+ dest[2] = source[2];
+ dest += 3;
+ source += 3;
+ }
+
+ bool ret = Compute(vertices, countPoints, triangles, countTriangles, params);
+ HACD_FREE(vertices);
+ return ret;
+ }
+
+ virtual uint32_t GetNConvexHulls() const final
+ {
+ processPendingMessages();
+ return mHullCount;
+ }
+
+ virtual void Clean(void) final // release internally allocated memory
+ {
+ Cancel();
+ releaseHACD();
+ mVHACD->Clean();
+ }
+
+ virtual void Release(void) final // release IVHACD
+ {
+ delete this;
+ }
+
+ virtual bool OCLInit(void* const oclDevice,
+ IVHACD::IUserLogger* const logger = 0) final
+ {
+ return mVHACD->OCLInit(oclDevice, logger);
+ }
+
+ virtual bool OCLRelease(IVHACD::IUserLogger* const logger = 0) final
+ {
+ return mVHACD->OCLRelease(logger);
+ }
+
+ virtual void Update(const double overallProgress,
+ const double stageProgress,
+ const double operationProgress,
+ const char* const stage,
+ const char* const operation) final
+ {
+ mMessageMutex.lock();
+ mHaveUpdateMessage = true;
+ mOverallProgress = overallProgress;
+ mStageProgress = stageProgress;
+ mOperationProgress = operationProgress;
+ mStage = std::string(stage);
+ mOperation = std::string(operation);
+ mMessageMutex.unlock();
+ }
+
+ virtual void Log(const char* const msg) final
+ {
+ mMessageMutex.lock();
+ mHaveLogMessage = true;
+ mMessage = std::string(msg);
+ mMessageMutex.unlock();
+ }
+
+ virtual bool IsReady(void) const final
+ {
+ processPendingMessages();
+ return !mRunning;
+ }
+
+ // As a convenience for the calling application we only send it update and log messages from it's own main
+ // thread. This reduces the complexity burden on the caller by making sure it only has to deal with log
+ // messages in it's main application thread.
+ void processPendingMessages(void) const
+ {
+ // If we have a new update message and the user has specified a callback we send the message and clear the semaphore
+ if (mHaveUpdateMessage && mCallback)
+ {
+ mMessageMutex.lock();
+ mCallback->Update(mOverallProgress, mStageProgress, mOperationProgress, mStage.c_str(), mOperation.c_str());
+ mHaveUpdateMessage = false;
+ mMessageMutex.unlock();
+ }
+ // If we have a new log message and the user has specified a callback we send the message and clear the semaphore
+ if (mHaveLogMessage && mLogger)
+ {
+ mMessageMutex.lock();
+ mLogger->Log(mMessage.c_str());
+ mHaveLogMessage = false;
+ mMessageMutex.unlock();
+ }
+ }
+
+ // Will compute the center of mass of the convex hull decomposition results and return it
+ // in 'centerOfMass'. Returns false if the center of mass could not be computed.
+ virtual bool ComputeCenterOfMass(double centerOfMass[3]) const
+ {
+ bool ret = false;
+
+ centerOfMass[0] = 0;
+ centerOfMass[1] = 0;
+ centerOfMass[2] = 0;
+
+ if (mVHACD && IsReady() )
+ {
+ ret = mVHACD->ComputeCenterOfMass(centerOfMass);
+ }
+ return ret;
+ }
+
+ // Will analyze the HACD results and compute the constraints solutions.
+ // It will analyze the point at which any two convex hulls touch each other and
+ // return the total number of constraint pairs found
+ virtual uint32_t ComputeConstraints(void) final
+ {
+ uint32_t ret = 0;
+ if (mVHACD && IsReady())
+ {
+ ret = mVHACD->ComputeConstraints();
+ }
+ return ret;
+ }
+
+ virtual const Constraint *GetConstraint(uint32_t index) const final
+ {
+ const Constraint * ret = nullptr;
+ if (mVHACD && IsReady())
+ {
+ ret = mVHACD->GetConstraint(index);
+ }
+ return ret;
+
+ }
+
+
+
+private:
+ double *mVertices{ nullptr };
+ uint32_t *mIndices{ nullptr };
+ std::atomic< uint32_t> mHullCount{ 0 };
+ VHACD::IVHACD::ConvexHull *mHulls{ nullptr };
+ VHACD::IVHACD::IUserCallback *mCallback{ nullptr };
+ VHACD::IVHACD::IUserLogger *mLogger{ nullptr };
+ VHACD::IVHACD *mVHACD{ nullptr };
+ std::thread *mThread{ nullptr };
+ std::atomic< bool > mRunning{ false };
+ std::atomic<bool> mCancel{ false };
+
+ // Thread safe caching mechanism for messages and update status.
+ // This is so that caller always gets messages in his own thread
+ // Member variables are marked as 'mutable' since the message dispatch function
+ // is called from const query methods.
+ mutable std::mutex mMessageMutex;
+ mutable std::atomic< bool > mHaveUpdateMessage{ false };
+ mutable std::atomic< bool > mHaveLogMessage{ false };
+ mutable double mOverallProgress{ 0 };
+ mutable double mStageProgress{ 0 };
+ mutable double mOperationProgress{ 0 };
+ mutable std::string mStage;
+ mutable std::string mOperation;
+ mutable std::string mMessage;
+};
+
+IVHACD* CreateVHACD_ASYNC(void)
+{
+ MyHACD_API *m = new MyHACD_API;
+ return static_cast<IVHACD *>(m);
+}
+
+
+}; // end of VHACD namespace
+
diff --git a/sdk/extensions/authoring/source/VHACD/src/VHACD.cpp b/sdk/extensions/authoring/source/VHACD/src/VHACD.cpp
new file mode 100644
index 0000000..613ef5a
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/src/VHACD.cpp
@@ -0,0 +1,1784 @@
+/* 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 <algorithm>
+#include <fstream>
+#include <iomanip>
+#include <limits>
+#include <sstream>
+#if _OPENMP
+#include <omp.h>
+#endif // _OPENMP
+
+#include "../public/VHACD.h"
+#include "btConvexHullComputer.h"
+#include "vhacdICHull.h"
+#include "vhacdMesh.h"
+#include "vhacdSArray.h"
+#include "vhacdTimer.h"
+#include "vhacdVHACD.h"
+#include "vhacdVector.h"
+#include "vhacdVolume.h"
+#include "FloatMath.h"
+
+// Internal debugging feature only
+#define DEBUG_VISUALIZE_CONSTRAINTS 0
+
+#if DEBUG_VISUALIZE_CONSTRAINTS
+#include "NvRenderDebug.h"
+extern RENDER_DEBUG::RenderDebug *gRenderDebug;
+#pragma warning(disable:4702)
+#endif
+
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define ABS(a) (((a) < 0) ? -(a) : (a))
+#define ZSGN(a) (((a) < 0) ? -1 : (a) > 0 ? 1 : 0)
+#define MAX_DOUBLE (1.79769e+308)
+
+#ifdef _MSC_VER
+#pragma warning(disable:4267 4100 4244 4456)
+#endif
+
+#ifdef USE_SSE
+#include <immintrin.h>
+
+const int32_t SIMD_WIDTH = 4;
+inline int32_t FindMinimumElement(const float* const d, float* const _, const int32_t n)
+{
+ // Min within vectors
+ __m128 min_i = _mm_set1_ps(-1.0f);
+ __m128 min_v = _mm_set1_ps(std::numeric_limits<float>::max());
+ for (int32_t i = 0; i <= n - SIMD_WIDTH; i += SIMD_WIDTH) {
+ const __m128 data = _mm_load_ps(&d[i]);
+ const __m128 pred = _mm_cmplt_ps(data, min_v);
+
+ min_i = _mm_blendv_ps(min_i, _mm_set1_ps(i), pred);
+ min_v = _mm_min_ps(data, min_v);
+ }
+
+ /* Min within vector */
+ const __m128 min1 = _mm_shuffle_ps(min_v, min_v, _MM_SHUFFLE(1, 0, 3, 2));
+ const __m128 min2 = _mm_min_ps(min_v, min1);
+ const __m128 min3 = _mm_shuffle_ps(min2, min2, _MM_SHUFFLE(0, 1, 0, 1));
+ const __m128 min4 = _mm_min_ps(min2, min3);
+ float min_d = _mm_cvtss_f32(min4);
+
+ // Min index
+ const int32_t min_idx = __builtin_ctz(_mm_movemask_ps(_mm_cmpeq_ps(min_v, min4)));
+ int32_t ret = min_i[min_idx] + min_idx;
+
+ // Trailing elements
+ for (int32_t i = (n & ~(SIMD_WIDTH - 1)); i < n; ++i) {
+ if (d[i] < min_d) {
+ min_d = d[i];
+ ret = i;
+ }
+ }
+
+ *m = min_d;
+ return ret;
+}
+
+inline int32_t FindMinimumElement(const float* const d, float* const m, const int32_t begin, const int32_t end)
+{
+ // Leading elements
+ int32_t min_i = -1;
+ float min_d = std::numeric_limits<float>::max();
+ const int32_t aligned = (begin & ~(SIMD_WIDTH - 1)) + ((begin & (SIMD_WIDTH - 1)) ? SIMD_WIDTH : 0);
+ for (int32_t i = begin; i < std::min(end, aligned); ++i) {
+ if (d[i] < min_d) {
+ min_d = d[i];
+ min_i = i;
+ }
+ }
+
+ // Middle and trailing elements
+ float r_m = std::numeric_limits<float>::max();
+ const int32_t n = end - aligned;
+ const int32_t r_i = (n > 0) ? FindMinimumElement(&d[aligned], &r_m, n) : 0;
+
+ // Pick the lowest
+ if (r_m < min_d) {
+ *m = r_m;
+ return r_i + aligned;
+ }
+ else {
+ *m = min_d;
+ return min_i;
+ }
+}
+#else
+inline int32_t FindMinimumElement(const float* const d, float* const m, const int32_t begin, const int32_t end)
+{
+ int32_t idx = -1;
+ float min = (std::numeric_limits<float>::max)();
+ for (size_t i = begin; i < size_t(end); ++i) {
+ if (d[i] < min) {
+ idx = i;
+ min = d[i];
+ }
+ }
+
+ *m = min;
+ return idx;
+}
+#endif
+
+//#define OCL_SOURCE_FROM_FILE
+#ifndef OCL_SOURCE_FROM_FILE
+const char* oclProgramSource = "\
+__kernel void ComputePartialVolumes(__global short4 * voxels, \
+ const int32_t numVoxels, \
+ const float4 plane, \
+ const float4 minBB, \
+ const float4 scale, \
+ __local uint4 * localPartialVolumes, \
+ __global uint4 * partialVolumes) \
+{ \
+ int32_t localId = get_local_id(0); \
+ int32_t groupSize = get_local_size(0); \
+ int32_t 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 (int32_t 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 int32_t dataSize, \
+ __local uint4 * partialSums) \
+{ \
+ int32_t globalId = get_global_id(0); \
+ int32_t localId = get_local_id(0); \
+ int32_t groupSize = get_local_size(0); \
+ int32_t i; \
+ if (globalId < dataSize) \
+ { \
+ partialSums[localId] = data[globalId]; \
+ } \
+ else \
+ { \
+ partialSums[localId] = (0, 0, 0, 0); \
+ } \
+ barrier(CLK_LOCAL_MEM_FENCE); \
+ for (i = groupSize >> 1; i > 0; i >>= 1) \
+ { \
+ if (localId < i) \
+ { \
+ partialSums[localId] += partialSums[localId + i]; \
+ } \
+ barrier(CLK_LOCAL_MEM_FENCE); \
+ } \
+ if (localId == 0) \
+ { \
+ data[get_group_id(0)] = partialSums[0]; \
+ } \
+}";
+#endif //OCL_SOURCE_FROM_FILE
+
+namespace VHACD {
+IVHACD* CreateVHACD(void)
+{
+ return new VHACD();
+}
+bool VHACD::OCLInit(void* const oclDevice, IUserLogger* const logger)
+{
+#ifdef CL_VERSION_1_1
+ m_oclDevice = (cl_device_id*)oclDevice;
+ cl_int error;
+ m_oclContext = clCreateContext(NULL, 1, m_oclDevice, NULL, NULL, &error);
+ if (error != CL_SUCCESS) {
+ if (logger) {
+ logger->Log("Couldn't create context\n");
+ }
+ return false;
+ }
+
+#ifdef OCL_SOURCE_FROM_FILE
+ std::string cl_files = OPENCL_CL_FILES;
+// read kernal from file
+#ifdef _WIN32
+ std::replace(cl_files.begin(), cl_files.end(), '/', '\\');
+#endif // _WIN32
+
+ FILE* program_handle = fopen(cl_files.c_str(), "rb");
+ fseek(program_handle, 0, SEEK_END);
+ size_t program_size = ftell(program_handle);
+ rewind(program_handle);
+ char* program_buffer = new char[program_size + 1];
+ program_buffer[program_size] = '\0';
+ fread(program_buffer, sizeof(char), program_size, program_handle);
+ fclose(program_handle);
+ // create program
+ m_oclProgram = clCreateProgramWithSource(m_oclContext, 1, (const char**)&program_buffer, &program_size, &error);
+ delete[] program_buffer;
+#else
+ size_t program_size = strlen(oclProgramSource);
+ m_oclProgram = clCreateProgramWithSource(m_oclContext, 1, (const char**)&oclProgramSource, &program_size, &error);
+#endif
+ if (error != CL_SUCCESS) {
+ if (logger) {
+ logger->Log("Couldn't create program\n");
+ }
+ return false;
+ }
+
+ /* Build program */
+ error = clBuildProgram(m_oclProgram, 1, m_oclDevice, "-cl-denorms-are-zero", NULL, NULL);
+ if (error != CL_SUCCESS) {
+ size_t log_size;
+ /* Find Size of log and print to std output */
+ clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);
+ char* program_log = new char[log_size + 2];
+ program_log[log_size] = '\n';
+ program_log[log_size + 1] = '\0';
+ clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, log_size + 1, program_log, NULL);
+ if (logger) {
+ logger->Log("Couldn't build program\n");
+ logger->Log(program_log);
+ }
+ delete[] program_log;
+ return false;
+ }
+
+ delete[] m_oclQueue;
+ delete[] m_oclKernelComputePartialVolumes;
+ delete[] m_oclKernelComputeSum;
+ m_oclQueue = new cl_command_queue[m_ompNumProcessors];
+ m_oclKernelComputePartialVolumes = new cl_kernel[m_ompNumProcessors];
+ m_oclKernelComputeSum = new cl_kernel[m_ompNumProcessors];
+
+ const char nameKernelComputePartialVolumes[] = "ComputePartialVolumes";
+ const char nameKernelComputeSum[] = "ComputePartialSums";
+ for (int32_t k = 0; k < m_ompNumProcessors; ++k) {
+ m_oclKernelComputePartialVolumes[k] = clCreateKernel(m_oclProgram, nameKernelComputePartialVolumes, &error);
+ if (error != CL_SUCCESS) {
+ if (logger) {
+ logger->Log("Couldn't create kernel\n");
+ }
+ return false;
+ }
+ m_oclKernelComputeSum[k] = clCreateKernel(m_oclProgram, nameKernelComputeSum, &error);
+ if (error != CL_SUCCESS) {
+ if (logger) {
+ logger->Log("Couldn't create kernel\n");
+ }
+ return false;
+ }
+ }
+
+ error = clGetKernelWorkGroupInfo(m_oclKernelComputePartialVolumes[0],
+ *m_oclDevice,
+ CL_KERNEL_WORK_GROUP_SIZE,
+ sizeof(size_t),
+ &m_oclWorkGroupSize,
+ NULL);
+ size_t workGroupSize = 0;
+ error = clGetKernelWorkGroupInfo(m_oclKernelComputeSum[0],
+ *m_oclDevice,
+ CL_KERNEL_WORK_GROUP_SIZE,
+ sizeof(size_t),
+ &workGroupSize,
+ NULL);
+ if (error != CL_SUCCESS) {
+ if (logger) {
+ logger->Log("Couldn't query work group info\n");
+ }
+ return false;
+ }
+
+ if (workGroupSize < m_oclWorkGroupSize) {
+ m_oclWorkGroupSize = workGroupSize;
+ }
+
+ for (int32_t k = 0; k < m_ompNumProcessors; ++k) {
+ m_oclQueue[k] = clCreateCommandQueue(m_oclContext, *m_oclDevice, 0 /*CL_QUEUE_PROFILING_ENABLE*/, &error);
+ if (error != CL_SUCCESS) {
+ if (logger) {
+ logger->Log("Couldn't create queue\n");
+ }
+ return false;
+ }
+ }
+ return true;
+#else //CL_VERSION_1_1
+ return false;
+#endif //CL_VERSION_1_1
+}
+bool VHACD::OCLRelease(IUserLogger* const logger)
+{
+#ifdef CL_VERSION_1_1
+ cl_int error;
+ if (m_oclKernelComputePartialVolumes) {
+ for (int32_t k = 0; k < m_ompNumProcessors; ++k) {
+ error = clReleaseKernel(m_oclKernelComputePartialVolumes[k]);
+ if (error != CL_SUCCESS) {
+ if (logger) {
+ logger->Log("Couldn't release kernal\n");
+ }
+ return false;
+ }
+ }
+ delete[] m_oclKernelComputePartialVolumes;
+ }
+ if (m_oclKernelComputeSum) {
+ for (int32_t k = 0; k < m_ompNumProcessors; ++k) {
+ error = clReleaseKernel(m_oclKernelComputeSum[k]);
+ if (error != CL_SUCCESS) {
+ if (logger) {
+ logger->Log("Couldn't release kernal\n");
+ }
+ return false;
+ }
+ }
+ delete[] m_oclKernelComputeSum;
+ }
+ if (m_oclQueue) {
+ for (int32_t k = 0; k < m_ompNumProcessors; ++k) {
+ error = clReleaseCommandQueue(m_oclQueue[k]);
+ if (error != CL_SUCCESS) {
+ if (logger) {
+ logger->Log("Couldn't release queue\n");
+ }
+ return false;
+ }
+ }
+ delete[] m_oclQueue;
+ }
+ error = clReleaseProgram(m_oclProgram);
+ if (error != CL_SUCCESS) {
+ if (logger) {
+ logger->Log("Couldn't release program\n");
+ }
+ return false;
+ }
+ error = clReleaseContext(m_oclContext);
+ if (error != CL_SUCCESS) {
+ if (logger) {
+ logger->Log("Couldn't release context\n");
+ }
+ return false;
+ }
+
+ return true;
+#else //CL_VERSION_1_1
+ return false;
+#endif //CL_VERSION_1_1
+}
+void VHACD::ComputePrimitiveSet(const Parameters& params)
+{
+ if (GetCancel()) {
+ return;
+ }
+ m_timer.Tic();
+
+ m_stage = "Compute primitive set";
+ m_operation = "Convert volume to pset";
+
+ std::ostringstream msg;
+ if (params.m_logger) {
+ msg << "+ " << m_stage << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+
+ Update(0.0, 0.0, params);
+ if (params.m_mode == 0) {
+ VoxelSet* vset = new VoxelSet;
+ m_volume->Convert(*vset);
+ m_pset = vset;
+ }
+ else {
+ TetrahedronSet* tset = new TetrahedronSet;
+ m_volume->Convert(*tset);
+ m_pset = tset;
+ }
+
+ delete m_volume;
+ m_volume = 0;
+
+ if (params.m_logger) {
+ msg.str("");
+ msg << "\t # primitives " << m_pset->GetNPrimitives() << std::endl;
+ msg << "\t # inside surface " << m_pset->GetNPrimitivesInsideSurf() << std::endl;
+ msg << "\t # on surface " << m_pset->GetNPrimitivesOnSurf() << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+
+ m_overallProgress = 15.0;
+ Update(100.0, 100.0, params);
+ m_timer.Toc();
+ if (params.m_logger) {
+ msg.str("");
+ msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+}
+bool VHACD::Compute(const double* const points, const uint32_t nPoints,
+ const uint32_t* const triangles,const uint32_t nTriangles, const Parameters& params)
+{
+ return ComputeACD(points, nPoints, triangles, nTriangles, params);
+}
+bool VHACD::Compute(const float* const points,const uint32_t nPoints,
+ const uint32_t* const triangles,const uint32_t nTriangles, const Parameters& params)
+{
+ return ComputeACD(points, nPoints, triangles, nTriangles, params);
+}
+double ComputePreferredCuttingDirection(const PrimitiveSet* const tset, Vec3<double>& 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<Plane>& planes)
+{
+ const Vec3<short> minV = vset.GetMinBBVoxels();
+ const Vec3<short> maxV = vset.GetMaxBBVoxels();
+ Vec3<double> 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<double>(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<double>(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<double>(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<Plane>& planes)
+{
+ const Vec3<double> minV = tset.GetMinBB();
+ const Vec3<double> 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<short>((maxV[0] - minV[0]) / scale + 0.5);
+ const short j1 = static_cast<short>((maxV[1] - minV[1]) / scale + 0.5);
+ const short k1 = static_cast<short>((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<Plane>& planes)
+{
+ const Vec3<short> minV = vset.GetMinBBVoxels();
+ const Vec3<short> maxV = vset.GetMaxBBVoxels();
+ Vec3<double> 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<double>(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<double>(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<double>(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<Plane>& planes)
+{
+ const Vec3<double> minV = tset.GetMinBB();
+ const Vec3<double> 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<short>(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<short>(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<short>(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<Plane>& planes,
+ const Vec3<double>& preferredCuttingDirection, const double w, const double alpha, const double beta,
+ const int32_t convexhullDownsampling, const double progress0, const double progress1, Plane& bestPlane,
+ double& minConcavity, const Parameters& params)
+{
+ if (GetCancel()) {
+ return;
+ }
+ char msg[256];
+ size_t nPrimitives = inputPSet->GetNPrimitives();
+ bool oclAcceleration = (nPrimitives > OCL_MIN_NUM_PRIMITIVES && params.m_oclAcceleration && params.m_mode == 0) ? true : false;
+ int32_t iBest = -1;
+ int32_t nPlanes = static_cast<int32_t>(planes.Size());
+ bool cancel = false;
+ int32_t done = 0;
+ double minTotal = MAX_DOUBLE;
+ double minBalance = MAX_DOUBLE;
+ double minSymmetry = MAX_DOUBLE;
+ minConcavity = MAX_DOUBLE;
+
+ SArray<Vec3<double> >* chPts = new SArray<Vec3<double> >[2 * m_ompNumProcessors];
+ Mesh* chs = new Mesh[2 * m_ompNumProcessors];
+ PrimitiveSet* onSurfacePSet = inputPSet->Create();
+ inputPSet->SelectOnSurface(onSurfacePSet);
+
+ PrimitiveSet** psets = 0;
+ if (!params.m_convexhullApproximation) {
+ psets = new PrimitiveSet*[2 * m_ompNumProcessors];
+ for (int32_t i = 0; i < 2 * m_ompNumProcessors; ++i) {
+ psets[i] = inputPSet->Create();
+ }
+ }
+
+#ifdef CL_VERSION_1_1
+ // allocate OpenCL data structures
+ cl_mem voxels;
+ cl_mem* partialVolumes = 0;
+ size_t globalSize = 0;
+ size_t nWorkGroups = 0;
+ double unitVolume = 0.0;
+ if (oclAcceleration) {
+ VoxelSet* vset = (VoxelSet*)inputPSet;
+ const Vec3<double> minBB = vset->GetMinBB();
+ const float fMinBB[4] = { (float)minBB[0], (float)minBB[1], (float)minBB[2], 1.0f };
+ const float fSclae[4] = { (float)vset->GetScale(), (float)vset->GetScale(), (float)vset->GetScale(), 0.0f };
+ const int32_t nVoxels = (int32_t)nPrimitives;
+ unitVolume = vset->GetUnitVolume();
+ nWorkGroups = (nPrimitives + 4 * m_oclWorkGroupSize - 1) / (4 * m_oclWorkGroupSize);
+ globalSize = nWorkGroups * m_oclWorkGroupSize;
+ cl_int error;
+ voxels = clCreateBuffer(m_oclContext,
+ CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
+ sizeof(Voxel) * nPrimitives,
+ vset->GetVoxels(),
+ &error);
+ if (error != CL_SUCCESS) {
+ if (params.m_logger) {
+ params.m_logger->Log("Couldn't create buffer\n");
+ }
+ SetCancel(true);
+ }
+
+ partialVolumes = new cl_mem[m_ompNumProcessors];
+ for (int32_t i = 0; i < m_ompNumProcessors; ++i) {
+ partialVolumes[i] = clCreateBuffer(m_oclContext,
+ CL_MEM_WRITE_ONLY,
+ sizeof(uint32_t) * 4 * nWorkGroups,
+ NULL,
+ &error);
+ if (error != CL_SUCCESS) {
+ if (params.m_logger) {
+ params.m_logger->Log("Couldn't create buffer\n");
+ }
+ SetCancel(true);
+ break;
+ }
+ error = clSetKernelArg(m_oclKernelComputePartialVolumes[i], 0, sizeof(cl_mem), &voxels);
+ error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 1, sizeof(uint32_t), &nVoxels);
+ error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 3, sizeof(float) * 4, fMinBB);
+ error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 4, sizeof(float) * 4, &fSclae);
+ error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 5, sizeof(uint32_t) * 4 * m_oclWorkGroupSize, NULL);
+ error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 6, sizeof(cl_mem), &(partialVolumes[i]));
+ error |= clSetKernelArg(m_oclKernelComputeSum[i], 0, sizeof(cl_mem), &(partialVolumes[i]));
+ error |= clSetKernelArg(m_oclKernelComputeSum[i], 2, sizeof(uint32_t) * 4 * m_oclWorkGroupSize, NULL);
+ if (error != CL_SUCCESS) {
+ if (params.m_logger) {
+ params.m_logger->Log("Couldn't kernel 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 (int32_t x = 0; x < nPlanes; ++x) {
+ int32_t threadID = 0;
+#if USE_THREAD == 1 && _OPENMP
+ threadID = omp_get_thread_num();
+#pragma omp flush(cancel)
+#endif
+ if (!cancel) {
+ //Update progress
+ if (GetCancel()) {
+ cancel = true;
+#if USE_THREAD == 1 && _OPENMP
+#pragma omp flush(cancel)
+#endif
+ }
+ Plane plane = planes[x];
+
+ if (oclAcceleration) {
+#ifdef CL_VERSION_1_1
+ const float fPlane[4] = { (float)plane.m_a, (float)plane.m_b, (float)plane.m_c, (float)plane.m_d };
+ cl_int error = clSetKernelArg(m_oclKernelComputePartialVolumes[threadID], 2, sizeof(float) * 4, fPlane);
+ if (error != CL_SUCCESS) {
+ if (params.m_logger) {
+ params.m_logger->Log("Couldn't kernel atguments \n");
+ }
+ SetCancel(true);
+ }
+
+ error = clEnqueueNDRangeKernel(m_oclQueue[threadID], m_oclKernelComputePartialVolumes[threadID],
+ 1, NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL);
+ if (error != CL_SUCCESS) {
+ if (params.m_logger) {
+ params.m_logger->Log("Couldn't run kernel \n");
+ }
+ SetCancel(true);
+ }
+ int32_t nValues = (int32_t)nWorkGroups;
+ while (nValues > 1) {
+ error = clSetKernelArg(m_oclKernelComputeSum[threadID], 1, sizeof(int32_t), &nValues);
+ if (error != CL_SUCCESS) {
+ if (params.m_logger) {
+ params.m_logger->Log("Couldn't kernel atguments \n");
+ }
+ SetCancel(true);
+ }
+ size_t nWorkGroups = (nValues + m_oclWorkGroupSize - 1) / m_oclWorkGroupSize;
+ size_t globalSize = nWorkGroups * m_oclWorkGroupSize;
+ error = clEnqueueNDRangeKernel(m_oclQueue[threadID], m_oclKernelComputeSum[threadID],
+ 1, NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL);
+ if (error != CL_SUCCESS) {
+ if (params.m_logger) {
+ params.m_logger->Log("Couldn't run kernel \n");
+ }
+ SetCancel(true);
+ }
+ nValues = (int32_t)nWorkGroups;
+ }
+#endif // CL_VERSION_1_1
+ }
+
+ Mesh& leftCH = chs[threadID];
+ Mesh& rightCH = chs[threadID + m_ompNumProcessors];
+ rightCH.ResizePoints(0);
+ leftCH.ResizePoints(0);
+ rightCH.ResizeTriangles(0);
+ leftCH.ResizeTriangles(0);
+
+// compute convex-hulls
+#ifdef TEST_APPROX_CH
+ double volumeLeftCH1;
+ double volumeRightCH1;
+#endif //TEST_APPROX_CH
+ if (params.m_convexhullApproximation) {
+ SArray<Vec3<double> >& leftCHPts = chPts[threadID];
+ SArray<Vec3<double> >& rightCHPts = chPts[threadID + m_ompNumProcessors];
+ rightCHPts.Resize(0);
+ leftCHPts.Resize(0);
+ onSurfacePSet->Intersect(plane, &rightCHPts, &leftCHPts, convexhullDownsampling * 32);
+ inputPSet->GetConvexHull().Clip(plane, rightCHPts, leftCHPts);
+ rightCH.ComputeConvexHull((double*)rightCHPts.Data(), rightCHPts.Size());
+ leftCH.ComputeConvexHull((double*)leftCHPts.Data(), leftCHPts.Size());
+#ifdef TEST_APPROX_CH
+ Mesh leftCH1;
+ Mesh rightCH1;
+ VoxelSet right;
+ VoxelSet left;
+ onSurfacePSet->Clip(plane, &right, &left);
+ right.ComputeConvexHull(rightCH1, convexhullDownsampling);
+ left.ComputeConvexHull(leftCH1, convexhullDownsampling);
+
+ volumeLeftCH1 = leftCH1.ComputeVolume();
+ volumeRightCH1 = rightCH1.ComputeVolume();
+#endif //TEST_APPROX_CH
+ }
+ else {
+ PrimitiveSet* const right = psets[threadID];
+ PrimitiveSet* const left = psets[threadID + m_ompNumProcessors];
+ onSurfacePSet->Clip(plane, right, left);
+ right->ComputeConvexHull(rightCH, convexhullDownsampling);
+ left->ComputeConvexHull(leftCH, convexhullDownsampling);
+ }
+ double volumeLeftCH = leftCH.ComputeVolume();
+ double volumeRightCH = rightCH.ComputeVolume();
+
+ // compute clipped volumes
+ double volumeLeft = 0.0;
+ double volumeRight = 0.0;
+ if (oclAcceleration) {
+#ifdef CL_VERSION_1_1
+ uint32_t volumes[4];
+ cl_int error = clEnqueueReadBuffer(m_oclQueue[threadID], partialVolumes[threadID], CL_TRUE,
+ 0, sizeof(uint32_t) * 4, volumes, 0, NULL, NULL);
+ size_t nPrimitivesRight = volumes[0] + volumes[1] + volumes[2] + volumes[3];
+ size_t nPrimitivesLeft = nPrimitives - nPrimitivesRight;
+ volumeRight = nPrimitivesRight * unitVolume;
+ volumeLeft = nPrimitivesLeft * unitVolume;
+ if (error != CL_SUCCESS) {
+ if (params.m_logger) {
+ params.m_logger->Log("Couldn't read buffer \n");
+ }
+ SetCancel(true);
+ }
+#endif // CL_VERSION_1_1
+ }
+ else {
+ inputPSet->ComputeClippedVolumes(plane, volumeRight, volumeLeft);
+ }
+ double concavityLeft = ComputeConcavity(volumeLeft, volumeLeftCH, m_volumeCH0);
+ double concavityRight = ComputeConcavity(volumeRight, volumeRightCH, m_volumeCH0);
+ double concavity = (concavityLeft + concavityRight);
+
+ // compute cost
+ double balance = alpha * fabs(volumeLeft - volumeRight) / m_volumeCH0;
+ double d = w * (preferredCuttingDirection[0] * plane.m_a + preferredCuttingDirection[1] * plane.m_b + preferredCuttingDirection[2] * plane.m_c);
+ double symmetry = beta * d;
+ double total = concavity + balance + symmetry;
+
+#if USE_THREAD == 1 && _OPENMP
+#pragma omp critical
+#endif
+ {
+ if (total < minTotal || (total == minTotal && x < iBest)) {
+ minConcavity = concavity;
+ minBalance = balance;
+ minSymmetry = symmetry;
+ bestPlane = plane;
+ minTotal = total;
+ iBest = x;
+ }
+ ++done;
+ if (!(done & 127)) // reduce update frequency
+ {
+ double progress = done * (progress1 - progress0) / nPlanes + progress0;
+ Update(m_stageProgress, progress, params);
+ }
+ }
+ }
+ }
+
+#ifdef DEBUG_TEMP
+ timerComputeCost.Toc();
+ printf_s("Cost[%i] = %f\n", nPlanes, timerComputeCost.GetElapsedTime());
+#endif // DEBUG_TEMP
+
+#ifdef CL_VERSION_1_1
+ if (oclAcceleration) {
+ clReleaseMemObject(voxels);
+ for (int32_t i = 0; i < m_ompNumProcessors; ++i) {
+ clReleaseMemObject(partialVolumes[i]);
+ }
+ delete[] partialVolumes;
+ }
+#endif // CL_VERSION_1_1
+
+ if (psets) {
+ for (int32_t i = 0; i < 2 * m_ompNumProcessors; ++i) {
+ delete psets[i];
+ }
+ delete[] psets;
+ }
+ delete onSurfacePSet;
+ delete[] chPts;
+ delete[] chs;
+ if (params.m_logger) {
+ sprintf(msg, "\n\t\t\t Best %04i T=%2.6f C=%2.6f B=%2.6f S=%2.6f (%1.1f, %1.1f, %1.1f, %3.3f)\n\n", iBest, minTotal, minConcavity, minBalance, minSymmetry, bestPlane.m_a, bestPlane.m_b, bestPlane.m_c, bestPlane.m_d);
+ params.m_logger->Log(msg);
+ }
+}
+void VHACD::ComputeACD(const Parameters& params)
+{
+ if (GetCancel()) {
+ return;
+ }
+ m_timer.Tic();
+
+ m_stage = "Approximate Convex Decomposition";
+ m_stageProgress = 0.0;
+ std::ostringstream msg;
+ if (params.m_logger) {
+ msg << "+ " << m_stage << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+
+ SArray<PrimitiveSet*> parts;
+ SArray<PrimitiveSet*> inputParts;
+ SArray<PrimitiveSet*> temp;
+ inputParts.PushBack(m_pset);
+ m_pset = 0;
+ SArray<Plane> planes;
+ SArray<Plane> planesRef;
+ uint32_t sub = 0;
+ bool firstIteration = true;
+ m_volumeCH0 = 1.0;
+
+ // Compute the decomposition depth based on the number of convex hulls being requested..
+ uint32_t hullCount = 2;
+ uint32_t depth = 1;
+ while (params.m_maxConvexHulls > hullCount)
+ {
+ depth++;
+ hullCount *= 2;
+ }
+ // We must always increment the decomposition depth one higher than the maximum number of hulls requested.
+ // The reason for this is as follows.
+ // Say, for example, the user requests 32 convex hulls exactly. This would be a decomposition depth of 5.
+ // However, when we do that, we do *not* necessarily get 32 hulls as a result. This is because, during
+ // the recursive descent of the binary tree, one or more of the leaf nodes may have no concavity and
+ // will not be split. So, in this way, even with a decomposition depth of 5, you can produce fewer than
+ // 32 hulls. So, in this case, we would set the decomposition depth to 6 (producing up to as high as 64 convex hulls).
+ // Then, the merge step which combines over-described hulls down to the user requested amount, we will end up
+ // getting exactly 32 convex hulls as a result.
+ // We could just allow the artist to directly control the decomposition depth directly, but this would be a bit
+ // too complex and the preference is simply to let them specify how many hulls they want and derive the solution
+ // from that.
+ depth++;
+
+
+ while (sub++ < depth && inputParts.Size() > 0 && !m_cancel) {
+ msg.str("");
+ msg << "Subdivision level " << sub;
+ m_operation = msg.str();
+
+ if (params.m_logger) {
+ msg.str("");
+ msg << "\t Subdivision level " << sub << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+
+ double maxConcavity = 0.0;
+ const size_t nInputParts = inputParts.Size();
+ Update(m_stageProgress, 0.0, params);
+ for (size_t p = 0; p < nInputParts && !m_cancel; ++p) {
+ const double progress0 = p * 100.0 / nInputParts;
+ const double progress1 = (p + 0.75) * 100.0 / nInputParts;
+ const double progress2 = (p + 1.00) * 100.0 / nInputParts;
+
+ Update(m_stageProgress, progress0, params);
+
+ PrimitiveSet* pset = inputParts[p];
+ inputParts[p] = 0;
+ double volume = pset->ComputeVolume();
+ pset->ComputeBB();
+ pset->ComputePrincipalAxes();
+ if (params.m_pca) {
+ pset->AlignToPrincipalAxes();
+ }
+
+ pset->ComputeConvexHull(pset->GetConvexHull());
+ double volumeCH = fabs(pset->GetConvexHull().ComputeVolume());
+ if (firstIteration) {
+ m_volumeCH0 = volumeCH;
+ }
+
+ double concavity = ComputeConcavity(volume, volumeCH, m_volumeCH0);
+ double error = 1.01 * pset->ComputeMaxVolumeError() / m_volumeCH0;
+
+ if (firstIteration) {
+ firstIteration = false;
+ }
+
+ if (params.m_logger) {
+ msg.str("");
+ msg << "\t -> Part[" << p
+ << "] C = " << concavity
+ << ", E = " << error
+ << ", VS = " << pset->GetNPrimitivesOnSurf()
+ << ", VI = " << pset->GetNPrimitivesInsideSurf()
+ << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+
+ if (concavity > params.m_concavity && concavity > error) {
+ Vec3<double> 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<double>& 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<Vec3<double> >& pts)
+{
+ const int32_t n = (int32_t)mesh->GetNPoints();
+ for (int32_t i = 0; i < n; ++i) {
+ pts.PushBack(mesh->GetPoint(i));
+ }
+}
+void ComputeConvexHull(const Mesh* const ch1, const Mesh* const ch2, SArray<Vec3<double> >& pts, Mesh* const combinedCH)
+{
+ pts.Resize(0);
+ AddPoints(ch1, pts);
+ AddPoints(ch2, pts);
+
+ btConvexHullComputer ch;
+ ch.compute((double*)pts.Data(), 3 * sizeof(double), (int32_t)pts.Size(), -1.0, -1.0);
+ combinedCH->ResizePoints(0);
+ combinedCH->ResizeTriangles(0);
+ for (int32_t v = 0; v < ch.vertices.size(); v++) {
+ combinedCH->AddPoint(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ()));
+ }
+ const int32_t nt = ch.faces.size();
+ for (int32_t t = 0; t < nt; ++t) {
+ const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]);
+ int32_t a = sourceEdge->getSourceVertex();
+ int32_t b = sourceEdge->getTargetVertex();
+ const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace();
+ int32_t c = edge->getTargetVertex();
+ while (c != a) {
+ combinedCH->AddTriangle(Vec3<int32_t>(a, b, c));
+ edge = edge->getNextEdgeOfFace();
+ b = c;
+ c = edge->getTargetVertex();
+ }
+ }
+}
+void VHACD::MergeConvexHulls(const Parameters& params)
+{
+ if (GetCancel()) {
+ return;
+ }
+ m_timer.Tic();
+
+ m_stage = "Merge Convex Hulls";
+
+ std::ostringstream msg;
+ if (params.m_logger) {
+ msg << "+ " << m_stage << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+
+ // Get the current number of convex hulls
+ size_t nConvexHulls = m_convexHulls.Size();
+ // Iteration counter
+ int32_t iteration = 0;
+ // While we have more than at least one convex hull and the user has not asked us to cancel the operation
+ if (nConvexHulls > 1 && !m_cancel)
+ {
+ // Get the gamma error threshold for when to exit
+ SArray<Vec3<double> > pts;
+ Mesh combinedCH;
+
+ // Populate the cost matrix
+ size_t idx = 0;
+ SArray<float> 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<float>::max)();
+ const size_t addr = FindMinimumElement(costMatrix.Data(), &bestCost, 0, costMatrix.Size());
+ if ( (costSize-1) < params.m_maxConvexHulls)
+ {
+ break;
+ }
+ const size_t addrI = (static_cast<int32_t>(sqrt(1 + (8 * addr))) - 1) >> 1;
+ const size_t p1 = addrI + 1;
+ const size_t p2 = addr - ((addrI * (addrI + 1)) >> 1);
+ assert(p1 >= 0);
+ assert(p2 >= 0);
+ assert(p1 < costSize);
+ assert(p2 < costSize);
+
+ if (params.m_logger)
+ {
+ msg.str("");
+ msg << "\t\t Merging (" << p1 << ", " << p2 << ") " << bestCost << std::endl
+ << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+
+ // Make the lowest cost row and column into a new hull
+ Mesh* cch = new Mesh;
+ ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, cch);
+ delete m_convexHulls[p2];
+ m_convexHulls[p2] = cch;
+
+ delete m_convexHulls[p1];
+ std::swap(m_convexHulls[p1], m_convexHulls[m_convexHulls.Size() - 1]);
+ m_convexHulls.PopBack();
+
+ costSize = costSize - 1;
+
+ // Calculate costs versus the new hull
+ size_t rowIdx = ((p2 - 1) * p2) >> 1;
+ const float volume1 = m_convexHulls[p2]->ComputeVolume();
+ for (size_t i = 0; (i < p2) && (!m_cancel); ++i)
+ {
+ ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, &combinedCH);
+ costMatrix[rowIdx++] = ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0);
+ }
+
+ rowIdx += p2;
+ for (size_t i = p2 + 1; (i < costSize) && (!m_cancel); ++i)
+ {
+ ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, &combinedCH);
+ costMatrix[rowIdx] = ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0);
+ rowIdx += i;
+ assert(rowIdx >= 0);
+ }
+
+ // Move the top column in to replace its space
+ const size_t erase_idx = ((costSize - 1) * costSize) >> 1;
+ if (p1 < costSize) {
+ rowIdx = (addrI * p1) >> 1;
+ size_t top_row = erase_idx;
+ for (size_t i = 0; i < p1; ++i) {
+ if (i != p2) {
+ costMatrix[rowIdx] = costMatrix[top_row];
+ }
+ ++rowIdx;
+ ++top_row;
+ }
+
+ ++top_row;
+ rowIdx += p1;
+ for (size_t i = p1 + 1; i < (costSize + 1); ++i) {
+ costMatrix[rowIdx] = costMatrix[top_row++];
+ rowIdx += i;
+ assert(rowIdx >= 0);
+ }
+ }
+ costMatrix.Resize(erase_idx);
+ }
+ }
+ m_overallProgress = 99.0;
+ Update(100.0, 100.0, params);
+ m_timer.Toc();
+ if (params.m_logger) {
+ msg.str("");
+ msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+}
+void VHACD::SimplifyConvexHull(Mesh* const ch, const size_t nvertices, const double minVolume)
+{
+ if (nvertices <= 4) {
+ return;
+ }
+ ICHull icHull;
+ if (mRaycastMesh)
+ {
+ // We project these points onto the original source mesh to increase precision
+ // The voxelization process drops floating point precision so returned data points are not exactly lying on the
+ // surface of the original source mesh.
+ // The first step is we need to compute the bounding box of the mesh we are trying to build a convex hull for.
+ // From this bounding box, we compute the length of the diagonal to get a relative size and center for point projection
+ uint32_t nPoints = ch->GetNPoints();
+ Vec3<double> *inputPoints = ch->GetPointsBuffer();
+ Vec3<double> bmin(inputPoints[0]);
+ Vec3<double> bmax(inputPoints[1]);
+ for (uint32_t i = 1; i < nPoints; i++)
+ {
+ const Vec3<double> &p = inputPoints[i];
+ p.UpdateMinMax(bmin, bmax);
+ }
+ Vec3<double> center;
+ double diagonalLength = center.GetCenter(bmin, bmax); // Get the center of the bounding box
+ // This is the error threshold for determining if we should use the raycast result data point vs. the voxelized result.
+ double pointDistanceThreshold = diagonalLength * 0.05;
+ // If a new point is within 1/100th the diagonal length of the bounding volume we do not add it. To do so would create a
+ // thin sliver in the resulting convex hull
+ double snapDistanceThreshold = diagonalLength * 0.01;
+ double snapDistanceThresholdSquared = snapDistanceThreshold*snapDistanceThreshold;
+
+ // Allocate buffer for projected vertices
+ Vec3<double> *outputPoints = new Vec3<double>[nPoints];
+ uint32_t outCount = 0;
+ for (uint32_t i = 0; i < nPoints; i++)
+ {
+ Vec3<double> &inputPoint = inputPoints[i];
+ Vec3<double> &outputPoint = outputPoints[outCount];
+ // Compute the direction vector from the center of this mesh to the vertex
+ Vec3<double> dir = inputPoint - center;
+ // Normalize the direction vector.
+ dir.Normalize();
+ // Multiply times the diagonal length of the mesh
+ dir *= diagonalLength;
+ // Add the center back in again to get the destination point
+ dir += center;
+ // By default the output point is equal to the input point
+ outputPoint = inputPoint;
+ double pointDistance;
+ if (mRaycastMesh->raycast(center.GetData(), dir.GetData(), inputPoint.GetData(), outputPoint.GetData(),&pointDistance) )
+ {
+ // If the nearest intersection point is too far away, we keep the original source data point.
+ // Not all points lie directly on the original mesh surface
+ if (pointDistance > pointDistanceThreshold)
+ {
+ outputPoint = inputPoint;
+ }
+ }
+ // Ok, before we add this point, we do not want to create points which are extremely close to each other.
+ // This will result in tiny sliver triangles which are really bad for collision detection.
+ bool foundNearbyPoint = false;
+ for (uint32_t j = 0; j < outCount; j++)
+ {
+ // If this new point is extremely close to an existing point, we do not add it!
+ double squaredDistance = outputPoints[j].GetDistanceSquared(outputPoint);
+ if (squaredDistance < snapDistanceThresholdSquared )
+ {
+ foundNearbyPoint = true;
+ break;
+ }
+ }
+ if (!foundNearbyPoint)
+ {
+ outCount++;
+ }
+ }
+ icHull.AddPoints(outputPoints, outCount);
+ delete[]outputPoints;
+ }
+ else
+ {
+ icHull.AddPoints(ch->GetPointsBuffer(), ch->GetNPoints());
+ }
+ icHull.Process((uint32_t)nvertices, minVolume);
+ TMMesh& mesh = icHull.GetMesh();
+ const size_t nT = mesh.GetNTriangles();
+ const size_t nV = mesh.GetNVertices();
+ ch->ResizePoints(nV);
+ ch->ResizeTriangles(nT);
+ mesh.GetIFS(ch->GetPointsBuffer(), ch->GetTrianglesBuffer());
+}
+void VHACD::SimplifyConvexHulls(const Parameters& params)
+{
+ if (m_cancel || params.m_maxNumVerticesPerCH < 4) {
+ return;
+ }
+ m_timer.Tic();
+
+ m_stage = "Simplify convex-hulls";
+ m_operation = "Simplify convex-hulls";
+
+ std::ostringstream msg;
+ const size_t nConvexHulls = m_convexHulls.Size();
+ if (params.m_logger) {
+ msg << "+ Simplify " << nConvexHulls << " convex-hulls " << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+
+ Update(0.0, 0.0, params);
+ for (size_t i = 0; i < nConvexHulls && !m_cancel; ++i) {
+ if (params.m_logger) {
+ msg.str("");
+ msg << "\t\t Simplify CH[" << std::setfill('0') << std::setw(5) << i << "] " << m_convexHulls[i]->GetNPoints() << " V, " << m_convexHulls[i]->GetNTriangles() << " T" << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+ SimplifyConvexHull(m_convexHulls[i], params.m_maxNumVerticesPerCH, m_volumeCH0 * params.m_minVolumePerCH);
+ }
+
+ m_overallProgress = 100.0;
+ Update(100.0, 100.0, params);
+ m_timer.Toc();
+ if (params.m_logger) {
+ msg.str("");
+ msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl;
+ params.m_logger->Log(msg.str().c_str());
+ }
+}
+
+bool VHACD::ComputeCenterOfMass(double centerOfMass[3]) const
+{
+ bool ret = false;
+
+ centerOfMass[0] = 0;
+ centerOfMass[1] = 0;
+ centerOfMass[2] = 0;
+ // Get number of convex hulls in the result
+ uint32_t hullCount = GetNConvexHulls();
+ if (hullCount) // if we have results
+ {
+ ret = true;
+ double totalVolume = 0;
+ // Initialize the center of mass to zero
+ centerOfMass[0] = 0;
+ centerOfMass[1] = 0;
+ centerOfMass[2] = 0;
+ // Compute the total volume of all convex hulls
+ for (uint32_t i = 0; i < hullCount; i++)
+ {
+ ConvexHull ch;
+ GetConvexHull(i, ch);
+ totalVolume += ch.m_volume;
+ }
+ // compute the reciprocal of the total volume
+ double recipVolume = 1.0 / totalVolume;
+ // Add in the weighted by volume average of the center point of each convex hull
+ for (uint32_t i = 0; i < hullCount; i++)
+ {
+ ConvexHull ch;
+ GetConvexHull(i, ch);
+ double ratio = ch.m_volume*recipVolume;
+ centerOfMass[0] += ch.m_center[0] * ratio;
+ centerOfMass[1] += ch.m_center[1] * ratio;
+ centerOfMass[2] += ch.m_center[2] * ratio;
+ }
+ }
+ return ret;
+}
+
+#pragma warning(disable:4189 4101)
+
+// Will analyze the HACD results and compute the constraints solutions.
+// It will analyze the point at which any two convex hulls touch each other and
+// return the total number of constraint pairs found
+uint32_t VHACD::ComputeConstraints(void)
+{
+ mConstraints.clear(); // erase any previous constraint results
+ uint32_t hullCount = GetNConvexHulls(); // get the number of convex hulls in the results
+ if (hullCount == 0)
+ return 0;
+#if DEBUG_VISUALIZE_CONSTRAINTS
+ gRenderDebug->pushRenderState();
+ gRenderDebug->setCurrentDisplayTime(10);
+#endif
+
+ // We voxelize the convex hull
+ class HullData
+ {
+ public:
+ HullData(void)
+ {
+ FLOAT_MATH::fm_initMinMax(mBmin, mBmax);
+ }
+
+ ~HullData(void)
+ {
+ FLOAT_MATH::fm_releaseVertexIndex(mVertexIndex);
+ FLOAT_MATH::fm_releaseTesselate(mTesselate);
+ delete[]mIndices;
+ }
+
+ void computeResolution(void)
+ {
+ mDiagonalDistance = FLOAT_MATH::fm_distance(mBmin, mBmax);
+ mTessellateDistance = mDiagonalDistance / 20;
+ mNearestPointDistance = mDiagonalDistance / 20.0f;
+ mPointResolution = mDiagonalDistance / 100;
+ mVertexIndex = FLOAT_MATH::fm_createVertexIndex(mPointResolution, false);
+ mTesselate = FLOAT_MATH::fm_createTesselate();
+ }
+
+ void computeTesselation(void)
+ {
+ mTesselationIndices = mTesselate->tesselate(mVertexIndex, mSourceTriangleCount, mIndices, mTessellateDistance, 6, mTessellateTriangleCount);
+ uint32_t vcount = mVertexIndex->getVcount();
+ }
+
+ bool getNearestVert(const double sourcePoint[3],
+ double nearest[3],
+ const HullData &other,
+ double nearestThreshold)
+ {
+ bool ret = false;
+
+ double nt2 = nearestThreshold*nearestThreshold;
+ uint32_t vcount = other.mVertexIndex->getVcount();
+ for (uint32_t i = 0; i < vcount; i++)
+ {
+ const double *p = other.mVertexIndex->getVertexDouble(i);
+ double d2 = FLOAT_MATH::fm_distanceSquared(sourcePoint, p);
+ if (d2 < nt2)
+ {
+ nearest[0] = p[0];
+ nearest[1] = p[1];
+ nearest[2] = p[2];
+ nt2 = d2;
+ ret = true;
+ }
+ }
+
+ return ret;
+ }
+
+ void findMatchingPoints(const HullData &other)
+ {
+ uint32_t vcount = mVertexIndex->getVcount();
+ for (uint32_t i = 0; i < vcount; i++)
+ {
+ const double *sourcePoint = mVertexIndex->getVertexDouble(i);
+ double nearestPoint[3];
+ if (getNearestVert(sourcePoint, nearestPoint, other, mNearestPointDistance))
+ {
+#if DEBUG_VISUALIZE_CONSTRAINTS
+ float fp1[3];
+ float fp2[3];
+ FLOAT_MATH::fm_doubleToFloat3(sourcePoint, fp1);
+ FLOAT_MATH::fm_doubleToFloat3(nearestPoint, fp2);
+ gRenderDebug->debugRay(fp1, fp2);
+#endif
+ }
+ }
+
+ }
+
+ double mBmin[3];
+ double mBmax[3];
+ double mDiagonalDistance;
+ double mTessellateDistance;
+ double mPointResolution;
+ double mNearestPointDistance;
+ uint32_t mSourceTriangleCount{ 0 };
+ uint32_t mTessellateTriangleCount{ 0 };
+ uint32_t *mIndices{ nullptr };
+ FLOAT_MATH::fm_VertexIndex *mVertexIndex{ nullptr };
+ FLOAT_MATH::fm_Tesselate *mTesselate{ nullptr };
+ const uint32_t *mTesselationIndices{ nullptr };
+ };
+
+ HullData *hullData = new HullData[hullCount];
+ for (uint32_t i = 0; i < hullCount; i++)
+ {
+ HullData &hd = hullData[i];
+ ConvexHull ch;
+ GetConvexHull(i, ch);
+ // Compute the bounding volume of this convex hull
+ for (uint32_t j = 0; j < ch.m_nPoints; j++)
+ {
+ const double *p = &ch.m_points[j * 3];
+ FLOAT_MATH::fm_minmax(p, hd.mBmin, hd.mBmax);
+ }
+ hd.computeResolution(); // Compute the tessellation resolution
+ uint32_t tcount = ch.m_nTriangles;
+ hd.mSourceTriangleCount = tcount;
+ hd.mIndices = new uint32_t[tcount * 3];
+ for (uint32_t j = 0; j < tcount; j++)
+ {
+ uint32_t i1 = ch.m_triangles[j * 3 + 0];
+ uint32_t i2 = ch.m_triangles[j * 3 + 1];
+ uint32_t i3 = ch.m_triangles[j * 3 + 2];
+ const double *p1 = &ch.m_points[i1 * 3];
+ const double *p2 = &ch.m_points[i2 * 3];
+ const double *p3 = &ch.m_points[i3 * 3];
+ bool newPos;
+ hd.mIndices[j * 3 + 0] = hd.mVertexIndex->getIndex(p1, newPos);
+ hd.mIndices[j * 3 + 1] = hd.mVertexIndex->getIndex(p2, newPos);
+ hd.mIndices[j * 3 + 2] = hd.mVertexIndex->getIndex(p3, newPos);
+ }
+ hd.computeTesselation();
+ }
+
+ for (uint32_t i = 0; i < hullCount; i++)
+ {
+ HullData &hd = hullData[i];
+ // Slightly inflate the bounding box around each convex hull for intersection tests
+ // during the constraint building phase
+ FLOAT_MATH::fm_inflateMinMax(hd.mBmin, hd.mBmax, 0.05f);
+ }
+
+ // Look for every possible pair of convex hulls as possible constraints
+ for (uint32_t i = 0; i < hullCount; i++)
+ {
+ HullData &hd1 = hullData[i];
+ for (uint32_t j = i + 1; j < hullCount; j++)
+ {
+ HullData &hd2 = hullData[j];
+ if (FLOAT_MATH::fm_intersectAABB(hd1.mBmin, hd1.mBmax, hd2.mBmin, hd2.mBmax))
+ {
+ // ok. if two convex hulls intersect, we are going to find the <n> number of nearest
+ // matching points between them.
+ hd1.findMatchingPoints(hd2);
+ }
+ }
+ }
+
+
+#if DEBUG_VISUALIZE_CONSTRAINTS
+ gRenderDebug->popRenderState();
+#endif
+
+ return uint32_t(mConstraints.size());
+}
+
+// Returns a pointer to the constraint index; null if the index is not valid or
+// the user did not previously call 'ComputeConstraints'
+const VHACD::IVHACD::Constraint *VHACD::GetConstraint(uint32_t index) const
+{
+ const Constraint *ret = nullptr;
+
+ if (index < mConstraints.size())
+ {
+ ret = &mConstraints[index];
+ }
+
+ return ret;
+}
+
+
+} // end of VHACD namespace \ No newline at end of file
diff --git a/sdk/extensions/authoring/source/VHACD/src/btAlignedAllocator.cpp b/sdk/extensions/authoring/source/VHACD/src/btAlignedAllocator.cpp
new file mode 100644
index 0000000..11d594f
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/src/btAlignedAllocator.cpp
@@ -0,0 +1,180 @@
+/*
+Bullet Continuous Collision Detection and Physics Library
+Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/
+
+This software is provided 'as-is', without any express or implied warranty.
+In no event will the authors be held liable for any damages arising from the use of this software.
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it freely,
+subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "btAlignedAllocator.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable:4311 4302)
+#endif
+
+int32_t gNumAlignedAllocs = 0;
+int32_t gNumAlignedFree = 0;
+int32_t gTotalBytesAlignedAllocs = 0; //detect memory leaks
+
+static void* btAllocDefault(size_t size)
+{
+ return malloc(size);
+}
+
+static void btFreeDefault(void* ptr)
+{
+ free(ptr);
+}
+
+static btAllocFunc* sAllocFunc = btAllocDefault;
+static btFreeFunc* sFreeFunc = btFreeDefault;
+
+#if defined(BT_HAS_ALIGNED_ALLOCATOR)
+#include <malloc.h>
+static void* btAlignedAllocDefault(size_t size, int32_t alignment)
+{
+ return _aligned_malloc(size, (size_t)alignment);
+}
+
+static void btAlignedFreeDefault(void* ptr)
+{
+ _aligned_free(ptr);
+}
+#elif defined(__CELLOS_LV2__)
+#include <stdlib.h>
+
+static inline void* btAlignedAllocDefault(size_t size, int32_t alignment)
+{
+ return memalign(alignment, size);
+}
+
+static inline void btAlignedFreeDefault(void* ptr)
+{
+ free(ptr);
+}
+#else
+static inline void* btAlignedAllocDefault(size_t size, int32_t alignment)
+{
+ void* ret;
+ char* real;
+ unsigned long offset;
+
+ real = (char*)sAllocFunc(size + sizeof(void*) + (alignment - 1));
+ if (real) {
+ offset = (alignment - (unsigned long)(real + sizeof(void*))) & (alignment - 1);
+ ret = (void*)((real + sizeof(void*)) + offset);
+ *((void**)(ret)-1) = (void*)(real);
+ }
+ else {
+ ret = (void*)(real);
+ }
+ return (ret);
+}
+
+static inline void btAlignedFreeDefault(void* ptr)
+{
+ void* real;
+
+ if (ptr) {
+ real = *((void**)(ptr)-1);
+ sFreeFunc(real);
+ }
+}
+#endif
+
+static btAlignedAllocFunc* sAlignedAllocFunc = btAlignedAllocDefault;
+static btAlignedFreeFunc* sAlignedFreeFunc = btAlignedFreeDefault;
+
+void btAlignedAllocSetCustomAligned(btAlignedAllocFunc* allocFunc, btAlignedFreeFunc* freeFunc)
+{
+ sAlignedAllocFunc = allocFunc ? allocFunc : btAlignedAllocDefault;
+ sAlignedFreeFunc = freeFunc ? freeFunc : btAlignedFreeDefault;
+}
+
+void btAlignedAllocSetCustom(btAllocFunc* allocFunc, btFreeFunc* freeFunc)
+{
+ sAllocFunc = allocFunc ? allocFunc : btAllocDefault;
+ sFreeFunc = freeFunc ? freeFunc : btFreeDefault;
+}
+
+#ifdef BT_DEBUG_MEMORY_ALLOCATIONS
+//this generic allocator provides the total allocated number of bytes
+#include <stdio.h>
+
+void* btAlignedAllocInternal(size_t size, int32_t alignment, int32_t line, char* filename)
+{
+ void* ret;
+ char* real;
+ unsigned long offset;
+
+ gTotalBytesAlignedAllocs += size;
+ gNumAlignedAllocs++;
+
+ real = (char*)sAllocFunc(size + 2 * sizeof(void*) + (alignment - 1));
+ if (real) {
+ offset = (alignment - (unsigned long)(real + 2 * sizeof(void*))) & (alignment - 1);
+ ret = (void*)((real + 2 * sizeof(void*)) + offset);
+ *((void**)(ret)-1) = (void*)(real);
+ *((int32_t*)(ret)-2) = size;
+ }
+ else {
+ ret = (void*)(real); //??
+ }
+
+ printf("allocation#%d at address %x, from %s,line %d, size %d\n", gNumAlignedAllocs, real, filename, line, size);
+
+ int32_t* ptr = (int32_t*)ret;
+ *ptr = 12;
+ return (ret);
+}
+
+void btAlignedFreeInternal(void* ptr, int32_t line, char* filename)
+{
+
+ void* real;
+ gNumAlignedFree++;
+
+ if (ptr) {
+ real = *((void**)(ptr)-1);
+ int32_t size = *((int32_t*)(ptr)-2);
+ gTotalBytesAlignedAllocs -= size;
+
+ printf("free #%d at address %x, from %s,line %d, size %d\n", gNumAlignedFree, real, filename, line, size);
+
+ sFreeFunc(real);
+ }
+ else {
+ printf("NULL ptr\n");
+ }
+}
+
+#else //BT_DEBUG_MEMORY_ALLOCATIONS
+
+void* btAlignedAllocInternal(size_t size, int32_t alignment)
+{
+ gNumAlignedAllocs++;
+ void* ptr;
+ ptr = sAlignedAllocFunc(size, alignment);
+ // printf("btAlignedAllocInternal %d, %x\n",size,ptr);
+ return ptr;
+}
+
+void btAlignedFreeInternal(void* ptr)
+{
+ if (!ptr) {
+ return;
+ }
+
+ gNumAlignedFree++;
+ // printf("btAlignedFreeInternal %x\n",ptr);
+ sAlignedFreeFunc(ptr);
+}
+
+#endif //BT_DEBUG_MEMORY_ALLOCATIONS
diff --git a/sdk/extensions/authoring/source/VHACD/src/btConvexHullComputer.cpp b/sdk/extensions/authoring/source/VHACD/src/btConvexHullComputer.cpp
new file mode 100644
index 0000000..d3d749a
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/src/btConvexHullComputer.cpp
@@ -0,0 +1,2479 @@
+/*
+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 <string.h>
+
+#include "btAlignedObjectArray.h"
+#include "btConvexHullComputer.h"
+#include "btMinMax.h"
+#include "btVector3.h"
+
+#ifdef __GNUC__
+#include <stdint.h>
+#elif defined(_MSC_VER)
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#else
+typedef int32_t int32_t;
+typedef long long int32_t int64_t;
+typedef uint32_t uint32_t;
+typedef unsigned long long int32_t uint64_t;
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(disable:4458)
+#endif
+
+//The definition of USE_X86_64_ASM is moved into the build system. You can enable it manually by commenting out the following lines
+//#if (defined(__GNUC__) && defined(__x86_64__) && !defined(__ICL)) // || (defined(__ICL) && defined(_M_X64)) bug in Intel compiler, disable inline assembly
+// #define USE_X86_64_ASM
+//#endif
+
+//#define DEBUG_CONVEX_HULL
+//#define SHOW_ITERATIONS
+
+#if defined(DEBUG_CONVEX_HULL) || defined(SHOW_ITERATIONS)
+#include <stdio.h>
+#endif
+
+// Convex hull implementation based on Preparata and Hong
+// Ole Kniemeyer, MAXON Computer GmbH
+class btConvexHullInternal {
+public:
+ class Point64 {
+ public:
+ int64_t x;
+ int64_t y;
+ int64_t z;
+
+ Point64(int64_t x, int64_t y, int64_t z)
+ : x(x)
+ , y(y)
+ , z(z)
+ {
+ }
+
+ bool isZero()
+ {
+ return (x == 0) && (y == 0) && (z == 0);
+ }
+
+ int64_t dot(const Point64& b) const
+ {
+ return x * b.x + y * b.y + z * b.z;
+ }
+ };
+
+ class Point32 {
+ public:
+ int32_t x;
+ int32_t y;
+ int32_t z;
+ int32_t index;
+
+ Point32()
+ {
+ }
+
+ Point32(int32_t x, int32_t y, int32_t z)
+ : x(x)
+ , y(y)
+ , z(z)
+ , index(-1)
+ {
+ }
+
+ bool operator==(const Point32& b) const
+ {
+ return (x == b.x) && (y == b.y) && (z == b.z);
+ }
+
+ bool operator!=(const Point32& b) const
+ {
+ return (x != b.x) || (y != b.y) || (z != b.z);
+ }
+
+ bool isZero()
+ {
+ return (x == 0) && (y == 0) && (z == 0);
+ }
+
+ Point64 cross(const Point32& b) const
+ {
+ return Point64(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x);
+ }
+
+ Point64 cross(const Point64& b) const
+ {
+ return Point64(y * b.z - z * b.y, z * b.x - x * b.z, x * b.y - y * b.x);
+ }
+
+ int64_t dot(const Point32& b) const
+ {
+ return x * b.x + y * b.y + z * b.z;
+ }
+
+ int64_t dot(const Point64& b) const
+ {
+ return x * b.x + y * b.y + z * b.z;
+ }
+
+ Point32 operator+(const Point32& b) const
+ {
+ return Point32(x + b.x, y + b.y, z + b.z);
+ }
+
+ Point32 operator-(const Point32& b) const
+ {
+ return Point32(x - b.x, y - b.y, z - b.z);
+ }
+ };
+
+ class Int128 {
+ public:
+ uint64_t low;
+ uint64_t high;
+
+ Int128()
+ {
+ }
+
+ Int128(uint64_t low, uint64_t high)
+ : low(low)
+ , high(high)
+ {
+ }
+
+ Int128(uint64_t low)
+ : low(low)
+ , high(0)
+ {
+ }
+
+ Int128(int64_t value)
+ : low(value)
+ , high((value >= 0) ? 0 : (uint64_t)-1LL)
+ {
+ }
+
+ static Int128 mul(int64_t a, int64_t b);
+
+ static Int128 mul(uint64_t a, uint64_t b);
+
+ Int128 operator-() const
+ {
+ return Int128((uint64_t) - (int64_t)low, ~high + (low == 0));
+ }
+
+ Int128 operator+(const Int128& b) const
+ {
+#ifdef USE_X86_64_ASM
+ Int128 result;
+ __asm__("addq %[bl], %[rl]\n\t"
+ "adcq %[bh], %[rh]\n\t"
+ : [rl] "=r"(result.low), [rh] "=r"(result.high)
+ : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high)
+ : "cc");
+ return result;
+#else
+ uint64_t lo = low + b.low;
+ return Int128(lo, high + b.high + (lo < low));
+#endif
+ }
+
+ Int128 operator-(const Int128& b) const
+ {
+#ifdef USE_X86_64_ASM
+ Int128 result;
+ __asm__("subq %[bl], %[rl]\n\t"
+ "sbbq %[bh], %[rh]\n\t"
+ : [rl] "=r"(result.low), [rh] "=r"(result.high)
+ : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high)
+ : "cc");
+ return result;
+#else
+ return *this + -b;
+#endif
+ }
+
+ Int128& operator+=(const Int128& b)
+ {
+#ifdef USE_X86_64_ASM
+ __asm__("addq %[bl], %[rl]\n\t"
+ "adcq %[bh], %[rh]\n\t"
+ : [rl] "=r"(low), [rh] "=r"(high)
+ : "0"(low), "1"(high), [bl] "g"(b.low), [bh] "g"(b.high)
+ : "cc");
+#else
+ uint64_t lo = low + b.low;
+ if (lo < low) {
+ ++high;
+ }
+ low = lo;
+ high += b.high;
+#endif
+ return *this;
+ }
+
+ Int128& operator++()
+ {
+ if (++low == 0) {
+ ++high;
+ }
+ return *this;
+ }
+
+ Int128 operator*(int64_t b) const;
+
+ btScalar toScalar() const
+ {
+ return ((int64_t)high >= 0) ? btScalar(high) * (btScalar(0x100000000LL) * btScalar(0x100000000LL)) + btScalar(low)
+ : -(-*this).toScalar();
+ }
+
+ int32_t getSign() const
+ {
+ return ((int64_t)high < 0) ? -1 : (high || low) ? 1 : 0;
+ }
+
+ bool operator<(const Int128& b) const
+ {
+ return (high < b.high) || ((high == b.high) && (low < b.low));
+ }
+
+ int32_t ucmp(const Int128& b) const
+ {
+ if (high < b.high) {
+ return -1;
+ }
+ if (high > b.high) {
+ return 1;
+ }
+ if (low < b.low) {
+ return -1;
+ }
+ if (low > b.low) {
+ return 1;
+ }
+ return 0;
+ }
+ };
+
+ class Rational64 {
+ private:
+ uint64_t m_numerator;
+ uint64_t m_denominator;
+ int32_t sign;
+
+ public:
+ Rational64(int64_t numerator, int64_t denominator)
+ {
+ if (numerator > 0) {
+ sign = 1;
+ m_numerator = (uint64_t)numerator;
+ }
+ else if (numerator < 0) {
+ sign = -1;
+ m_numerator = (uint64_t)-numerator;
+ }
+ else {
+ sign = 0;
+ m_numerator = 0;
+ }
+ if (denominator > 0) {
+ m_denominator = (uint64_t)denominator;
+ }
+ else if (denominator < 0) {
+ sign = -sign;
+ m_denominator = (uint64_t)-denominator;
+ }
+ else {
+ m_denominator = 0;
+ }
+ }
+
+ bool isNegativeInfinity() const
+ {
+ return (sign < 0) && (m_denominator == 0);
+ }
+
+ bool isNaN() const
+ {
+ return (sign == 0) && (m_denominator == 0);
+ }
+
+ int32_t compare(const Rational64& b) const;
+
+ btScalar toScalar() const
+ {
+ return sign * ((m_denominator == 0) ? SIMD_INFINITY : (btScalar)m_numerator / m_denominator);
+ }
+ };
+
+ class Rational128 {
+ private:
+ Int128 numerator;
+ Int128 denominator;
+ int32_t sign;
+ bool isInt64;
+
+ public:
+ Rational128(int64_t value)
+ {
+ if (value > 0) {
+ sign = 1;
+ this->numerator = value;
+ }
+ else if (value < 0) {
+ sign = -1;
+ this->numerator = -value;
+ }
+ else {
+ sign = 0;
+ this->numerator = (uint64_t)0;
+ }
+ this->denominator = (uint64_t)1;
+ isInt64 = true;
+ }
+
+ Rational128(const Int128& numerator, const Int128& denominator)
+ {
+ sign = numerator.getSign();
+ if (sign >= 0) {
+ this->numerator = numerator;
+ }
+ else {
+ this->numerator = -numerator;
+ }
+ int32_t dsign = denominator.getSign();
+ if (dsign >= 0) {
+ this->denominator = denominator;
+ }
+ else {
+ sign = -sign;
+ this->denominator = -denominator;
+ }
+ isInt64 = false;
+ }
+
+ int32_t compare(const Rational128& b) const;
+
+ int32_t compare(int64_t b) const;
+
+ btScalar toScalar() const
+ {
+ return sign * ((denominator.getSign() == 0) ? SIMD_INFINITY : numerator.toScalar() / denominator.toScalar());
+ }
+ };
+
+ class PointR128 {
+ public:
+ Int128 x;
+ Int128 y;
+ Int128 z;
+ Int128 denominator;
+
+ PointR128()
+ {
+ }
+
+ PointR128(Int128 x, Int128 y, Int128 z, Int128 denominator)
+ : x(x)
+ , y(y)
+ , z(z)
+ , denominator(denominator)
+ {
+ }
+
+ btScalar xvalue() const
+ {
+ return x.toScalar() / denominator.toScalar();
+ }
+
+ btScalar yvalue() const
+ {
+ return y.toScalar() / denominator.toScalar();
+ }
+
+ btScalar zvalue() const
+ {
+ return z.toScalar() / denominator.toScalar();
+ }
+ };
+
+ class Edge;
+ class Face;
+
+ class Vertex {
+ public:
+ Vertex* next;
+ Vertex* prev;
+ Edge* edges;
+ Face* firstNearbyFace;
+ Face* lastNearbyFace;
+ PointR128 point128;
+ Point32 point;
+ int32_t copy;
+
+ Vertex()
+ : next(NULL)
+ , prev(NULL)
+ , edges(NULL)
+ , firstNearbyFace(NULL)
+ , lastNearbyFace(NULL)
+ , copy(-1)
+ {
+ }
+
+#ifdef DEBUG_CONVEX_HULL
+ void print()
+ {
+ printf("V%d (%d, %d, %d)", point.index, point.x, point.y, point.z);
+ }
+
+ void printGraph();
+#endif
+
+ Point32 operator-(const Vertex& b) const
+ {
+ return point - b.point;
+ }
+
+ Rational128 dot(const Point64& b) const
+ {
+ return (point.index >= 0) ? Rational128(point.dot(b))
+ : Rational128(point128.x * b.x + point128.y * b.y + point128.z * b.z, point128.denominator);
+ }
+
+ btScalar xvalue() const
+ {
+ return (point.index >= 0) ? btScalar(point.x) : point128.xvalue();
+ }
+
+ btScalar yvalue() const
+ {
+ return (point.index >= 0) ? btScalar(point.y) : point128.yvalue();
+ }
+
+ btScalar zvalue() const
+ {
+ return (point.index >= 0) ? btScalar(point.z) : point128.zvalue();
+ }
+
+ void receiveNearbyFaces(Vertex* src)
+ {
+ if (lastNearbyFace) {
+ lastNearbyFace->nextWithSameNearbyVertex = src->firstNearbyFace;
+ }
+ else {
+ firstNearbyFace = src->firstNearbyFace;
+ }
+ if (src->lastNearbyFace) {
+ lastNearbyFace = src->lastNearbyFace;
+ }
+ for (Face* f = src->firstNearbyFace; f; f = f->nextWithSameNearbyVertex) {
+ btAssert(f->nearbyVertex == src);
+ f->nearbyVertex = this;
+ }
+ src->firstNearbyFace = NULL;
+ src->lastNearbyFace = NULL;
+ }
+ };
+
+ class Edge {
+ public:
+ Edge* next;
+ Edge* prev;
+ Edge* reverse;
+ Vertex* target;
+ Face* face;
+ int32_t copy;
+
+ ~Edge()
+ {
+ next = NULL;
+ prev = NULL;
+ reverse = NULL;
+ target = NULL;
+ face = NULL;
+ }
+
+ void link(Edge* n)
+ {
+ btAssert(reverse->target == n->reverse->target);
+ next = n;
+ n->prev = this;
+ }
+
+#ifdef DEBUG_CONVEX_HULL
+ void print()
+ {
+ printf("E%p : %d -> %d, n=%p p=%p (0 %d\t%d\t%d) -> (%d %d %d)", this, reverse->target->point.index, target->point.index, next, prev,
+ reverse->target->point.x, reverse->target->point.y, reverse->target->point.z, target->point.x, target->point.y, target->point.z);
+ }
+#endif
+ };
+
+ class Face {
+ public:
+ Face* next;
+ Vertex* nearbyVertex;
+ Face* nextWithSameNearbyVertex;
+ Point32 origin;
+ Point32 dir0;
+ Point32 dir1;
+
+ Face()
+ : next(NULL)
+ , nearbyVertex(NULL)
+ , nextWithSameNearbyVertex(NULL)
+ {
+ }
+
+ void init(Vertex* a, Vertex* b, Vertex* c)
+ {
+ nearbyVertex = a;
+ origin = a->point;
+ dir0 = *b - *a;
+ dir1 = *c - *a;
+ if (a->lastNearbyFace) {
+ a->lastNearbyFace->nextWithSameNearbyVertex = this;
+ }
+ else {
+ a->firstNearbyFace = this;
+ }
+ a->lastNearbyFace = this;
+ }
+
+ Point64 getNormal()
+ {
+ return dir0.cross(dir1);
+ }
+ };
+
+ template <typename UWord, typename UHWord>
+ 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 <typename T>
+ class PoolArray {
+ private:
+ T* array;
+ int32_t size;
+
+ public:
+ PoolArray<T>* next;
+
+ PoolArray(int32_t size)
+ : size(size)
+ , next(NULL)
+ {
+ array = (T*)btAlignedAlloc(sizeof(T) * size, 16);
+ }
+
+ ~PoolArray()
+ {
+ btAlignedFree(array);
+ }
+
+ T* init()
+ {
+ T* o = array;
+ for (int32_t i = 0; i < size; i++, o++) {
+ o->next = (i + 1 < size) ? o + 1 : NULL;
+ }
+ return array;
+ }
+ };
+
+ template <typename T>
+ class Pool {
+ private:
+ PoolArray<T>* arrays;
+ PoolArray<T>* nextArray;
+ T* freeObjects;
+ int32_t arraySize;
+
+ public:
+ Pool()
+ : arrays(NULL)
+ , nextArray(NULL)
+ , freeObjects(NULL)
+ , arraySize(256)
+ {
+ }
+
+ ~Pool()
+ {
+ while (arrays) {
+ PoolArray<T>* p = arrays;
+ arrays = p->next;
+ p->~PoolArray<T>();
+ btAlignedFree(p);
+ }
+ }
+
+ void reset()
+ {
+ nextArray = arrays;
+ freeObjects = NULL;
+ }
+
+ void setArraySize(int32_t arraySize)
+ {
+ this->arraySize = arraySize;
+ }
+
+ T* newObject()
+ {
+ T* o = freeObjects;
+ if (!o) {
+ PoolArray<T>* p = nextArray;
+ if (p) {
+ nextArray = p->next;
+ }
+ else {
+ p = new (btAlignedAlloc(sizeof(PoolArray<T>), 16)) PoolArray<T>(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<Vertex> vertexPool;
+ Pool<Edge> edgePool;
+ Pool<Face> facePool;
+ btAlignedObjectArray<Vertex*> originalVertices;
+ int32_t mergeStamp;
+ int32_t minAxis;
+ int32_t medAxis;
+ int32_t maxAxis;
+ int32_t usedEdgePairs;
+ int32_t maxUsedEdgePairs;
+
+ static Orientation getOrientation(const Edge* prev, const Edge* next, const Point32& s, const Point32& t);
+ Edge* findMaxAngle(bool ccw, const Vertex* start, const Point32& s, const Point64& rxs, const Point64& sxrxs, Rational64& minCot);
+ void findEdgeForCoplanarFaces(Vertex* c0, Vertex* c1, Edge*& e0, Edge*& e1, Vertex* stop0, Vertex* stop1);
+
+ Edge* newEdgePair(Vertex* from, Vertex* to);
+
+ void removeEdgePair(Edge* edge)
+ {
+ Edge* n = edge->next;
+ Edge* r = edge->reverse;
+
+ btAssert(edge->target && r->target);
+
+ if (n != edge) {
+ n->prev = edge->prev;
+ edge->prev->next = n;
+ r->target->edges = n;
+ }
+ else {
+ r->target->edges = NULL;
+ }
+
+ n = r->next;
+
+ if (n != r) {
+ n->prev = r->prev;
+ r->prev->next = n;
+ edge->target->edges = n;
+ }
+ else {
+ edge->target->edges = NULL;
+ }
+
+ edgePool.freeObject(edge);
+ edgePool.freeObject(r);
+ usedEdgePairs--;
+ }
+
+ void computeInternal(int32_t start, int32_t end, IntermediateHull& result);
+
+ bool mergeProjection(IntermediateHull& h0, IntermediateHull& h1, Vertex*& c0, Vertex*& c1);
+
+ void merge(IntermediateHull& h0, IntermediateHull& h1);
+
+ btVector3 toBtVector(const Point32& v);
+
+ btVector3 getBtNormal(Face* face);
+
+ bool shiftFace(Face* face, btScalar amount, btAlignedObjectArray<Vertex*> stack);
+
+public:
+ Vertex* vertexList;
+
+ void compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count);
+
+ btVector3 getCoordinates(const Vertex* v);
+
+ btScalar shrink(btScalar amount, btScalar clampAmount);
+};
+
+btConvexHullInternal::Int128 btConvexHullInternal::Int128::operator*(int64_t b) const
+{
+ bool negative = (int64_t)high < 0;
+ Int128 a = negative ? -*this : *this;
+ if (b < 0) {
+ negative = !negative;
+ b = -b;
+ }
+ Int128 result = mul(a.low, (uint64_t)b);
+ result.high += a.high * (uint64_t)b;
+ return negative ? -result : result;
+}
+
+btConvexHullInternal::Int128 btConvexHullInternal::Int128::mul(int64_t a, int64_t b)
+{
+ Int128 result;
+
+#ifdef USE_X86_64_ASM
+ __asm__("imulq %[b]"
+ : "=a"(result.low), "=d"(result.high)
+ : "0"(a), [b] "r"(b)
+ : "cc");
+ return result;
+
+#else
+ bool negative = a < 0;
+ if (negative) {
+ a = -a;
+ }
+ if (b < 0) {
+ negative = !negative;
+ b = -b;
+ }
+ DMul<uint64_t, uint32_t>::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<uint64_t, uint32_t>::mul(a, b, result.low, result.high);
+#endif
+
+ return result;
+}
+
+int32_t btConvexHullInternal::Rational64::compare(const Rational64& b) const
+{
+ if (sign != b.sign) {
+ return sign - b.sign;
+ }
+ else if (sign == 0) {
+ return 0;
+ }
+
+// return (numerator * b.denominator > b.numerator * denominator) ? sign : (numerator * b.denominator < b.numerator * denominator) ? -sign : 0;
+
+#ifdef USE_X86_64_ASM
+
+ int32_t result;
+ int64_t tmp;
+ int64_t dummy;
+ __asm__("mulq %[bn]\n\t"
+ "movq %%rax, %[tmp]\n\t"
+ "movq %%rdx, %%rbx\n\t"
+ "movq %[tn], %%rax\n\t"
+ "mulq %[bd]\n\t"
+ "subq %[tmp], %%rax\n\t"
+ "sbbq %%rbx, %%rdx\n\t" // rdx:rax contains 128-bit-difference "numerator*b.denominator - b.numerator*denominator"
+ "setnsb %%bh\n\t" // bh=1 if difference is non-negative, bh=0 otherwise
+ "orq %%rdx, %%rax\n\t"
+ "setnzb %%bl\n\t" // bl=1 if difference if non-zero, bl=0 if it is zero
+ "decb %%bh\n\t" // now bx=0x0000 if difference is zero, 0xff01 if it is negative, 0x0001 if it is positive (i.e., same sign as difference)
+ "shll $16, %%ebx\n\t" // ebx has same sign as difference
+ : "=&b"(result), [tmp] "=&r"(tmp), "=a"(dummy)
+ : "a"(denominator), [bn] "g"(b.numerator), [tn] "g"(numerator), [bd] "g"(b.denominator)
+ : "%rdx", "cc");
+ return result ? result ^ sign // if sign is +1, only bit 0 of result is inverted, which does not change the sign of result (and cannot result in zero)
+ // if sign is -1, all bits of result are inverted, which changes the sign of result (and again cannot result in zero)
+ : 0;
+
+#else
+
+ return sign * Int128::mul(m_numerator, b.m_denominator).ucmp(Int128::mul(m_denominator, b.m_numerator));
+
+#endif
+}
+
+int32_t btConvexHullInternal::Rational128::compare(const Rational128& b) const
+{
+ if (sign != b.sign) {
+ return sign - b.sign;
+ }
+ else if (sign == 0) {
+ return 0;
+ }
+ if (isInt64) {
+ return -b.compare(sign * (int64_t)numerator.low);
+ }
+
+ Int128 nbdLow, nbdHigh, dbnLow, dbnHigh;
+ DMul<Int128, uint64_t>::mul(numerator, b.denominator, nbdLow, nbdHigh);
+ DMul<Int128, uint64_t>::mul(denominator, b.numerator, dbnLow, dbnHigh);
+
+ int32_t cmp = nbdHigh.ucmp(dbnHigh);
+ if (cmp) {
+ return cmp * sign;
+ }
+ return nbdLow.ucmp(dbnLow) * sign;
+}
+
+int32_t btConvexHullInternal::Rational128::compare(int64_t b) const
+{
+ if (isInt64) {
+ int64_t a = sign * (int64_t)numerator.low;
+ return (a > b) ? 1 : (a < b) ? -1 : 0;
+ }
+ if (b > 0) {
+ if (sign <= 0) {
+ return -1;
+ }
+ }
+ else if (b < 0) {
+ if (sign >= 0) {
+ return 1;
+ }
+ b = -b;
+ }
+ else {
+ return sign;
+ }
+
+ return numerator.ucmp(denominator * b) * sign;
+}
+
+btConvexHullInternal::Edge* btConvexHullInternal::newEdgePair(Vertex* from, Vertex* to)
+{
+ btAssert(from && to);
+ Edge* e = edgePool.newObject();
+ Edge* r = edgePool.newObject();
+ e->reverse = r;
+ r->reverse = e;
+ e->copy = mergeStamp;
+ r->copy = mergeStamp;
+ e->target = to;
+ r->target = from;
+ e->face = NULL;
+ r->face = NULL;
+ usedEdgePairs++;
+ if (usedEdgePairs > maxUsedEdgePairs) {
+ maxUsedEdgePairs = usedEdgePairs;
+ }
+ return e;
+}
+
+bool btConvexHullInternal::mergeProjection(IntermediateHull& h0, IntermediateHull& h1, Vertex*& c0, Vertex*& c1)
+{
+ Vertex* v0 = h0.maxYx;
+ Vertex* v1 = h1.minYx;
+ if ((v0->point.x == v1->point.x) && (v0->point.y == v1->point.y)) {
+ btAssert(v0->point.z < v1->point.z);
+ Vertex* v1p = v1->prev;
+ if (v1p == v1) {
+ c0 = v0;
+ if (v1->edges) {
+ btAssert(v1->edges->next == v1->edges);
+ v1 = v1->edges->target;
+ btAssert(v1->edges->next == v1->edges);
+ }
+ c1 = v1;
+ return false;
+ }
+ Vertex* v1n = v1->next;
+ v1p->next = v1n;
+ v1n->prev = v1p;
+ if (v1 == h1.minXy) {
+ if ((v1n->point.x < v1p->point.x) || ((v1n->point.x == v1p->point.x) && (v1n->point.y < v1p->point.y))) {
+ h1.minXy = v1n;
+ }
+ else {
+ h1.minXy = v1p;
+ }
+ }
+ if (v1 == h1.maxXy) {
+ if ((v1n->point.x > v1p->point.x) || ((v1n->point.x == v1p->point.x) && (v1n->point.y > v1p->point.y))) {
+ h1.maxXy = v1n;
+ }
+ else {
+ h1.maxXy = v1p;
+ }
+ }
+ }
+
+ v0 = h0.maxXy;
+ v1 = h1.maxXy;
+ Vertex* v00 = NULL;
+ Vertex* v10 = NULL;
+ int32_t sign = 1;
+
+ for (int32_t side = 0; side <= 1; side++) {
+ int32_t dx = (v1->point.x - v0->point.x) * sign;
+ if (dx > 0) {
+ while (true) {
+ int32_t dy = v1->point.y - v0->point.y;
+
+ Vertex* w0 = side ? v0->next : v0->prev;
+ if (w0 != v0) {
+ int32_t dx0 = (w0->point.x - v0->point.x) * sign;
+ int32_t dy0 = w0->point.y - v0->point.y;
+ if ((dy0 <= 0) && ((dx0 == 0) || ((dx0 < 0) && (dy0 * dx <= dy * dx0)))) {
+ v0 = w0;
+ dx = (v1->point.x - v0->point.x) * sign;
+ continue;
+ }
+ }
+
+ Vertex* w1 = side ? v1->next : v1->prev;
+ if (w1 != v1) {
+ int32_t dx1 = (w1->point.x - v1->point.x) * sign;
+ int32_t dy1 = w1->point.y - v1->point.y;
+ int32_t dxn = (w1->point.x - v0->point.x) * sign;
+ if ((dxn > 0) && (dy1 < 0) && ((dx1 == 0) || ((dx1 < 0) && (dy1 * dx < dy * dx1)))) {
+ v1 = w1;
+ dx = dxn;
+ continue;
+ }
+ }
+
+ break;
+ }
+ }
+ else if (dx < 0) {
+ while (true) {
+ int32_t dy = v1->point.y - v0->point.y;
+
+ Vertex* w1 = side ? v1->prev : v1->next;
+ if (w1 != v1) {
+ int32_t dx1 = (w1->point.x - v1->point.x) * sign;
+ int32_t dy1 = w1->point.y - v1->point.y;
+ if ((dy1 >= 0) && ((dx1 == 0) || ((dx1 < 0) && (dy1 * dx <= dy * dx1)))) {
+ v1 = w1;
+ dx = (v1->point.x - v0->point.x) * sign;
+ continue;
+ }
+ }
+
+ Vertex* w0 = side ? v0->prev : v0->next;
+ if (w0 != v0) {
+ int32_t dx0 = (w0->point.x - v0->point.x) * sign;
+ int32_t dy0 = w0->point.y - v0->point.y;
+ int32_t dxn = (v1->point.x - w0->point.x) * sign;
+ if ((dxn < 0) && (dy0 > 0) && ((dx0 == 0) || ((dx0 < 0) && (dy0 * dx < dy * dx0)))) {
+ v0 = w0;
+ dx = dxn;
+ continue;
+ }
+ }
+
+ break;
+ }
+ }
+ else {
+ int32_t x = v0->point.x;
+ int32_t y0 = v0->point.y;
+ Vertex* w0 = v0;
+ Vertex* t;
+ while (((t = side ? w0->next : w0->prev) != v0) && (t->point.x == x) && (t->point.y <= y0)) {
+ w0 = t;
+ y0 = t->point.y;
+ }
+ v0 = w0;
+
+ int32_t y1 = v1->point.y;
+ Vertex* w1 = v1;
+ while (((t = side ? w1->prev : w1->next) != v1) && (t->point.x == x) && (t->point.y >= y1)) {
+ w1 = t;
+ y1 = t->point.y;
+ }
+ v1 = w1;
+ }
+
+ if (side == 0) {
+ v00 = v0;
+ v10 = v1;
+
+ v0 = h0.minXy;
+ v1 = h1.minXy;
+ sign = -1;
+ }
+ }
+
+ v0->prev = v1;
+ v1->next = v0;
+
+ v00->next = v10;
+ v10->prev = v00;
+
+ if (h1.minXy->point.x < h0.minXy->point.x) {
+ h0.minXy = h1.minXy;
+ }
+ if (h1.maxXy->point.x >= h0.maxXy->point.x) {
+ h0.maxXy = h1.maxXy;
+ }
+
+ h0.maxYx = h1.maxYx;
+
+ c0 = v00;
+ c1 = v10;
+
+ return true;
+}
+
+void btConvexHullInternal::computeInternal(int32_t start, int32_t end, IntermediateHull& result)
+{
+ int32_t n = end - start;
+ switch (n) {
+ case 0:
+ result.minXy = NULL;
+ result.maxXy = NULL;
+ result.minYx = NULL;
+ result.maxYx = NULL;
+ return;
+ case 2: {
+ Vertex* v = originalVertices[start];
+ Vertex* w = v + 1;
+ if (v->point != w->point) {
+ int32_t dx = v->point.x - w->point.x;
+ int32_t dy = v->point.y - w->point.y;
+
+ if ((dx == 0) && (dy == 0)) {
+ if (v->point.z > w->point.z) {
+ Vertex* t = w;
+ w = v;
+ v = t;
+ }
+ btAssert(v->point.z < w->point.z);
+ v->next = v;
+ v->prev = v;
+ result.minXy = v;
+ result.maxXy = v;
+ result.minYx = v;
+ result.maxYx = v;
+ }
+ else {
+ v->next = w;
+ v->prev = w;
+ w->next = v;
+ w->prev = v;
+
+ if ((dx < 0) || ((dx == 0) && (dy < 0))) {
+ result.minXy = v;
+ result.maxXy = w;
+ }
+ else {
+ result.minXy = w;
+ result.maxXy = v;
+ }
+
+ if ((dy < 0) || ((dy == 0) && (dx < 0))) {
+ result.minYx = v;
+ result.maxYx = w;
+ }
+ else {
+ result.minYx = w;
+ result.maxYx = v;
+ }
+ }
+
+ Edge* e = newEdgePair(v, w);
+ e->link(e);
+ v->edges = e;
+
+ e = e->reverse;
+ e->link(e);
+ w->edges = e;
+
+ return;
+ }
+ }
+ // lint -fallthrough
+ case 1: {
+ Vertex* v = originalVertices[start];
+ v->edges = NULL;
+ v->next = v;
+ v->prev = v;
+
+ result.minXy = v;
+ result.maxXy = v;
+ result.minYx = v;
+ result.maxYx = v;
+
+ return;
+ }
+ }
+
+ int32_t split0 = start + n / 2;
+ Point32 p = originalVertices[split0 - 1]->point;
+ int32_t split1 = split0;
+ while ((split1 < end) && (originalVertices[split1]->point == p)) {
+ split1++;
+ }
+ computeInternal(start, split0, result);
+ IntermediateHull hull1;
+ computeInternal(split1, end, hull1);
+#ifdef DEBUG_CONVEX_HULL
+ printf("\n\nMerge\n");
+ result.print();
+ hull1.print();
+#endif
+ merge(result, hull1);
+#ifdef DEBUG_CONVEX_HULL
+ printf("\n Result\n");
+ result.print();
+#endif
+}
+
+#ifdef DEBUG_CONVEX_HULL
+void btConvexHullInternal::IntermediateHull::print()
+{
+ printf(" Hull\n");
+ for (Vertex* v = minXy; v;) {
+ printf(" ");
+ v->print();
+ if (v == maxXy) {
+ printf(" maxXy");
+ }
+ if (v == minYx) {
+ printf(" minYx");
+ }
+ if (v == maxYx) {
+ printf(" maxYx");
+ }
+ if (v->next->prev != v) {
+ printf(" Inconsistency");
+ }
+ printf("\n");
+ v = v->next;
+ if (v == minXy) {
+ break;
+ }
+ }
+ if (minXy) {
+ minXy->copy = (minXy->copy == -1) ? -2 : -1;
+ minXy->printGraph();
+ }
+}
+
+void btConvexHullInternal::Vertex::printGraph()
+{
+ print();
+ printf("\nEdges\n");
+ Edge* e = edges;
+ if (e) {
+ do {
+ e->print();
+ printf("\n");
+ e = e->next;
+ } while (e != edges);
+ do {
+ Vertex* v = e->target;
+ if (v->copy != copy) {
+ v->copy = copy;
+ v->printGraph();
+ }
+ e = e->next;
+ } while (e != edges);
+ }
+}
+#endif
+
+btConvexHullInternal::Orientation btConvexHullInternal::getOrientation(const Edge* prev, const Edge* next, const Point32& s, const Point32& t)
+{
+ btAssert(prev->reverse->target == next->reverse->target);
+ if (prev->next == next) {
+ if (prev->prev == next) {
+ Point64 n = t.cross(s);
+ Point64 m = (*prev->target - *next->reverse->target).cross(*next->target - *next->reverse->target);
+ btAssert(!m.isZero());
+ int64_t dot = n.dot(m);
+ btAssert(dot != 0);
+ return (dot > 0) ? COUNTER_CLOCKWISE : CLOCKWISE;
+ }
+ return COUNTER_CLOCKWISE;
+ }
+ else if (prev->prev == next) {
+ return CLOCKWISE;
+ }
+ else {
+ return NONE;
+ }
+}
+
+btConvexHullInternal::Edge* btConvexHullInternal::findMaxAngle(bool ccw, const Vertex* start, const Point32& s, const Point64& rxs, const Point64& sxrxs, Rational64& minCot)
+{
+ Edge* minEdge = NULL;
+
+#ifdef DEBUG_CONVEX_HULL
+ printf("find max edge for %d\n", start->point.index);
+#endif
+ Edge* e = start->edges;
+ if (e) {
+ do {
+ if (e->copy > mergeStamp) {
+ Point32 t = *e->target - *start;
+ Rational64 cot(t.dot(sxrxs), t.dot(rxs));
+#ifdef DEBUG_CONVEX_HULL
+ printf(" Angle is %f (%d) for ", (float)btAtan(cot.toScalar()), (int32_t)cot.isNaN());
+ e->print();
+#endif
+ if (cot.isNaN()) {
+ btAssert(ccw ? (t.dot(s) < 0) : (t.dot(s) > 0));
+ }
+ else {
+ int32_t cmp;
+ if (minEdge == NULL) {
+ minCot = cot;
+ minEdge = e;
+ }
+ else if ((cmp = cot.compare(minCot)) < 0) {
+ minCot = cot;
+ minEdge = e;
+ }
+ else if ((cmp == 0) && (ccw == (getOrientation(minEdge, e, s, t) == COUNTER_CLOCKWISE))) {
+ minEdge = e;
+ }
+ }
+#ifdef DEBUG_CONVEX_HULL
+ printf("\n");
+#endif
+ }
+ e = e->next;
+ } while (e != start->edges);
+ }
+ return minEdge;
+}
+
+void btConvexHullInternal::findEdgeForCoplanarFaces(Vertex* c0, Vertex* c1, Edge*& e0, Edge*& e1, Vertex* stop0, Vertex* stop1)
+{
+ Edge* start0 = e0;
+ Edge* start1 = e1;
+ Point32 et0 = start0 ? start0->target->point : c0->point;
+ Point32 et1 = start1 ? start1->target->point : c1->point;
+ Point32 s = c1->point - c0->point;
+ Point64 normal = ((start0 ? start0 : start1)->target->point - c0->point).cross(s);
+ int64_t dist = c0->point.dot(normal);
+ btAssert(!start1 || (start1->target->point.dot(normal) == dist));
+ Point64 perp = s.cross(normal);
+ btAssert(!perp.isZero());
+
+#ifdef DEBUG_CONVEX_HULL
+ printf(" Advancing %d %d (%p %p, %d %d)\n", c0->point.index, c1->point.index, start0, start1, start0 ? start0->target->point.index : -1, start1 ? start1->target->point.index : -1);
+#endif
+
+ int64_t maxDot0 = et0.dot(perp);
+ if (e0) {
+ while (e0->target != stop0) {
+ Edge* e = e0->reverse->prev;
+ if (e->target->point.dot(normal) < dist) {
+ break;
+ }
+ btAssert(e->target->point.dot(normal) == dist);
+ if (e->copy == mergeStamp) {
+ break;
+ }
+ int64_t dot = e->target->point.dot(perp);
+ if (dot <= maxDot0) {
+ break;
+ }
+ maxDot0 = dot;
+ e0 = e;
+ et0 = e->target->point;
+ }
+ }
+
+ int64_t maxDot1 = et1.dot(perp);
+ if (e1) {
+ while (e1->target != stop1) {
+ Edge* e = e1->reverse->next;
+ if (e->target->point.dot(normal) < dist) {
+ break;
+ }
+ btAssert(e->target->point.dot(normal) == dist);
+ if (e->copy == mergeStamp) {
+ break;
+ }
+ int64_t dot = e->target->point.dot(perp);
+ if (dot <= maxDot1) {
+ break;
+ }
+ maxDot1 = dot;
+ e1 = e;
+ et1 = e->target->point;
+ }
+ }
+
+#ifdef DEBUG_CONVEX_HULL
+ printf(" Starting at %d %d\n", et0.index, et1.index);
+#endif
+
+ int64_t dx = maxDot1 - maxDot0;
+ if (dx > 0) {
+ while (true) {
+ int64_t dy = (et1 - et0).dot(s);
+
+ if (e0 && (e0->target != stop0)) {
+ Edge* f0 = e0->next->reverse;
+ if (f0->copy > mergeStamp) {
+ int64_t dx0 = (f0->target->point - et0).dot(perp);
+ int64_t dy0 = (f0->target->point - et0).dot(s);
+ if ((dx0 == 0) ? (dy0 < 0) : ((dx0 < 0) && (Rational64(dy0, dx0).compare(Rational64(dy, dx)) >= 0))) {
+ et0 = f0->target->point;
+ dx = (et1 - et0).dot(perp);
+ e0 = (e0 == start0) ? NULL : f0;
+ continue;
+ }
+ }
+ }
+
+ if (e1 && (e1->target != stop1)) {
+ Edge* f1 = e1->reverse->next;
+ if (f1->copy > mergeStamp) {
+ Point32 d1 = f1->target->point - et1;
+ if (d1.dot(normal) == 0) {
+ int64_t dx1 = d1.dot(perp);
+ int64_t dy1 = d1.dot(s);
+ int64_t dxn = (f1->target->point - et0).dot(perp);
+ if ((dxn > 0) && ((dx1 == 0) ? (dy1 < 0) : ((dx1 < 0) && (Rational64(dy1, dx1).compare(Rational64(dy, dx)) > 0)))) {
+ e1 = f1;
+ et1 = e1->target->point;
+ dx = dxn;
+ continue;
+ }
+ }
+ else {
+ btAssert((e1 == start1) && (d1.dot(normal) < 0));
+ }
+ }
+ }
+
+ break;
+ }
+ }
+ else if (dx < 0) {
+ while (true) {
+ int64_t dy = (et1 - et0).dot(s);
+
+ if (e1 && (e1->target != stop1)) {
+ Edge* f1 = e1->prev->reverse;
+ if (f1->copy > mergeStamp) {
+ int64_t dx1 = (f1->target->point - et1).dot(perp);
+ int64_t dy1 = (f1->target->point - et1).dot(s);
+ if ((dx1 == 0) ? (dy1 > 0) : ((dx1 < 0) && (Rational64(dy1, dx1).compare(Rational64(dy, dx)) <= 0))) {
+ et1 = f1->target->point;
+ dx = (et1 - et0).dot(perp);
+ e1 = (e1 == start1) ? NULL : f1;
+ continue;
+ }
+ }
+ }
+
+ if (e0 && (e0->target != stop0)) {
+ Edge* f0 = e0->reverse->prev;
+ if (f0->copy > mergeStamp) {
+ Point32 d0 = f0->target->point - et0;
+ if (d0.dot(normal) == 0) {
+ int64_t dx0 = d0.dot(perp);
+ int64_t dy0 = d0.dot(s);
+ int64_t dxn = (et1 - f0->target->point).dot(perp);
+ if ((dxn < 0) && ((dx0 == 0) ? (dy0 > 0) : ((dx0 < 0) && (Rational64(dy0, dx0).compare(Rational64(dy, dx)) < 0)))) {
+ e0 = f0;
+ et0 = e0->target->point;
+ dx = dxn;
+ continue;
+ }
+ }
+ else {
+ btAssert((e0 == start0) && (d0.dot(normal) < 0));
+ }
+ }
+ }
+
+ break;
+ }
+ }
+#ifdef DEBUG_CONVEX_HULL
+ printf(" Advanced edges to %d %d\n", et0.index, et1.index);
+#endif
+}
+
+void btConvexHullInternal::merge(IntermediateHull& h0, IntermediateHull& h1)
+{
+ if (!h1.maxXy) {
+ return;
+ }
+ if (!h0.maxXy) {
+ h0 = h1;
+ return;
+ }
+
+ mergeStamp--;
+
+ Vertex* c0 = NULL;
+ Edge* toPrev0 = NULL;
+ Edge* firstNew0 = NULL;
+ Edge* pendingHead0 = NULL;
+ Edge* pendingTail0 = NULL;
+ Vertex* c1 = NULL;
+ Edge* toPrev1 = NULL;
+ Edge* firstNew1 = NULL;
+ Edge* pendingHead1 = NULL;
+ Edge* pendingTail1 = NULL;
+ Point32 prevPoint;
+
+ if (mergeProjection(h0, h1, c0, c1)) {
+ Point32 s = *c1 - *c0;
+ Point64 normal = Point32(0, 0, -1).cross(s);
+ Point64 t = s.cross(normal);
+ btAssert(!t.isZero());
+
+ Edge* e = c0->edges;
+ Edge* start0 = NULL;
+ if (e) {
+ do {
+ int64_t dot = (*e->target - *c0).dot(normal);
+ btAssert(dot <= 0);
+ if ((dot == 0) && ((*e->target - *c0).dot(t) > 0)) {
+ if (!start0 || (getOrientation(start0, e, s, Point32(0, 0, -1)) == CLOCKWISE)) {
+ start0 = e;
+ }
+ }
+ e = e->next;
+ } while (e != c0->edges);
+ }
+
+ e = c1->edges;
+ Edge* start1 = NULL;
+ if (e) {
+ do {
+ int64_t dot = (*e->target - *c1).dot(normal);
+ btAssert(dot <= 0);
+ if ((dot == 0) && ((*e->target - *c1).dot(t) > 0)) {
+ if (!start1 || (getOrientation(start1, e, s, Point32(0, 0, -1)) == COUNTER_CLOCKWISE)) {
+ start1 = e;
+ }
+ }
+ e = e->next;
+ } while (e != c1->edges);
+ }
+
+ if (start0 || start1) {
+ findEdgeForCoplanarFaces(c0, c1, start0, start1, NULL, NULL);
+ if (start0) {
+ c0 = start0->target;
+ }
+ if (start1) {
+ c1 = start1->target;
+ }
+ }
+
+ prevPoint = c1->point;
+ prevPoint.z++;
+ }
+ else {
+ prevPoint = c1->point;
+ prevPoint.x++;
+ }
+
+ Vertex* first0 = c0;
+ Vertex* first1 = c1;
+ bool firstRun = true;
+
+ while (true) {
+ Point32 s = *c1 - *c0;
+ Point32 r = prevPoint - c0->point;
+ Point64 rxs = r.cross(s);
+ Point64 sxrxs = s.cross(rxs);
+
+#ifdef DEBUG_CONVEX_HULL
+ printf("\n Checking %d %d\n", c0->point.index, c1->point.index);
+#endif
+ Rational64 minCot0(0, 0);
+ Edge* min0 = findMaxAngle(false, c0, s, rxs, sxrxs, minCot0);
+ Rational64 minCot1(0, 0);
+ Edge* min1 = findMaxAngle(true, c1, s, rxs, sxrxs, minCot1);
+ if (!min0 && !min1) {
+ Edge* e = newEdgePair(c0, c1);
+ e->link(e);
+ c0->edges = e;
+
+ e = e->reverse;
+ e->link(e);
+ c1->edges = e;
+ return;
+ }
+ else {
+ int32_t cmp = !min0 ? 1 : !min1 ? -1 : minCot0.compare(minCot1);
+#ifdef DEBUG_CONVEX_HULL
+ printf(" -> Result %d\n", cmp);
+#endif
+ if (firstRun || ((cmp >= 0) ? !minCot1.isNegativeInfinity() : !minCot0.isNegativeInfinity())) {
+ Edge* e = newEdgePair(c0, c1);
+ if (pendingTail0) {
+ pendingTail0->prev = e;
+ }
+ else {
+ pendingHead0 = e;
+ }
+ e->next = pendingTail0;
+ pendingTail0 = e;
+
+ e = e->reverse;
+ if (pendingTail1) {
+ pendingTail1->next = e;
+ }
+ else {
+ pendingHead1 = e;
+ }
+ e->prev = pendingTail1;
+ pendingTail1 = e;
+ }
+
+ Edge* e0 = min0;
+ Edge* e1 = min1;
+
+#ifdef DEBUG_CONVEX_HULL
+ printf(" Found min edges to %d %d\n", e0 ? e0->target->point.index : -1, e1 ? e1->target->point.index : -1);
+#endif
+
+ if (cmp == 0) {
+ findEdgeForCoplanarFaces(c0, c1, e0, e1, NULL, NULL);
+ }
+
+ if ((cmp >= 0) && e1) {
+ if (toPrev1) {
+ for (Edge *e = toPrev1->next, *n = NULL; e != min1; e = n) {
+ n = e->next;
+ removeEdgePair(e);
+ }
+ }
+
+ if (pendingTail1) {
+ if (toPrev1) {
+ toPrev1->link(pendingHead1);
+ }
+ else {
+ min1->prev->link(pendingHead1);
+ firstNew1 = pendingHead1;
+ }
+ pendingTail1->link(min1);
+ pendingHead1 = NULL;
+ pendingTail1 = NULL;
+ }
+ else if (!toPrev1) {
+ firstNew1 = min1;
+ }
+
+ prevPoint = c1->point;
+ c1 = e1->target;
+ toPrev1 = e1->reverse;
+ }
+
+ if ((cmp <= 0) && e0) {
+ if (toPrev0) {
+ for (Edge *e = toPrev0->prev, *n = NULL; e != min0; e = n) {
+ n = e->prev;
+ removeEdgePair(e);
+ }
+ }
+
+ if (pendingTail0) {
+ if (toPrev0) {
+ pendingHead0->link(toPrev0);
+ }
+ else {
+ pendingHead0->link(min0->next);
+ firstNew0 = pendingHead0;
+ }
+ min0->link(pendingTail0);
+ pendingHead0 = NULL;
+ pendingTail0 = NULL;
+ }
+ else if (!toPrev0) {
+ firstNew0 = min0;
+ }
+
+ prevPoint = c0->point;
+ c0 = e0->target;
+ toPrev0 = e0->reverse;
+ }
+ }
+
+ if ((c0 == first0) && (c1 == first1)) {
+ if (toPrev0 == NULL) {
+ pendingHead0->link(pendingTail0);
+ c0->edges = pendingTail0;
+ }
+ else {
+ for (Edge *e = toPrev0->prev, *n = NULL; e != firstNew0; e = n) {
+ n = e->prev;
+ removeEdgePair(e);
+ }
+ if (pendingTail0) {
+ pendingHead0->link(toPrev0);
+ firstNew0->link(pendingTail0);
+ }
+ }
+
+ if (toPrev1 == NULL) {
+ pendingTail1->link(pendingHead1);
+ c1->edges = pendingTail1;
+ }
+ else {
+ for (Edge *e = toPrev1->next, *n = NULL; e != firstNew1; e = n) {
+ n = e->next;
+ removeEdgePair(e);
+ }
+ if (pendingTail1) {
+ toPrev1->link(pendingHead1);
+ pendingTail1->link(firstNew1);
+ }
+ }
+
+ return;
+ }
+
+ firstRun = false;
+ }
+}
+
+static bool pointCmp(const btConvexHullInternal::Point32& p, const btConvexHullInternal::Point32& q)
+{
+ return (p.y < q.y) || ((p.y == q.y) && ((p.x < q.x) || ((p.x == q.x) && (p.z < q.z))));
+}
+
+void btConvexHullInternal::compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count)
+{
+ btVector3 min(btScalar(1e30), btScalar(1e30), btScalar(1e30)), max(btScalar(-1e30), btScalar(-1e30), btScalar(-1e30));
+ const char* ptr = (const char*)coords;
+ if (doubleCoords) {
+ for (int32_t i = 0; i < count; i++) {
+ const double* v = (const double*)ptr;
+ btVector3 p((btScalar)v[0], (btScalar)v[1], (btScalar)v[2]);
+ ptr += stride;
+ min.setMin(p);
+ max.setMax(p);
+ }
+ }
+ else {
+ for (int32_t i = 0; i < count; i++) {
+ const float* v = (const float*)ptr;
+ btVector3 p(v[0], v[1], v[2]);
+ ptr += stride;
+ min.setMin(p);
+ max.setMax(p);
+ }
+ }
+
+ btVector3 s = max - min;
+ maxAxis = s.maxAxis();
+ minAxis = s.minAxis();
+ if (minAxis == maxAxis) {
+ minAxis = (maxAxis + 1) % 3;
+ }
+ medAxis = 3 - maxAxis - minAxis;
+
+ s /= btScalar(10216);
+ if (((medAxis + 1) % 3) != maxAxis) {
+ s *= -1;
+ }
+ scaling = s;
+
+ if (s[0] != 0) {
+ s[0] = btScalar(1) / s[0];
+ }
+ if (s[1] != 0) {
+ s[1] = btScalar(1) / s[1];
+ }
+ if (s[2] != 0) {
+ s[2] = btScalar(1) / s[2];
+ }
+
+ center = (min + max) * btScalar(0.5);
+
+ btAlignedObjectArray<Point32> points;
+ points.resize(count);
+ ptr = (const char*)coords;
+ if (doubleCoords) {
+ for (int32_t i = 0; i < count; i++) {
+ const double* v = (const double*)ptr;
+ btVector3 p((btScalar)v[0], (btScalar)v[1], (btScalar)v[2]);
+ ptr += stride;
+ p = (p - center) * s;
+ points[i].x = (int32_t)p[medAxis];
+ points[i].y = (int32_t)p[maxAxis];
+ points[i].z = (int32_t)p[minAxis];
+ points[i].index = i;
+ }
+ }
+ else {
+ for (int32_t i = 0; i < count; i++) {
+ const float* v = (const float*)ptr;
+ btVector3 p(v[0], v[1], v[2]);
+ ptr += stride;
+ p = (p - center) * s;
+ points[i].x = (int32_t)p[medAxis];
+ points[i].y = (int32_t)p[maxAxis];
+ points[i].z = (int32_t)p[minAxis];
+ points[i].index = i;
+ }
+ }
+ points.quickSort(pointCmp);
+
+ vertexPool.reset();
+ vertexPool.setArraySize(count);
+ originalVertices.resize(count);
+ for (int32_t i = 0; i < count; i++) {
+ Vertex* v = vertexPool.newObject();
+ v->edges = NULL;
+ v->point = points[i];
+ v->copy = -1;
+ originalVertices[i] = v;
+ }
+
+ points.clear();
+
+ edgePool.reset();
+ edgePool.setArraySize(6 * count);
+
+ usedEdgePairs = 0;
+ maxUsedEdgePairs = 0;
+
+ mergeStamp = -3;
+
+ IntermediateHull hull;
+ computeInternal(0, count, hull);
+ vertexList = hull.minXy;
+#ifdef DEBUG_CONVEX_HULL
+ printf("max. edges %d (3v = %d)", maxUsedEdgePairs, 3 * count);
+#endif
+}
+
+btVector3 btConvexHullInternal::toBtVector(const Point32& v)
+{
+ btVector3 p;
+ p[medAxis] = btScalar(v.x);
+ p[maxAxis] = btScalar(v.y);
+ p[minAxis] = btScalar(v.z);
+ return p * scaling;
+}
+
+btVector3 btConvexHullInternal::getBtNormal(Face* face)
+{
+ return toBtVector(face->dir0).cross(toBtVector(face->dir1)).normalized();
+}
+
+btVector3 btConvexHullInternal::getCoordinates(const Vertex* v)
+{
+ btVector3 p;
+ p[medAxis] = v->xvalue();
+ p[maxAxis] = v->yvalue();
+ p[minAxis] = v->zvalue();
+ return p * scaling + center;
+}
+
+btScalar btConvexHullInternal::shrink(btScalar amount, btScalar clampAmount)
+{
+ if (!vertexList) {
+ return 0;
+ }
+ int32_t stamp = --mergeStamp;
+ btAlignedObjectArray<Vertex*> stack;
+ vertexList->copy = stamp;
+ stack.push_back(vertexList);
+ btAlignedObjectArray<Face*> faces;
+
+ Point32 ref = vertexList->point;
+ Int128 hullCenterX(0, 0);
+ Int128 hullCenterY(0, 0);
+ Int128 hullCenterZ(0, 0);
+ Int128 volume(0, 0);
+
+ while (stack.size() > 0) {
+ Vertex* v = stack[stack.size() - 1];
+ stack.pop_back();
+ Edge* e = v->edges;
+ if (e) {
+ do {
+ if (e->target->copy != stamp) {
+ e->target->copy = stamp;
+ stack.push_back(e->target);
+ }
+ if (e->copy != stamp) {
+ Face* face = facePool.newObject();
+ face->init(e->target, e->reverse->prev->target, v);
+ faces.push_back(face);
+ Edge* f = e;
+
+ Vertex* a = NULL;
+ Vertex* b = NULL;
+ do {
+ if (a && b) {
+ int64_t vol = (v->point - ref).dot((a->point - ref).cross(b->point - ref));
+ btAssert(vol >= 0);
+ Point32 c = v->point + a->point + b->point + ref;
+ hullCenterX += vol * c.x;
+ hullCenterY += vol * c.y;
+ hullCenterZ += vol * c.z;
+ volume += vol;
+ }
+
+ btAssert(f->copy != stamp);
+ f->copy = stamp;
+ f->face = face;
+
+ a = b;
+ b = f->target;
+
+ f = f->reverse->prev;
+ } while (f != e);
+ }
+ e = e->next;
+ } while (e != v->edges);
+ }
+ }
+
+ if (volume.getSign() <= 0) {
+ return 0;
+ }
+
+ btVector3 hullCenter;
+ hullCenter[medAxis] = hullCenterX.toScalar();
+ hullCenter[maxAxis] = hullCenterY.toScalar();
+ hullCenter[minAxis] = hullCenterZ.toScalar();
+ hullCenter /= 4 * volume.toScalar();
+ hullCenter *= scaling;
+
+ int32_t faceCount = faces.size();
+
+ if (clampAmount > 0) {
+ btScalar minDist = SIMD_INFINITY;
+ for (int32_t i = 0; i < faceCount; i++) {
+ btVector3 normal = getBtNormal(faces[i]);
+ btScalar dist = normal.dot(toBtVector(faces[i]->origin) - hullCenter);
+ if (dist < minDist) {
+ minDist = dist;
+ }
+ }
+
+ if (minDist <= 0) {
+ return 0;
+ }
+
+ amount = btMin(amount, minDist * clampAmount);
+ }
+
+ uint32_t seed = 243703;
+ for (int32_t i = 0; i < faceCount; i++, seed = 1664525 * seed + 1013904223) {
+ btSwap(faces[i], faces[seed % faceCount]);
+ }
+
+ for (int32_t i = 0; i < faceCount; i++) {
+ if (!shiftFace(faces[i], amount, stack)) {
+ return -amount;
+ }
+ }
+
+ return amount;
+}
+
+bool btConvexHullInternal::shiftFace(Face* face, btScalar amount, btAlignedObjectArray<Vertex*> stack)
+{
+ btVector3 origShift = getBtNormal(face) * -amount;
+ if (scaling[0] != 0) {
+ origShift[0] /= scaling[0];
+ }
+ if (scaling[1] != 0) {
+ origShift[1] /= scaling[1];
+ }
+ if (scaling[2] != 0) {
+ origShift[2] /= scaling[2];
+ }
+ Point32 shift((int32_t)origShift[medAxis], (int32_t)origShift[maxAxis], (int32_t)origShift[minAxis]);
+ if (shift.isZero()) {
+ return true;
+ }
+ Point64 normal = face->getNormal();
+#ifdef DEBUG_CONVEX_HULL
+ printf("\nShrinking face (%d %d %d) (%d %d %d) (%d %d %d) by (%d %d %d)\n",
+ face->origin.x, face->origin.y, face->origin.z, face->dir0.x, face->dir0.y, face->dir0.z, face->dir1.x, face->dir1.y, face->dir1.z, shift.x, shift.y, shift.z);
+#endif
+ int64_t origDot = face->origin.dot(normal);
+ Point32 shiftedOrigin = face->origin + shift;
+ int64_t shiftedDot = shiftedOrigin.dot(normal);
+ btAssert(shiftedDot <= origDot);
+ if (shiftedDot >= origDot) {
+ return false;
+ }
+
+ Edge* intersection = NULL;
+
+ Edge* startEdge = face->nearbyVertex->edges;
+#ifdef DEBUG_CONVEX_HULL
+ printf("Start edge is ");
+ startEdge->print();
+ printf(", normal is (%lld %lld %lld), shifted dot is %lld\n", normal.x, normal.y, normal.z, shiftedDot);
+#endif
+ Rational128 optDot = face->nearbyVertex->dot(normal);
+ int32_t cmp = optDot.compare(shiftedDot);
+#ifdef SHOW_ITERATIONS
+ int32_t n = 0;
+#endif
+ if (cmp >= 0) {
+ Edge* e = startEdge;
+ do {
+#ifdef SHOW_ITERATIONS
+ n++;
+#endif
+ Rational128 dot = e->target->dot(normal);
+ btAssert(dot.compare(origDot) <= 0);
+#ifdef DEBUG_CONVEX_HULL
+ printf("Moving downwards, edge is ");
+ e->print();
+ printf(", dot is %f (%f %lld)\n", (float)dot.toScalar(), (float)optDot.toScalar(), shiftedDot);
+#endif
+ if (dot.compare(optDot) < 0) {
+ int32_t c = dot.compare(shiftedDot);
+ optDot = dot;
+ e = e->reverse;
+ startEdge = e;
+ if (c < 0) {
+ intersection = e;
+ break;
+ }
+ cmp = c;
+ }
+ e = e->prev;
+ } while (e != startEdge);
+
+ if (!intersection) {
+ return false;
+ }
+ }
+ else {
+ Edge* e = startEdge;
+ do {
+#ifdef SHOW_ITERATIONS
+ n++;
+#endif
+ Rational128 dot = e->target->dot(normal);
+ btAssert(dot.compare(origDot) <= 0);
+#ifdef DEBUG_CONVEX_HULL
+ printf("Moving upwards, edge is ");
+ e->print();
+ printf(", dot is %f (%f %lld)\n", (float)dot.toScalar(), (float)optDot.toScalar(), shiftedDot);
+#endif
+ if (dot.compare(optDot) > 0) {
+ cmp = dot.compare(shiftedDot);
+ if (cmp >= 0) {
+ intersection = e;
+ break;
+ }
+ optDot = dot;
+ e = e->reverse;
+ startEdge = e;
+ }
+ e = e->prev;
+ } while (e != startEdge);
+
+ if (!intersection) {
+ return true;
+ }
+ }
+
+#ifdef SHOW_ITERATIONS
+ printf("Needed %d iterations to find initial intersection\n", n);
+#endif
+
+ if (cmp == 0) {
+ Edge* e = intersection->reverse->next;
+#ifdef SHOW_ITERATIONS
+ n = 0;
+#endif
+ while (e->target->dot(normal).compare(shiftedDot) <= 0) {
+#ifdef SHOW_ITERATIONS
+ n++;
+#endif
+ e = e->next;
+ if (e == intersection->reverse) {
+ return true;
+ }
+#ifdef DEBUG_CONVEX_HULL
+ printf("Checking for outwards edge, current edge is ");
+ e->print();
+ printf("\n");
+#endif
+ }
+#ifdef SHOW_ITERATIONS
+ printf("Needed %d iterations to check for complete containment\n", n);
+#endif
+ }
+
+ Edge* firstIntersection = NULL;
+ Edge* faceEdge = NULL;
+ Edge* firstFaceEdge = NULL;
+
+#ifdef SHOW_ITERATIONS
+ int32_t m = 0;
+#endif
+ while (true) {
+#ifdef SHOW_ITERATIONS
+ m++;
+#endif
+#ifdef DEBUG_CONVEX_HULL
+ printf("Intersecting edge is ");
+ intersection->print();
+ printf("\n");
+#endif
+ if (cmp == 0) {
+ Edge* e = intersection->reverse->next;
+ startEdge = e;
+#ifdef SHOW_ITERATIONS
+ n = 0;
+#endif
+ while (true) {
+#ifdef SHOW_ITERATIONS
+ n++;
+#endif
+ if (e->target->dot(normal).compare(shiftedDot) >= 0) {
+ break;
+ }
+ intersection = e->reverse;
+ e = e->next;
+ if (e == startEdge) {
+ return true;
+ }
+ }
+#ifdef SHOW_ITERATIONS
+ printf("Needed %d iterations to advance intersection\n", n);
+#endif
+ }
+
+#ifdef DEBUG_CONVEX_HULL
+ printf("Advanced intersecting edge to ");
+ intersection->print();
+ printf(", cmp = %d\n", cmp);
+#endif
+
+ if (!firstIntersection) {
+ firstIntersection = intersection;
+ }
+ else if (intersection == firstIntersection) {
+ break;
+ }
+
+ int32_t prevCmp = cmp;
+ Edge* prevIntersection = intersection;
+ Edge* prevFaceEdge = faceEdge;
+
+ Edge* e = intersection->reverse;
+#ifdef SHOW_ITERATIONS
+ n = 0;
+#endif
+ while (true) {
+#ifdef SHOW_ITERATIONS
+ n++;
+#endif
+ e = e->reverse->prev;
+ btAssert(e != intersection->reverse);
+ cmp = e->target->dot(normal).compare(shiftedDot);
+#ifdef DEBUG_CONVEX_HULL
+ printf("Testing edge ");
+ e->print();
+ printf(" -> cmp = %d\n", cmp);
+#endif
+ if (cmp >= 0) {
+ intersection = e;
+ break;
+ }
+ }
+#ifdef SHOW_ITERATIONS
+ printf("Needed %d iterations to find other intersection of face\n", n);
+#endif
+
+ if (cmp > 0) {
+ Vertex* removed = intersection->target;
+ e = intersection->reverse;
+ if (e->prev == e) {
+ removed->edges = NULL;
+ }
+ else {
+ removed->edges = e->prev;
+ e->prev->link(e->next);
+ e->link(e);
+ }
+#ifdef DEBUG_CONVEX_HULL
+ printf("1: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z);
+#endif
+
+ Point64 n0 = intersection->face->getNormal();
+ Point64 n1 = intersection->reverse->face->getNormal();
+ int64_t m00 = face->dir0.dot(n0);
+ int64_t m01 = face->dir1.dot(n0);
+ int64_t m10 = face->dir0.dot(n1);
+ int64_t m11 = face->dir1.dot(n1);
+ int64_t r0 = (intersection->face->origin - shiftedOrigin).dot(n0);
+ int64_t r1 = (intersection->reverse->face->origin - shiftedOrigin).dot(n1);
+ Int128 det = Int128::mul(m00, m11) - Int128::mul(m01, m10);
+ btAssert(det.getSign() != 0);
+ Vertex* v = vertexPool.newObject();
+ v->point.index = -1;
+ v->copy = -1;
+ v->point128 = PointR128(Int128::mul(face->dir0.x * r0, m11) - Int128::mul(face->dir0.x * r1, m01)
+ + Int128::mul(face->dir1.x * r1, m00) - Int128::mul(face->dir1.x * r0, m10) + det * shiftedOrigin.x,
+ Int128::mul(face->dir0.y * r0, m11) - Int128::mul(face->dir0.y * r1, m01)
+ + Int128::mul(face->dir1.y * r1, m00) - Int128::mul(face->dir1.y * r0, m10) + det * shiftedOrigin.y,
+ Int128::mul(face->dir0.z * r0, m11) - Int128::mul(face->dir0.z * r1, m01)
+ + Int128::mul(face->dir1.z * r1, m00) - Int128::mul(face->dir1.z * r0, m10) + det * shiftedOrigin.z,
+ det);
+ v->point.x = (int32_t)v->point128.xvalue();
+ v->point.y = (int32_t)v->point128.yvalue();
+ v->point.z = (int32_t)v->point128.zvalue();
+ intersection->target = v;
+ v->edges = e;
+
+ stack.push_back(v);
+ stack.push_back(removed);
+ stack.push_back(NULL);
+ }
+
+ if (cmp || prevCmp || (prevIntersection->reverse->next->target != intersection->target)) {
+ faceEdge = newEdgePair(prevIntersection->target, intersection->target);
+ if (prevCmp == 0) {
+ faceEdge->link(prevIntersection->reverse->next);
+ }
+ if ((prevCmp == 0) || prevFaceEdge) {
+ prevIntersection->reverse->link(faceEdge);
+ }
+ if (cmp == 0) {
+ intersection->reverse->prev->link(faceEdge->reverse);
+ }
+ faceEdge->reverse->link(intersection->reverse);
+ }
+ else {
+ faceEdge = prevIntersection->reverse->next;
+ }
+
+ if (prevFaceEdge) {
+ if (prevCmp > 0) {
+ faceEdge->link(prevFaceEdge->reverse);
+ }
+ else if (faceEdge != prevFaceEdge->reverse) {
+ stack.push_back(prevFaceEdge->target);
+ while (faceEdge->next != prevFaceEdge->reverse) {
+ Vertex* removed = faceEdge->next->target;
+ removeEdgePair(faceEdge->next);
+ stack.push_back(removed);
+#ifdef DEBUG_CONVEX_HULL
+ printf("2: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z);
+#endif
+ }
+ stack.push_back(NULL);
+ }
+ }
+ faceEdge->face = face;
+ faceEdge->reverse->face = intersection->face;
+
+ if (!firstFaceEdge) {
+ firstFaceEdge = faceEdge;
+ }
+ }
+#ifdef SHOW_ITERATIONS
+ printf("Needed %d iterations to process all intersections\n", m);
+#endif
+
+ if (cmp > 0) {
+ firstFaceEdge->reverse->target = faceEdge->target;
+ firstIntersection->reverse->link(firstFaceEdge);
+ firstFaceEdge->link(faceEdge->reverse);
+ }
+ else if (firstFaceEdge != faceEdge->reverse) {
+ stack.push_back(faceEdge->target);
+ while (firstFaceEdge->next != faceEdge->reverse) {
+ Vertex* removed = firstFaceEdge->next->target;
+ removeEdgePair(firstFaceEdge->next);
+ stack.push_back(removed);
+#ifdef DEBUG_CONVEX_HULL
+ printf("3: Removed part contains (%d %d %d)\n", removed->point.x, removed->point.y, removed->point.z);
+#endif
+ }
+ stack.push_back(NULL);
+ }
+
+ btAssert(stack.size() > 0);
+ vertexList = stack[0];
+
+#ifdef DEBUG_CONVEX_HULL
+ printf("Removing part\n");
+#endif
+#ifdef SHOW_ITERATIONS
+ n = 0;
+#endif
+ int32_t pos = 0;
+ while (pos < stack.size()) {
+ int32_t end = stack.size();
+ while (pos < end) {
+ Vertex* kept = stack[pos++];
+#ifdef DEBUG_CONVEX_HULL
+ kept->print();
+#endif
+ bool deeper = false;
+ Vertex* removed;
+ while ((removed = stack[pos++]) != NULL) {
+#ifdef SHOW_ITERATIONS
+ n++;
+#endif
+ kept->receiveNearbyFaces(removed);
+ while (removed->edges) {
+ if (!deeper) {
+ deeper = true;
+ stack.push_back(kept);
+ }
+ stack.push_back(removed->edges->target);
+ removeEdgePair(removed->edges);
+ }
+ }
+ if (deeper) {
+ stack.push_back(NULL);
+ }
+ }
+ }
+#ifdef SHOW_ITERATIONS
+ printf("Needed %d iterations to remove part\n", n);
+#endif
+
+ stack.resize(0);
+ face->origin = shiftedOrigin;
+
+ return true;
+}
+
+static int32_t getVertexCopy(btConvexHullInternal::Vertex* vertex, btAlignedObjectArray<btConvexHullInternal::Vertex*>& vertices)
+{
+ int32_t index = vertex->copy;
+ if (index < 0) {
+ index = vertices.size();
+ vertex->copy = index;
+ vertices.push_back(vertex);
+#ifdef DEBUG_CONVEX_HULL
+ printf("Vertex %d gets index *%d\n", vertex->point.index, index);
+#endif
+ }
+ return index;
+}
+
+btScalar btConvexHullComputer::compute(const void* coords, bool doubleCoords, int32_t stride, int32_t count, btScalar shrink, btScalar shrinkClamp)
+{
+ if (count <= 0) {
+ vertices.clear();
+ edges.clear();
+ faces.clear();
+ return 0;
+ }
+
+ btConvexHullInternal hull;
+ hull.compute(coords, doubleCoords, stride, count);
+
+ btScalar shift = 0;
+ if ((shrink > 0) && ((shift = hull.shrink(shrink, shrinkClamp)) < 0)) {
+ vertices.clear();
+ edges.clear();
+ faces.clear();
+ return shift;
+ }
+
+ vertices.resize(0);
+ edges.resize(0);
+ faces.resize(0);
+
+ btAlignedObjectArray<btConvexHullInternal::Vertex*> oldVertices;
+ getVertexCopy(hull.vertexList, oldVertices);
+ int32_t copied = 0;
+ while (copied < oldVertices.size()) {
+ btConvexHullInternal::Vertex* v = oldVertices[copied];
+ vertices.push_back(hull.getCoordinates(v));
+ btConvexHullInternal::Edge* firstEdge = v->edges;
+ if (firstEdge) {
+ int32_t firstCopy = -1;
+ int32_t prevCopy = -1;
+ btConvexHullInternal::Edge* e = firstEdge;
+ do {
+ if (e->copy < 0) {
+ int32_t s = edges.size();
+ edges.push_back(Edge());
+ edges.push_back(Edge());
+ Edge* c = &edges[s];
+ Edge* r = &edges[s + 1];
+ e->copy = s;
+ e->reverse->copy = s + 1;
+ c->reverse = 1;
+ r->reverse = -1;
+ c->targetVertex = getVertexCopy(e->target, oldVertices);
+ r->targetVertex = copied;
+#ifdef DEBUG_CONVEX_HULL
+ printf(" CREATE: Vertex *%d has edge to *%d\n", copied, c->getTargetVertex());
+#endif
+ }
+ if (prevCopy >= 0) {
+ edges[e->copy].next = prevCopy - e->copy;
+ }
+ else {
+ firstCopy = e->copy;
+ }
+ prevCopy = e->copy;
+ e = e->next;
+ } while (e != firstEdge);
+ edges[firstCopy].next = prevCopy - firstCopy;
+ }
+ copied++;
+ }
+
+ for (int32_t i = 0; i < copied; i++) {
+ btConvexHullInternal::Vertex* v = oldVertices[i];
+ btConvexHullInternal::Edge* firstEdge = v->edges;
+ if (firstEdge) {
+ btConvexHullInternal::Edge* e = firstEdge;
+ do {
+ if (e->copy >= 0) {
+#ifdef DEBUG_CONVEX_HULL
+ printf("Vertex *%d has edge to *%d\n", i, edges[e->copy].getTargetVertex());
+#endif
+ faces.push_back(e->copy);
+ btConvexHullInternal::Edge* f = e;
+ do {
+#ifdef DEBUG_CONVEX_HULL
+ printf(" Face *%d\n", edges[f->copy].getTargetVertex());
+#endif
+ f->copy = -1;
+ f = f->reverse->prev;
+ } while (f != e);
+ }
+ e = e->next;
+ } while (e != firstEdge);
+ }
+ }
+
+ return shift;
+}
diff --git a/sdk/extensions/authoring/source/VHACD/src/vhacdICHull.cpp b/sdk/extensions/authoring/source/VHACD/src/vhacdICHull.cpp
new file mode 100644
index 0000000..989587c
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/src/vhacdICHull.cpp
@@ -0,0 +1,731 @@
+/* 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 <limits>
+
+#ifdef _MSC_VER
+#pragma warning(disable:4456 4706)
+#endif
+
+
+namespace VHACD {
+const double ICHull::sc_eps = 1.0e-15;
+const int32_t ICHull::sc_dummyIndex = std::numeric_limits<int32_t>::max();
+ICHull::ICHull()
+{
+ m_isFlat = false;
+}
+bool ICHull::AddPoints(const Vec3<double>* points, size_t nPoints)
+{
+ if (!points) {
+ return false;
+ }
+ CircularListElement<TMMVertex>* 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<int32_t>(i);
+ }
+ return true;
+}
+bool ICHull::AddPoint(const Vec3<double>& point, int32_t id)
+{
+ if (AddPoints(&point, 1)) {
+ m_mesh.m_vertices.GetData().m_name = id;
+ return true;
+ }
+ return false;
+}
+
+ICHullError ICHull::Process()
+{
+ uint32_t addedPoints = 0;
+ if (m_mesh.GetNVertices() < 3) {
+ return ICHullErrorNotEnoughPoints;
+ }
+ if (m_mesh.GetNVertices() == 3) {
+ m_isFlat = true;
+ CircularListElement<TMMTriangle>* t1 = m_mesh.AddTriangle();
+ CircularListElement<TMMTriangle>* t2 = m_mesh.AddTriangle();
+ CircularListElement<TMMVertex>* v0 = m_mesh.m_vertices.GetHead();
+ CircularListElement<TMMVertex>* v1 = v0->GetNext();
+ CircularListElement<TMMVertex>* v2 = v1->GetNext();
+ // Compute the normal to the plane
+ Vec3<double> p0 = v0->GetData().m_pos;
+ Vec3<double> p1 = v1->GetData().m_pos;
+ Vec3<double> 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<TMMVertex>& 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<TMMVertex>& 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<CircularListElement<TMMTriangle>*> trianglesToDuplicate;
+ size_t nT = m_mesh.GetNTriangles();
+ for (size_t f = 0; f < nT; f++) {
+ TMMTriangle& currentTriangle = m_mesh.m_triangles.GetHead()->GetData();
+ if (currentTriangle.m_vertices[0]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[1]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[2]->GetData().m_name == sc_dummyIndex) {
+ m_trianglesToDelete.PushBack(m_mesh.m_triangles.GetHead());
+ for (int32_t k = 0; k < 3; k++) {
+ for (int32_t h = 0; h < 2; h++) {
+ if (currentTriangle.m_edges[k]->GetData().m_triangles[h] == m_mesh.m_triangles.GetHead()) {
+ currentTriangle.m_edges[k]->GetData().m_triangles[h] = 0;
+ break;
+ }
+ }
+ }
+ }
+ else {
+ trianglesToDuplicate.PushBack(m_mesh.m_triangles.GetHead());
+ }
+ m_mesh.m_triangles.Next();
+ }
+ size_t nE = m_mesh.GetNEdges();
+ for (size_t e = 0; e < nE; e++) {
+ TMMEdge& currentEdge = m_mesh.m_edges.GetHead()->GetData();
+ if (currentEdge.m_triangles[0] == 0 && currentEdge.m_triangles[1] == 0) {
+ m_edgesToDelete.PushBack(m_mesh.m_edges.GetHead());
+ }
+ m_mesh.m_edges.Next();
+ }
+ size_t nV = m_mesh.GetNVertices();
+ CircularList<TMMVertex>& 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<TMMTriangle>* newTriangle;
+ for (size_t t = 0; t < trianglesToDuplicate.Size(); t++) {
+ newTriangle = m_mesh.AddTriangle();
+ newTriangle->GetData().m_vertices[0] = trianglesToDuplicate[t]->GetData().m_vertices[1];
+ newTriangle->GetData().m_vertices[1] = trianglesToDuplicate[t]->GetData().m_vertices[0];
+ newTriangle->GetData().m_vertices[2] = trianglesToDuplicate[t]->GetData().m_vertices[2];
+ }
+ }
+ return ICHullErrorOK;
+}
+ICHullError ICHull::Process(const uint32_t nPointsCH,
+ const double minVolume)
+{
+ uint32_t addedPoints = 0;
+ if (nPointsCH < 3 || m_mesh.GetNVertices() < 3) {
+ return ICHullErrorNotEnoughPoints;
+ }
+ if (m_mesh.GetNVertices() == 3) {
+ m_isFlat = true;
+ CircularListElement<TMMTriangle>* t1 = m_mesh.AddTriangle();
+ CircularListElement<TMMTriangle>* t2 = m_mesh.AddTriangle();
+ CircularListElement<TMMVertex>* v0 = m_mesh.m_vertices.GetHead();
+ CircularListElement<TMMVertex>* v1 = v0->GetNext();
+ CircularListElement<TMMVertex>* v2 = v1->GetNext();
+ // Compute the normal to the plane
+ Vec3<double> p0 = v0->GetData().m_pos;
+ Vec3<double> p1 = v1->GetData().m_pos;
+ Vec3<double> 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<TMMVertex>& 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<TMMVertex>& 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<CircularListElement<TMMTriangle>*> trianglesToDuplicate;
+ size_t nT = m_mesh.GetNTriangles();
+ for (size_t f = 0; f < nT; f++) {
+ TMMTriangle& currentTriangle = m_mesh.m_triangles.GetHead()->GetData();
+ if (currentTriangle.m_vertices[0]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[1]->GetData().m_name == sc_dummyIndex || currentTriangle.m_vertices[2]->GetData().m_name == sc_dummyIndex) {
+ m_trianglesToDelete.PushBack(m_mesh.m_triangles.GetHead());
+ for (int32_t k = 0; k < 3; k++) {
+ for (int32_t h = 0; h < 2; h++) {
+ if (currentTriangle.m_edges[k]->GetData().m_triangles[h] == m_mesh.m_triangles.GetHead()) {
+ currentTriangle.m_edges[k]->GetData().m_triangles[h] = 0;
+ break;
+ }
+ }
+ }
+ }
+ else {
+ trianglesToDuplicate.PushBack(m_mesh.m_triangles.GetHead());
+ }
+ m_mesh.m_triangles.Next();
+ }
+ size_t nE = m_mesh.GetNEdges();
+ for (size_t e = 0; e < nE; e++) {
+ TMMEdge& currentEdge = m_mesh.m_edges.GetHead()->GetData();
+ if (currentEdge.m_triangles[0] == 0 && currentEdge.m_triangles[1] == 0) {
+ m_edgesToDelete.PushBack(m_mesh.m_edges.GetHead());
+ }
+ m_mesh.m_edges.Next();
+ }
+ size_t nV = m_mesh.GetNVertices();
+ CircularList<TMMVertex>& 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<TMMTriangle>* 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<TMMVertex>& vertices = m_mesh.GetVertices();
+ CircularListElement<TMMVertex>* vMaxVolume = 0;
+ CircularListElement<TMMVertex>* 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<TMMVertex>* vHead = vHeadPrev->GetNext();
+ vertices.GetHead() = vHead;
+ if (!vMaxVolume) {
+ return false;
+ }
+ if (vMaxVolume != vHead) {
+ Vec3<double> pos = vHead->GetData().m_pos;
+ int32_t id = vHead->GetData().m_name;
+ vHead->GetData().m_pos = vMaxVolume->GetData().m_pos;
+ vHead->GetData().m_name = vMaxVolume->GetData().m_name;
+ vMaxVolume->GetData().m_pos = pos;
+ vHead->GetData().m_name = id;
+ }
+ return true;
+}
+ICHullError ICHull::DoubleTriangle()
+{
+ // find three non colinear points
+ m_isFlat = false;
+ CircularList<TMMVertex>& vertices = m_mesh.GetVertices();
+ CircularListElement<TMMVertex>* 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<TMMVertex>* v1 = v0->GetNext();
+ CircularListElement<TMMVertex>* v2 = v1->GetNext();
+ // mark points as processed
+ v0->GetData().m_tag = v1->GetData().m_tag = v2->GetData().m_tag = true;
+
+ // create two triangles
+ CircularListElement<TMMTriangle>* f0 = MakeFace(v0, v1, v2, 0);
+ MakeFace(v2, v1, v0, f0);
+
+ // find a fourth non-coplanar point to form tetrahedron
+ CircularListElement<TMMVertex>* 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<double> bary(0.0, 0.0, 0.0);
+ CircularListElement<TMMVertex>* vBary = v0;
+ do {
+ bary += vBary->GetData().m_pos;
+ } while ((vBary = vBary->GetNext()) != v0);
+ bary /= static_cast<double>(vertices.GetSize());
+
+ // Compute the normal to the plane
+ Vec3<double> p0 = v0->GetData().m_pos;
+ Vec3<double> p1 = v1->GetData().m_pos;
+ Vec3<double> 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<double> 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<TMMTriangle>* ICHull::MakeFace(CircularListElement<TMMVertex>* v0,
+ CircularListElement<TMMVertex>* v1,
+ CircularListElement<TMMVertex>* v2,
+ CircularListElement<TMMTriangle>* fold)
+{
+ CircularListElement<TMMEdge>* e0;
+ CircularListElement<TMMEdge>* e1;
+ CircularListElement<TMMEdge>* e2;
+ int32_t index = 0;
+ if (!fold) // if first face to be created
+ {
+ e0 = m_mesh.AddEdge(); // create the three edges
+ e1 = m_mesh.AddEdge();
+ e2 = m_mesh.AddEdge();
+ }
+ else // otherwise re-use existing edges (in reverse order)
+ {
+ e0 = fold->GetData().m_edges[2];
+ e1 = fold->GetData().m_edges[1];
+ e2 = fold->GetData().m_edges[0];
+ index = 1;
+ }
+ e0->GetData().m_vertices[0] = v0;
+ e0->GetData().m_vertices[1] = v1;
+ e1->GetData().m_vertices[0] = v1;
+ e1->GetData().m_vertices[1] = v2;
+ e2->GetData().m_vertices[0] = v2;
+ e2->GetData().m_vertices[1] = v0;
+ // create the new face
+ CircularListElement<TMMTriangle>* 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<TMMTriangle>* ICHull::MakeConeFace(CircularListElement<TMMEdge>* e, CircularListElement<TMMVertex>* p)
+{
+ // create two new edges if they don't already exist
+ CircularListElement<TMMEdge>* newEdges[2];
+ for (int32_t i = 0; i < 2; ++i) {
+ if (!(newEdges[i] = e->GetData().m_vertices[i]->GetData().m_duplicate)) { // if the edge doesn't exits add it and mark the vertex as duplicated
+ newEdges[i] = m_mesh.AddEdge();
+ newEdges[i]->GetData().m_vertices[0] = e->GetData().m_vertices[i];
+ newEdges[i]->GetData().m_vertices[1] = p;
+ e->GetData().m_vertices[i]->GetData().m_duplicate = newEdges[i];
+ }
+ }
+ // make the new face
+ CircularListElement<TMMTriangle>* newFace = m_mesh.AddTriangle();
+ newFace->GetData().m_edges[0] = e;
+ newFace->GetData().m_edges[1] = newEdges[0];
+ newFace->GetData().m_edges[2] = newEdges[1];
+ MakeCCW(newFace, e, p);
+ for (int32_t i = 0; i < 2; ++i) {
+ for (int32_t j = 0; j < 2; ++j) {
+ if (!newEdges[i]->GetData().m_triangles[j]) {
+ newEdges[i]->GetData().m_triangles[j] = newFace;
+ break;
+ }
+ }
+ }
+ return newFace;
+}
+bool ICHull::ComputePointVolume(double& totalVolume, bool markVisibleFaces)
+{
+ // mark visible faces
+ CircularListElement<TMMTriangle>* fHead = m_mesh.GetTriangles().GetHead();
+ CircularListElement<TMMTriangle>* f = fHead;
+ CircularList<TMMVertex>& vertices = m_mesh.GetVertices();
+ CircularListElement<TMMVertex>* vertex0 = vertices.GetHead();
+ bool visible = false;
+ Vec3<double> pos0 = Vec3<double>(vertex0->GetData().m_pos.X(),
+ vertex0->GetData().m_pos.Y(),
+ vertex0->GetData().m_pos.Z());
+ double vol = 0.0;
+ totalVolume = 0.0;
+ Vec3<double> 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<TMMVertex>* v0 = m_mesh.GetVertices().GetHead();
+ CircularListElement<TMMEdge>* eHead = m_mesh.GetEdges().GetHead();
+ CircularListElement<TMMEdge>* e = eHead;
+ CircularListElement<TMMEdge>* tmp = 0;
+ int32_t nvisible = 0;
+ m_edgesToDelete.Resize(0);
+ m_edgesToUpdate.Resize(0);
+ do {
+ tmp = e->GetNext();
+ nvisible = 0;
+ for (int32_t k = 0; k < 2; k++) {
+ if (e->GetData().m_triangles[k]->GetData().m_visible) {
+ nvisible++;
+ }
+ }
+ if (nvisible == 2) {
+ m_edgesToDelete.PushBack(e);
+ }
+ else if (nvisible == 1) {
+ e->GetData().m_newFace = MakeConeFace(e, v0);
+ m_edgesToUpdate.PushBack(e);
+ }
+ e = tmp;
+ } while (e != eHead);
+ return true;
+}
+bool ICHull::MakeCCW(CircularListElement<TMMTriangle>* f,
+ CircularListElement<TMMEdge>* e,
+ CircularListElement<TMMVertex>* v)
+{
+ // the visible face adjacent to e
+ CircularListElement<TMMTriangle>* fv;
+ if (e->GetData().m_triangles[0]->GetData().m_visible) {
+ fv = e->GetData().m_triangles[0];
+ }
+ else {
+ fv = e->GetData().m_triangles[1];
+ }
+
+ // set vertex[0] and vertex[1] to have the same orientation as the corresponding vertices of fv.
+ int32_t i; // index of e->m_vertices[0] in fv
+ CircularListElement<TMMVertex>* v0 = e->GetData().m_vertices[0];
+ CircularListElement<TMMVertex>* 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<TMMEdge>* tmp = f->GetData().m_edges[0];
+ f->GetData().m_edges[0] = f->GetData().m_edges[1];
+ f->GetData().m_edges[1] = tmp;
+ }
+ f->GetData().m_vertices[2] = v;
+ return true;
+}
+bool ICHull::CleanUp(uint32_t& addedPoints)
+{
+ bool r0 = CleanEdges();
+ bool r1 = CleanTriangles();
+ bool r2 = CleanVertices(addedPoints);
+ return r0 && r1 && r2;
+}
+bool ICHull::CleanEdges()
+{
+ // integrate the new faces into the data structure
+ CircularListElement<TMMEdge>* 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<TMMEdge>& 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<TMMTriangle>& triangles = m_mesh.GetTriangles();
+ const size_t nt_delete = m_trianglesToDelete.Size();
+ for (size_t i = 0; i < nt_delete; ++i) {
+ triangles.Delete(m_trianglesToDelete[i]);
+ }
+ m_trianglesToDelete.Resize(0);
+ return true;
+}
+bool ICHull::CleanVertices(uint32_t& addedPoints)
+{
+ // mark all vertices incident to some undeleted edge as on the hull
+ CircularList<TMMEdge>& edges = m_mesh.GetEdges();
+ CircularListElement<TMMEdge>* 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<TMMVertex>& vertices = m_mesh.GetVertices();
+ CircularListElement<TMMVertex>* vHead = vertices.GetHead();
+ CircularListElement<TMMVertex>* v = vHead;
+ v = v->GetPrev();
+ do {
+ if (v->GetData().m_tag && !v->GetData().m_onHull) {
+ CircularListElement<TMMVertex>* 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<double>& pt0, const double eps)
+{
+ const Vec3<double> pt(pt0.X(), pt0.Y(), pt0.Z());
+ if (m_isFlat) {
+ size_t nT = m_mesh.m_triangles.GetSize();
+ Vec3<double> 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<double> 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/sdk/extensions/authoring/source/VHACD/src/vhacdManifoldMesh.cpp b/sdk/extensions/authoring/source/VHACD/src/vhacdManifoldMesh.cpp
new file mode 100644
index 0000000..7aac9c0
--- /dev/null
+++ b/sdk/extensions/authoring/source/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 (int32_t i = 0; i < 3; i++) {
+ m_edges[i] = 0;
+ m_vertices[0] = 0;
+ }
+ m_visible = false;
+}
+TMMTriangle::TMMTriangle(void)
+{
+ Initialize();
+}
+TMMTriangle::~TMMTriangle(void)
+{
+}
+TMMesh::TMMesh()
+{
+}
+TMMesh::~TMMesh(void)
+{
+}
+void TMMesh::GetIFS(Vec3<double>* const points, Vec3<int32_t>* 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<int32_t>(currentTriangle.m_vertices[0]->GetData().m_id);
+ triangles[f].Y() = static_cast<int32_t>(currentTriangle.m_vertices[1]->GetData().m_id);
+ triangles[f].Z() = static_cast<int32_t>(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<TMMVertex>** vertexMap = new CircularListElement<TMMVertex>*[nV];
+ CircularListElement<TMMEdge>** edgeMap = new CircularListElement<TMMEdge>*[nE];
+ CircularListElement<TMMTriangle>** triangleMap = new CircularListElement<TMMTriangle>*[nT];
+ for (size_t v = 0; v < nV; v++) {
+ vertexMap[v] = m_vertices.GetHead();
+ m_vertices.Next();
+ }
+ for (size_t e = 0; e < nE; e++) {
+ edgeMap[e] = m_edges.GetHead();
+ m_edges.Next();
+ }
+ for (size_t f = 0; f < nT; f++) {
+ triangleMap[f] = m_triangles.GetHead();
+ m_triangles.Next();
+ }
+
+ // updating pointers
+ for (size_t v = 0; v < nV; v++) {
+ if (vertexMap[v]->GetData().m_duplicate) {
+ vertexMap[v]->GetData().m_duplicate = edgeMap[vertexMap[v]->GetData().m_duplicate->GetData().m_id];
+ }
+ }
+ for (size_t e = 0; e < nE; e++) {
+ if (edgeMap[e]->GetData().m_newFace) {
+ edgeMap[e]->GetData().m_newFace = triangleMap[edgeMap[e]->GetData().m_newFace->GetData().m_id];
+ }
+ if (nT > 0) {
+ for (int32_t f = 0; f < 2; f++) {
+ if (edgeMap[e]->GetData().m_triangles[f]) {
+ edgeMap[e]->GetData().m_triangles[f] = triangleMap[edgeMap[e]->GetData().m_triangles[f]->GetData().m_id];
+ }
+ }
+ }
+ for (int32_t v = 0; v < 2; v++) {
+ if (edgeMap[e]->GetData().m_vertices[v]) {
+ edgeMap[e]->GetData().m_vertices[v] = vertexMap[edgeMap[e]->GetData().m_vertices[v]->GetData().m_id];
+ }
+ }
+ }
+ for (size_t f = 0; f < nT; f++) {
+ if (nE > 0) {
+ for (int32_t e = 0; e < 3; e++) {
+ if (triangleMap[f]->GetData().m_edges[e]) {
+ triangleMap[f]->GetData().m_edges[e] = edgeMap[triangleMap[f]->GetData().m_edges[e]->GetData().m_id];
+ }
+ }
+ }
+ for (int32_t v = 0; v < 3; v++) {
+ if (triangleMap[f]->GetData().m_vertices[v]) {
+ triangleMap[f]->GetData().m_vertices[v] = vertexMap[triangleMap[f]->GetData().m_vertices[v]->GetData().m_id];
+ }
+ }
+ }
+ delete[] vertexMap;
+ delete[] edgeMap;
+ delete[] triangleMap;
+}
+bool TMMesh::CheckConsistancy()
+{
+ size_t nE = m_edges.GetSize();
+ size_t nT = m_triangles.GetSize();
+ for (size_t e = 0; e < nE; e++) {
+ for (int32_t f = 0; f < 2; f++) {
+ if (!m_edges.GetHead()->GetData().m_triangles[f]) {
+ return false;
+ }
+ }
+ m_edges.Next();
+ }
+ for (size_t f = 0; f < nT; f++) {
+ for (int32_t e = 0; e < 3; e++) {
+ int32_t found = 0;
+ for (int32_t k = 0; k < 2; k++) {
+ if (m_triangles.GetHead()->GetData().m_edges[e]->GetData().m_triangles[k] == m_triangles.GetHead()) {
+ found++;
+ }
+ }
+ if (found != 1) {
+ return false;
+ }
+ }
+ m_triangles.Next();
+ }
+ return true;
+}
+} \ No newline at end of file
diff --git a/sdk/extensions/authoring/source/VHACD/src/vhacdMesh.cpp b/sdk/extensions/authoring/source/VHACD/src/vhacdMesh.cpp
new file mode 100644
index 0000000..83e0952
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/src/vhacdMesh.cpp
@@ -0,0 +1,366 @@
+/* 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 <fstream>
+#include <iosfwd>
+#include <iostream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+
+namespace VHACD {
+Mesh::Mesh()
+{
+ m_diag = 1.0;
+}
+Mesh::~Mesh()
+{
+}
+
+Vec3<double>& Mesh::ComputeCenter(void)
+{
+ const size_t nV = GetNPoints();
+ if (nV)
+ {
+ m_minBB = GetPoint(0);
+ m_maxBB = GetPoint(0);
+ for (size_t v = 1; v < nV; v++)
+ {
+ Vec3<double> p = GetPoint(v);
+ if (p.X() < m_minBB.X())
+ {
+ m_minBB.X() = p.X();
+ }
+ if (p.Y() < m_minBB.Y())
+ {
+ m_minBB.Y() = p.Y();
+ }
+ if (p.Z() < m_minBB.Z())
+ {
+ m_minBB.Z() = p.Z();
+ }
+ if (p.X() > m_maxBB.X())
+ {
+ m_maxBB.X() = p.X();
+ }
+ if (p.Y() > m_maxBB.Y())
+ {
+ m_maxBB.Y() = p.Y();
+ }
+ if (p.Z() > m_maxBB.Z())
+ {
+ m_maxBB.Z() = p.Z();
+ }
+ }
+ m_center.X() = (m_maxBB.X() - m_minBB.X())*0.5 + m_minBB.X();
+ m_center.Y() = (m_maxBB.Y() - m_minBB.Y())*0.5 + m_minBB.Y();
+ m_center.Z() = (m_maxBB.Z() - m_minBB.Z())*0.5 + m_minBB.Z();
+ }
+ return m_center;
+}
+
+double Mesh::ComputeVolume() const
+{
+ const size_t nV = GetNPoints();
+ const size_t nT = GetNTriangles();
+ if (nV == 0 || nT == 0) {
+ return 0.0;
+ }
+
+ Vec3<double> bary(0.0, 0.0, 0.0);
+ for (size_t v = 0; v < nV; v++) {
+ bary += GetPoint(v);
+ }
+ bary /= static_cast<double>(nV);
+
+ Vec3<double> ver0, ver1, ver2;
+ double totalVolume = 0.0;
+ for (int32_t t = 0; t < int32_t(nT); t++) {
+ const Vec3<int32_t>& tri = GetTriangle(t);
+ ver0 = GetPoint(tri[0]);
+ ver1 = GetPoint(tri[1]);
+ ver2 = GetPoint(tri[2]);
+ totalVolume += ComputeVolume4(ver0, ver1, ver2, bary);
+ }
+ return totalVolume / 6.0;
+}
+
+void Mesh::ComputeConvexHull(const double* const pts,
+ const size_t nPts)
+{
+ ResizePoints(0);
+ ResizeTriangles(0);
+ btConvexHullComputer ch;
+ ch.compute(pts, 3 * sizeof(double), (int32_t)nPts, -1.0, -1.0);
+ for (int32_t v = 0; v < ch.vertices.size(); v++) {
+ AddPoint(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ()));
+ }
+ const int32_t nt = ch.faces.size();
+ for (int32_t t = 0; t < nt; ++t) {
+ const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]);
+ int32_t a = sourceEdge->getSourceVertex();
+ int32_t b = sourceEdge->getTargetVertex();
+ const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace();
+ int32_t c = edge->getTargetVertex();
+ while (c != a) {
+ AddTriangle(Vec3<int32_t>(a, b, c));
+ edge = edge->getNextEdgeOfFace();
+ b = c;
+ c = edge->getTargetVertex();
+ }
+ }
+}
+void Mesh::Clip(const Plane& plane,
+ SArray<Vec3<double> >& positivePart,
+ SArray<Vec3<double> >& negativePart) const
+{
+ const size_t nV = GetNPoints();
+ if (nV == 0) {
+ return;
+ }
+ double d;
+ for (size_t v = 0; v < nV; v++) {
+ const Vec3<double>& 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<double>& pt) const
+{
+ const size_t nV = GetNPoints();
+ const size_t nT = GetNTriangles();
+ if (nV == 0 || nT == 0) {
+ return false;
+ }
+ Vec3<double> ver0, ver1, ver2;
+ double volume;
+ for (int32_t t = 0; t < int32_t(nT); t++) {
+ const Vec3<int32_t>& 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<double> minBB = m_points[0];
+ Vec3<double> maxBB = m_points[0];
+ double x, y, z;
+ for (size_t v = 1; v < nPoints; v++) {
+ x = m_points[v][0];
+ y = m_points[v][1];
+ z = m_points[v][2];
+ if (x < minBB[0])
+ minBB[0] = x;
+ else if (x > maxBB[0])
+ maxBB[0] = x;
+ if (y < minBB[1])
+ minBB[1] = y;
+ else if (y > maxBB[1])
+ maxBB[1] = y;
+ if (z < minBB[2])
+ minBB[2] = z;
+ else if (z > maxBB[2])
+ maxBB[2] = z;
+ }
+ return (m_diag = (maxBB - minBB).GetNorm());
+}
+
+#ifdef VHACD_DEBUG_MESH
+bool Mesh::SaveVRML2(const std::string& fileName) const
+{
+ std::ofstream fout(fileName.c_str());
+ if (fout.is_open()) {
+ const Material material;
+
+ if (SaveVRML2(fout, material)) {
+ fout.close();
+ return true;
+ }
+ return false;
+ }
+ return false;
+}
+bool Mesh::SaveVRML2(std::ofstream& fout, const Material& material) const
+{
+ if (fout.is_open()) {
+ fout.setf(std::ios::fixed, std::ios::floatfield);
+ fout.setf(std::ios::showpoint);
+ fout.precision(6);
+ size_t nV = m_points.Size();
+ size_t nT = m_triangles.Size();
+ fout << "#VRML V2.0 utf8" << std::endl;
+ fout << "" << std::endl;
+ fout << "# Vertices: " << nV << std::endl;
+ fout << "# Triangles: " << nT << std::endl;
+ fout << "" << std::endl;
+ fout << "Group {" << std::endl;
+ fout << " children [" << std::endl;
+ fout << " Shape {" << std::endl;
+ fout << " appearance Appearance {" << std::endl;
+ fout << " material Material {" << std::endl;
+ fout << " diffuseColor " << material.m_diffuseColor[0] << " "
+ << material.m_diffuseColor[1] << " "
+ << material.m_diffuseColor[2] << std::endl;
+ fout << " ambientIntensity " << material.m_ambientIntensity << std::endl;
+ fout << " specularColor " << material.m_specularColor[0] << " "
+ << material.m_specularColor[1] << " "
+ << material.m_specularColor[2] << std::endl;
+ fout << " emissiveColor " << material.m_emissiveColor[0] << " "
+ << material.m_emissiveColor[1] << " "
+ << material.m_emissiveColor[2] << std::endl;
+ fout << " shininess " << material.m_shininess << std::endl;
+ fout << " transparency " << material.m_transparency << std::endl;
+ fout << " }" << std::endl;
+ fout << " }" << std::endl;
+ fout << " geometry IndexedFaceSet {" << std::endl;
+ fout << " ccw TRUE" << std::endl;
+ fout << " solid TRUE" << std::endl;
+ fout << " convex TRUE" << std::endl;
+ if (nV > 0) {
+ fout << " coord DEF co Coordinate {" << std::endl;
+ fout << " point [" << std::endl;
+ for (size_t v = 0; v < nV; v++) {
+ fout << " " << m_points[v][0] << " "
+ << m_points[v][1] << " "
+ << m_points[v][2] << "," << std::endl;
+ }
+ fout << " ]" << std::endl;
+ fout << " }" << std::endl;
+ }
+ if (nT > 0) {
+ fout << " coordIndex [ " << std::endl;
+ for (size_t f = 0; f < nT; f++) {
+ fout << " " << m_triangles[f][0] << ", "
+ << m_triangles[f][1] << ", "
+ << m_triangles[f][2] << ", -1," << std::endl;
+ }
+ fout << " ]" << std::endl;
+ }
+ fout << " }" << std::endl;
+ fout << " }" << std::endl;
+ fout << " ]" << std::endl;
+ fout << "}" << std::endl;
+ return true;
+ }
+ return false;
+}
+bool Mesh::SaveOFF(const std::string& fileName) const
+{
+ std::ofstream fout(fileName.c_str());
+ if (fout.is_open()) {
+ size_t nV = m_points.Size();
+ size_t nT = m_triangles.Size();
+ fout << "OFF" << std::endl;
+ fout << nV << " " << nT << " " << 0 << std::endl;
+ for (size_t v = 0; v < nV; v++) {
+ fout << m_points[v][0] << " "
+ << m_points[v][1] << " "
+ << m_points[v][2] << std::endl;
+ }
+ for (size_t f = 0; f < nT; f++) {
+ fout << "3 " << m_triangles[f][0] << " "
+ << m_triangles[f][1] << " "
+ << m_triangles[f][2] << std::endl;
+ }
+ fout.close();
+ return true;
+ }
+ return false;
+}
+
+bool Mesh::LoadOFF(const std::string& fileName, bool invert)
+{
+ FILE* fid = fopen(fileName.c_str(), "r");
+ if (fid) {
+ const std::string strOFF("OFF");
+ char temp[1024];
+ fscanf(fid, "%s", temp);
+ if (std::string(temp) != strOFF) {
+ fclose(fid);
+ return false;
+ }
+ else {
+ int32_t nv = 0;
+ int32_t nf = 0;
+ int32_t ne = 0;
+ fscanf(fid, "%i", &nv);
+ fscanf(fid, "%i", &nf);
+ fscanf(fid, "%i", &ne);
+ m_points.Resize(nv);
+ m_triangles.Resize(nf);
+ Vec3<double> coord;
+ float x, y, z;
+ for (int32_t p = 0; p < nv; p++) {
+ fscanf(fid, "%f", &x);
+ fscanf(fid, "%f", &y);
+ fscanf(fid, "%f", &z);
+ m_points[p][0] = x;
+ m_points[p][1] = y;
+ m_points[p][2] = z;
+ }
+ int32_t i, j, k, s;
+ for (int32_t t = 0; t < nf; ++t) {
+ fscanf(fid, "%i", &s);
+ if (s == 3) {
+ fscanf(fid, "%i", &i);
+ fscanf(fid, "%i", &j);
+ fscanf(fid, "%i", &k);
+ m_triangles[t][0] = i;
+ if (invert) {
+ m_triangles[t][1] = k;
+ m_triangles[t][2] = j;
+ }
+ else {
+ m_triangles[t][1] = j;
+ m_triangles[t][2] = k;
+ }
+ }
+ else // Fix me: support only triangular meshes
+ {
+ for (int32_t h = 0; h < s; ++h)
+ fscanf(fid, "%i", &s);
+ }
+ }
+ fclose(fid);
+ }
+ }
+ else {
+ return false;
+ }
+ return true;
+}
+#endif // VHACD_DEBUG_MESH
+}
diff --git a/sdk/extensions/authoring/source/VHACD/src/vhacdRaycastMesh.cpp b/sdk/extensions/authoring/source/VHACD/src/vhacdRaycastMesh.cpp
new file mode 100644
index 0000000..e8b9435
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/src/vhacdRaycastMesh.cpp
@@ -0,0 +1,208 @@
+#include "vhacdRaycastMesh.h"
+#include <math.h>
+#include <assert.h>
+
+namespace RAYCAST_MESH
+{
+
+/* a = b - c */
+#define vector(a,b,c) \
+ (a)[0] = (b)[0] - (c)[0]; \
+ (a)[1] = (b)[1] - (c)[1]; \
+ (a)[2] = (b)[2] - (c)[2];
+
+#define innerProduct(v,q) \
+ ((v)[0] * (q)[0] + \
+ (v)[1] * (q)[1] + \
+ (v)[2] * (q)[2])
+
+#define crossProduct(a,b,c) \
+ (a)[0] = (b)[1] * (c)[2] - (c)[1] * (b)[2]; \
+ (a)[1] = (b)[2] * (c)[0] - (c)[2] * (b)[0]; \
+ (a)[2] = (b)[0] * (c)[1] - (c)[0] * (b)[1];
+
+
+static inline bool rayIntersectsTriangle(const double *p,const double *d,const double *v0,const double *v1,const double *v2,double &t)
+{
+ double e1[3],e2[3],h[3],s[3],q[3];
+ double a,f,u,v;
+
+ vector(e1,v1,v0);
+ vector(e2,v2,v0);
+ crossProduct(h,d,e2);
+ a = innerProduct(e1,h);
+
+ if (a > -0.00001 && a < 0.00001)
+ return(false);
+
+ f = 1/a;
+ vector(s,p,v0);
+ u = f * (innerProduct(s,h));
+
+ if (u < 0.0 || u > 1.0)
+ return(false);
+
+ crossProduct(q,s,e1);
+ v = f * innerProduct(d,q);
+ if (v < 0.0 || u + v > 1.0)
+ return(false);
+ // at this stage we can compute t to find out where
+ // the intersection point is on the line
+ t = f * innerProduct(e2,q);
+ if (t > 0) // ray intersection
+ return(true);
+ else // this means that there is a line intersection
+ // but not a ray intersection
+ return (false);
+}
+
+static double getPointDistance(const double *p1, const double *p2)
+{
+ double dx = p1[0] - p2[0];
+ double dy = p1[1] - p2[1];
+ double dz = p1[2] - p2[2];
+ return sqrt(dx*dx + dy*dy + dz*dz);
+}
+
+class MyRaycastMesh : public VHACD::RaycastMesh
+{
+public:
+
+ template <class T>
+ MyRaycastMesh(uint32_t vcount,
+ const T *vertices,
+ uint32_t tcount,
+ const uint32_t *indices)
+ {
+ mVcount = vcount;
+ mVertices = new double[mVcount * 3];
+ for (uint32_t i = 0; i < mVcount; i++)
+ {
+ mVertices[i * 3 + 0] = vertices[0];
+ mVertices[i * 3 + 1] = vertices[1];
+ mVertices[i * 3 + 2] = vertices[2];
+ vertices += 3;
+ }
+ mTcount = tcount;
+ mIndices = new uint32_t[mTcount * 3];
+ for (uint32_t i = 0; i < mTcount; i++)
+ {
+ mIndices[i * 3 + 0] = indices[0];
+ mIndices[i * 3 + 1] = indices[1];
+ mIndices[i * 3 + 2] = indices[2];
+ indices += 3;
+ }
+ }
+
+
+ ~MyRaycastMesh(void)
+ {
+ delete[]mVertices;
+ delete[]mIndices;
+ }
+
+ virtual void release(void)
+ {
+ delete this;
+ }
+
+ virtual bool raycast(const double *from, // The starting point of the raycast
+ const double *to, // The ending point of the raycast
+ const double *closestToPoint, // The point to match the nearest hit location (can just be the 'from' location of no specific point)
+ double *hitLocation, // The point where the ray hit nearest to the 'closestToPoint' location
+ double *hitDistance) final // The distance the ray traveled to the hit location
+ {
+ bool ret = false;
+
+ double dir[3];
+
+ dir[0] = to[0] - from[0];
+ dir[1] = to[1] - from[1];
+ dir[2] = to[2] - from[2];
+
+ double distance = sqrt( dir[0]*dir[0] + dir[1]*dir[1]+dir[2]*dir[2] );
+ if ( distance < 0.0000000001f ) return false;
+ double recipDistance = 1.0f / distance;
+ dir[0]*=recipDistance;
+ dir[1]*=recipDistance;
+ dir[2]*=recipDistance;
+ const uint32_t *indices = mIndices;
+ const double *vertices = mVertices;
+ double nearestDistance = distance;
+
+ for (uint32_t tri=0; tri<mTcount; tri++)
+ {
+ uint32_t i1 = indices[tri*3+0];
+ uint32_t i2 = indices[tri*3+1];
+ uint32_t i3 = indices[tri*3+2];
+
+ const double *p1 = &vertices[i1*3];
+ const double *p2 = &vertices[i2*3];
+ const double *p3 = &vertices[i3*3];
+
+ double t;
+ if ( rayIntersectsTriangle(from,dir,p1,p2,p3,t))
+ {
+ double hitPos[3];
+
+ hitPos[0] = from[0] + dir[0] * t;
+ hitPos[1] = from[1] + dir[1] * t;
+ hitPos[2] = from[2] + dir[2] * t;
+
+ double pointDistance = getPointDistance(hitPos, closestToPoint);
+
+ if (pointDistance < nearestDistance )
+ {
+ nearestDistance = pointDistance;
+ if ( hitLocation )
+ {
+ hitLocation[0] = hitPos[0];
+ hitLocation[1] = hitPos[1];
+ hitLocation[2] = hitPos[2];
+ }
+ if ( hitDistance )
+ {
+ *hitDistance = pointDistance;
+ }
+ ret = true;
+ }
+ }
+ }
+ return ret;
+ }
+
+ uint32_t mVcount;
+ double *mVertices;
+ uint32_t mTcount;
+ uint32_t *mIndices;
+};
+
+};
+
+
+
+using namespace RAYCAST_MESH;
+
+namespace VHACD
+{
+
+ RaycastMesh * RaycastMesh::createRaycastMesh(uint32_t vcount, // The number of vertices in the source triangle mesh
+ const double *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc.
+ uint32_t tcount, // The number of triangles in the source triangle mesh
+ const uint32_t *indices) // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ...
+ {
+ MyRaycastMesh *m = new MyRaycastMesh(vcount, vertices, tcount, indices);
+ return static_cast<RaycastMesh *>(m);
+ }
+
+ RaycastMesh * RaycastMesh::createRaycastMesh(uint32_t vcount, // The number of vertices in the source triangle mesh
+ const float *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc.
+ uint32_t tcount, // The number of triangles in the source triangle mesh
+ const uint32_t *indices) // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ...
+ {
+ MyRaycastMesh *m = new MyRaycastMesh(vcount, vertices, tcount, indices);
+ return static_cast<RaycastMesh *>(m);
+ }
+
+
+} // end of VHACD namespace \ No newline at end of file
diff --git a/sdk/extensions/authoring/source/VHACD/src/vhacdVolume.cpp b/sdk/extensions/authoring/source/VHACD/src/vhacdVolume.cpp
new file mode 100644
index 0000000..5f076d5
--- /dev/null
+++ b/sdk/extensions/authoring/source/VHACD/src/vhacdVolume.cpp
@@ -0,0 +1,1622 @@
+/* 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 <algorithm>
+#include <float.h>
+#include <math.h>
+#include <queue>
+#include <string.h>
+
+#ifdef _MSC_VER
+#pragma warning(disable:4458 4100)
+#endif
+
+
+namespace VHACD {
+/********************************************************/
+/* AABB-triangle overlap test code */
+/* by Tomas Akenine-M�ller */
+/* Function: int32_t triBoxOverlap(float boxcenter[3], */
+/* float boxhalfsize[3],float triverts[3][3]); */
+/* History: */
+/* 2001-03-05: released the code in its first version */
+/* 2001-06-18: changed the order of the tests, faster */
+/* */
+/* Acknowledgement: Many thanks to Pierre Terdiman for */
+/* suggestions and discussions on how to optimize code. */
+/* Thanks to David Hunt for finding a ">="-bug! */
+/********************************************************/
+
+#define X 0
+#define Y 1
+#define Z 2
+#define FINDMINMAX(x0, x1, x2, min, max) \
+ min = max = x0; \
+ if (x1 < min) \
+ min = x1; \
+ if (x1 > max) \
+ max = x1; \
+ if (x2 < min) \
+ min = x2; \
+ if (x2 > max) \
+ max = x2;
+
+#define AXISTEST_X01(a, b, fa, fb) \
+ p0 = a * v0[Y] - b * v0[Z]; \
+ p2 = a * v2[Y] - b * v2[Z]; \
+ if (p0 < p2) { \
+ min = p0; \
+ max = p2; \
+ } \
+ else { \
+ min = p2; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \
+ if (min > rad || max < -rad) \
+ return 0;
+
+#define AXISTEST_X2(a, b, fa, fb) \
+ p0 = a * v0[Y] - b * v0[Z]; \
+ p1 = a * v1[Y] - b * v1[Z]; \
+ if (p0 < p1) { \
+ min = p0; \
+ max = p1; \
+ } \
+ else { \
+ min = p1; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \
+ if (min > rad || max < -rad) \
+ return 0;
+
+#define AXISTEST_Y02(a, b, fa, fb) \
+ p0 = -a * v0[X] + b * v0[Z]; \
+ p2 = -a * v2[X] + b * v2[Z]; \
+ if (p0 < p2) { \
+ min = p0; \
+ max = p2; \
+ } \
+ else { \
+ min = p2; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \
+ if (min > rad || max < -rad) \
+ return 0;
+
+#define AXISTEST_Y1(a, b, fa, fb) \
+ p0 = -a * v0[X] + b * v0[Z]; \
+ p1 = -a * v1[X] + b * v1[Z]; \
+ if (p0 < p1) { \
+ min = p0; \
+ max = p1; \
+ } \
+ else { \
+ min = p1; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \
+ if (min > rad || max < -rad) \
+ return 0;
+
+#define AXISTEST_Z12(a, b, fa, fb) \
+ p1 = a * v1[X] - b * v1[Y]; \
+ p2 = a * v2[X] - b * v2[Y]; \
+ if (p2 < p1) { \
+ min = p2; \
+ max = p1; \
+ } \
+ else { \
+ min = p1; \
+ max = p2; \
+ } \
+ rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \
+ if (min > rad || max < -rad) \
+ return 0;
+
+#define AXISTEST_Z0(a, b, fa, fb) \
+ p0 = a * v0[X] - b * v0[Y]; \
+ p1 = a * v1[X] - b * v1[Y]; \
+ if (p0 < p1) { \
+ min = p0; \
+ max = p1; \
+ } \
+ else { \
+ min = p1; \
+ max = p0; \
+ } \
+ rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \
+ if (min > rad || max < -rad) \
+ return 0;
+
+int32_t PlaneBoxOverlap(const Vec3<double>& normal,
+ const Vec3<double>& vert,
+ const Vec3<double>& maxbox)
+{
+ int32_t q;
+ Vec3<double> vmin, vmax;
+ double v;
+ for (q = X; q <= Z; q++) {
+ v = vert[q];
+ if (normal[q] > 0.0) {
+ vmin[q] = -maxbox[q] - v;
+ vmax[q] = maxbox[q] - v;
+ }
+ else {
+ vmin[q] = maxbox[q] - v;
+ vmax[q] = -maxbox[q] - v;
+ }
+ }
+ if (normal * vmin > 0.0)
+ return 0;
+ if (normal * vmax >= 0.0)
+ return 1;
+ return 0;
+}
+
+int32_t TriBoxOverlap(const Vec3<double>& boxcenter,
+ const Vec3<double>& boxhalfsize,
+ const Vec3<double>& triver0,
+ const Vec3<double>& triver1,
+ const Vec3<double>& 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<double> v0, v1, v2;
+ double min, max, p0, p1, p2, rad, fex, fey, fez; // -NJMP- "d" local variable removed
+ Vec3<double> normal, e0, e1, e2;
+
+ /* This is the fastest branch on Sun */
+ /* move everything so that the boxcenter is in (0,0,0) */
+
+ v0 = triver0 - boxcenter;
+ v1 = triver1 - boxcenter;
+ v2 = triver2 - boxcenter;
+
+ /* compute triangle edges */
+ e0 = v1 - v0; /* tri edge 0 */
+ e1 = v2 - v1; /* tri edge 1 */
+ e2 = v0 - v2; /* tri edge 2 */
+
+ /* Bullet 3: */
+ /* test the 9 tests first (this was faster) */
+ fex = fabs(e0[X]);
+ fey = fabs(e0[Y]);
+ fez = fabs(e0[Z]);
+
+ AXISTEST_X01(e0[Z], e0[Y], fez, fey);
+ AXISTEST_Y02(e0[Z], e0[X], fez, fex);
+ AXISTEST_Z12(e0[Y], e0[X], fey, fex);
+
+ fex = fabs(e1[X]);
+ fey = fabs(e1[Y]);
+ fez = fabs(e1[Z]);
+
+ AXISTEST_X01(e1[Z], e1[Y], fez, fey);
+ AXISTEST_Y02(e1[Z], e1[X], fez, fex);
+ AXISTEST_Z0(e1[Y], e1[X], fey, fex);
+
+ fex = fabs(e2[X]);
+ fey = fabs(e2[Y]);
+ fez = fabs(e2[Z]);
+
+ AXISTEST_X2(e2[Z], e2[Y], fez, fey);
+ AXISTEST_Y1(e2[Z], e2[X], fez, fex);
+ AXISTEST_Z12(e2[Y], e2[X], fey, fex);
+
+ /* Bullet 1: */
+ /* first test overlap in the {x,y,z}-directions */
+ /* find min, max of the triangle each direction, and test for overlap in */
+ /* that direction -- this is equivalent to testing a minimal AABB around */
+ /* the triangle against the AABB */
+
+ /* test in X-direction */
+ FINDMINMAX(v0[X], v1[X], v2[X], min, max);
+ if (min > boxhalfsize[X] || max < -boxhalfsize[X])
+ return 0;
+
+ /* test in Y-direction */
+ FINDMINMAX(v0[Y], v1[Y], v2[Y], min, max);
+ if (min > boxhalfsize[Y] || max < -boxhalfsize[Y])
+ return 0;
+
+ /* test in Z-direction */
+ FINDMINMAX(v0[Z], v1[Z], v2[Z], min, max);
+ if (min > boxhalfsize[Z] || max < -boxhalfsize[Z])
+ return 0;
+
+ /* Bullet 2: */
+ /* test if the box intersects the plane of the triangle */
+ /* compute plane equation of triangle: normal*x+d=0 */
+ normal = e0 ^ e1;
+
+ if (!PlaneBoxOverlap(normal, v0, boxhalfsize))
+ return 0;
+ return 1; /* box and triangle overlaps */
+}
+
+// Slightly modified version of Stan Melax's code for 3x3 matrix diagonalization (Thanks Stan!)
+// source: http://www.melax.com/diag.html?attredirects=0
+void Diagonalize(const double (&A)[3][3], double (&Q)[3][3], double (&D)[3][3])
+{
+ // A must be a symmetric matrix.
+ // returns Q and D such that
+ // Diagonal matrix D = QT * A * Q; and A = Q*D*QT
+ const int32_t maxsteps = 24; // certainly wont need that many.
+ int32_t k0, k1, k2;
+ double o[3], m[3];
+ double q[4] = { 0.0, 0.0, 0.0, 1.0 };
+ double jr[4];
+ double sqw, sqx, sqy, sqz;
+ double tmp1, tmp2, mq;
+ double AQ[3][3];
+ double thet, sgn, t, c;
+ for (int32_t i = 0; i < maxsteps; ++i) {
+ // quat to matrix
+ sqx = q[0] * q[0];
+ sqy = q[1] * q[1];
+ sqz = q[2] * q[2];
+ sqw = q[3] * q[3];
+ Q[0][0] = (sqx - sqy - sqz + sqw);
+ Q[1][1] = (-sqx + sqy - sqz + sqw);
+ Q[2][2] = (-sqx - sqy + sqz + sqw);
+ tmp1 = q[0] * q[1];
+ tmp2 = q[2] * q[3];
+ Q[1][0] = 2.0 * (tmp1 + tmp2);
+ Q[0][1] = 2.0 * (tmp1 - tmp2);
+ tmp1 = q[0] * q[2];
+ tmp2 = q[1] * q[3];
+ Q[2][0] = 2.0 * (tmp1 - tmp2);
+ Q[0][2] = 2.0 * (tmp1 + tmp2);
+ tmp1 = q[1] * q[2];
+ tmp2 = q[0] * q[3];
+ Q[2][1] = 2.0 * (tmp1 + tmp2);
+ Q[1][2] = 2.0 * (tmp1 - tmp2);
+
+ // AQ = A * Q
+ AQ[0][0] = Q[0][0] * A[0][0] + Q[1][0] * A[0][1] + Q[2][0] * A[0][2];
+ AQ[0][1] = Q[0][1] * A[0][0] + Q[1][1] * A[0][1] + Q[2][1] * A[0][2];
+ AQ[0][2] = Q[0][2] * A[0][0] + Q[1][2] * A[0][1] + Q[2][2] * A[0][2];
+ AQ[1][0] = Q[0][0] * A[0][1] + Q[1][0] * A[1][1] + Q[2][0] * A[1][2];
+ AQ[1][1] = Q[0][1] * A[0][1] + Q[1][1] * A[1][1] + Q[2][1] * A[1][2];
+ AQ[1][2] = Q[0][2] * A[0][1] + Q[1][2] * A[1][1] + Q[2][2] * A[1][2];
+ AQ[2][0] = Q[0][0] * A[0][2] + Q[1][0] * A[1][2] + Q[2][0] * A[2][2];
+ AQ[2][1] = Q[0][1] * A[0][2] + Q[1][1] * A[1][2] + Q[2][1] * A[2][2];
+ AQ[2][2] = Q[0][2] * A[0][2] + Q[1][2] * A[1][2] + Q[2][2] * A[2][2];
+ // D = Qt * AQ
+ D[0][0] = AQ[0][0] * Q[0][0] + AQ[1][0] * Q[1][0] + AQ[2][0] * Q[2][0];
+ D[0][1] = AQ[0][0] * Q[0][1] + AQ[1][0] * Q[1][1] + AQ[2][0] * Q[2][1];
+ D[0][2] = AQ[0][0] * Q[0][2] + AQ[1][0] * Q[1][2] + AQ[2][0] * Q[2][2];
+ D[1][0] = AQ[0][1] * Q[0][0] + AQ[1][1] * Q[1][0] + AQ[2][1] * Q[2][0];
+ D[1][1] = AQ[0][1] * Q[0][1] + AQ[1][1] * Q[1][1] + AQ[2][1] * Q[2][1];
+ D[1][2] = AQ[0][1] * Q[0][2] + AQ[1][1] * Q[1][2] + AQ[2][1] * Q[2][2];
+ D[2][0] = AQ[0][2] * Q[0][0] + AQ[1][2] * Q[1][0] + AQ[2][2] * Q[2][0];
+ D[2][1] = AQ[0][2] * Q[0][1] + AQ[1][2] * Q[1][1] + AQ[2][2] * Q[2][1];
+ D[2][2] = AQ[0][2] * Q[0][2] + AQ[1][2] * Q[1][2] + AQ[2][2] * Q[2][2];
+ o[0] = D[1][2];
+ o[1] = D[0][2];
+ o[2] = D[0][1];
+ m[0] = fabs(o[0]);
+ m[1] = fabs(o[1]);
+ m[2] = fabs(o[2]);
+
+ k0 = (m[0] > m[1] && m[0] > m[2]) ? 0 : (m[1] > m[2]) ? 1 : 2; // index of largest element of offdiag
+ k1 = (k0 + 1) % 3;
+ k2 = (k0 + 2) % 3;
+ if (o[k0] == 0.0) {
+ break; // diagonal already
+ }
+ thet = (D[k2][k2] - D[k1][k1]) / (2.0 * o[k0]);
+ sgn = (thet > 0.0) ? 1.0 : -1.0;
+ thet *= sgn; // make it positive
+ t = sgn / (thet + ((thet < 1.E6) ? sqrt(thet * thet + 1.0) : thet)); // sign(T)/(|T|+sqrt(T^2+1))
+ c = 1.0 / sqrt(t * t + 1.0); // c= 1/(t^2+1) , t=s/c
+ if (c == 1.0) {
+ break; // no room for improvement - reached machine precision.
+ }
+ jr[0] = jr[1] = jr[2] = jr[3] = 0.0;
+ jr[k0] = sgn * sqrt((1.0 - c) / 2.0); // using 1/2 angle identity sin(a/2) = sqrt((1-cos(a))/2)
+ jr[k0] *= -1.0; // since our quat-to-matrix convention was for v*M instead of M*v
+ jr[3] = sqrt(1.0 - jr[k0] * jr[k0]);
+ if (jr[3] == 1.0) {
+ break; // reached limits of floating point precision
+ }
+ q[0] = (q[3] * jr[0] + q[0] * jr[3] + q[1] * jr[2] - q[2] * jr[1]);
+ q[1] = (q[3] * jr[1] - q[0] * jr[2] + q[1] * jr[3] + q[2] * jr[0]);
+ q[2] = (q[3] * jr[2] + q[0] * jr[1] - q[1] * jr[0] + q[2] * jr[3]);
+ q[3] = (q[3] * jr[3] - q[0] * jr[0] - q[1] * jr[1] - q[2] * jr[2]);
+ mq = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]);
+ q[0] /= mq;
+ q[1] /= mq;
+ q[2] /= mq;
+ q[3] /= mq;
+ }
+}
+const double TetrahedronSet::EPS = 0.0000000000001;
+VoxelSet::VoxelSet()
+{
+ m_minBB[0] = m_minBB[1] = m_minBB[2] = 0.0;
+ m_minBBVoxels[0] = m_minBBVoxels[1] = m_minBBVoxels[2] = 0;
+ m_maxBBVoxels[0] = m_maxBBVoxels[1] = m_maxBBVoxels[2] = 1;
+ m_minBBPts[0] = m_minBBPts[1] = m_minBBPts[2] = 0;
+ m_maxBBPts[0] = m_maxBBPts[1] = m_maxBBPts[2] = 1;
+ m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0;
+ m_barycenterPCA[0] = m_barycenterPCA[1] = m_barycenterPCA[2] = 0.0;
+ m_scale = 1.0;
+ m_unitVolume = 1.0;
+ m_numVoxelsOnSurface = 0;
+ m_numVoxelsInsideSurface = 0;
+ memset(m_Q, 0, sizeof(double) * 9);
+ memset(m_D, 0, sizeof(double) * 9);
+}
+VoxelSet::~VoxelSet(void)
+{
+}
+void VoxelSet::ComputeBB()
+{
+ const size_t nVoxels = m_voxels.Size();
+ if (nVoxels == 0)
+ return;
+ for (int32_t h = 0; h < 3; ++h) {
+ m_minBBVoxels[h] = m_voxels[0].m_coord[h];
+ m_maxBBVoxels[h] = m_voxels[0].m_coord[h];
+ }
+ Vec3<double> bary(0.0);
+ for (size_t p = 0; p < nVoxels; ++p) {
+ for (int32_t h = 0; h < 3; ++h) {
+ bary[h] += m_voxels[p].m_coord[h];
+ if (m_minBBVoxels[h] > m_voxels[p].m_coord[h])
+ m_minBBVoxels[h] = m_voxels[p].m_coord[h];
+ if (m_maxBBVoxels[h] < m_voxels[p].m_coord[h])
+ m_maxBBVoxels[h] = m_voxels[p].m_coord[h];
+ }
+ }
+ bary /= (double)nVoxels;
+ for (int32_t h = 0; h < 3; ++h) {
+ m_minBBPts[h] = m_minBBVoxels[h] * m_scale + m_minBB[h];
+ m_maxBBPts[h] = m_maxBBVoxels[h] * m_scale + m_minBB[h];
+ m_barycenter[h] = (short)(bary[h] + 0.5);
+ }
+}
+void VoxelSet::ComputeConvexHull(Mesh& meshCH, const size_t sampling) const
+{
+ const size_t CLUSTER_SIZE = 65536;
+ const size_t nVoxels = m_voxels.Size();
+ if (nVoxels == 0)
+ return;
+
+ SArray<Vec3<double> > cpoints;
+
+ Vec3<double>* points = new Vec3<double>[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<double> p0((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale);
+ Vec3<double> p1((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale);
+ Vec3<double> p2((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale);
+ Vec3<double> p3((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale);
+ Vec3<double> p4((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale);
+ Vec3<double> p5((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale);
+ Vec3<double> p6((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale);
+ Vec3<double> p7((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale);
+ points[q++] = p0 + m_minBB;
+ points[q++] = p1 + m_minBB;
+ points[q++] = p2 + m_minBB;
+ points[q++] = p3 + m_minBB;
+ points[q++] = p4 + m_minBB;
+ points[q++] = p5 + m_minBB;
+ points[q++] = p6 + m_minBB;
+ points[q++] = p7 + m_minBB;
+ }
+ }
+ ++p;
+ }
+ btConvexHullComputer ch;
+ ch.compute((double*)points, 3 * sizeof(double), (int32_t)q, -1.0, -1.0);
+ for (int32_t v = 0; v < ch.vertices.size(); v++) {
+ cpoints.PushBack(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ()));
+ }
+ }
+ delete[] points;
+
+ points = cpoints.Data();
+ btConvexHullComputer ch;
+ ch.compute((double*)points, 3 * sizeof(double), (int32_t)cpoints.Size(), -1.0, -1.0);
+ meshCH.ResizePoints(0);
+ meshCH.ResizeTriangles(0);
+ for (int32_t v = 0; v < ch.vertices.size(); v++) {
+ meshCH.AddPoint(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ()));
+ }
+ const int32_t nt = ch.faces.size();
+ for (int32_t t = 0; t < nt; ++t) {
+ const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]);
+ int32_t a = sourceEdge->getSourceVertex();
+ int32_t b = sourceEdge->getTargetVertex();
+ const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace();
+ int32_t c = edge->getTargetVertex();
+ while (c != a) {
+ meshCH.AddTriangle(Vec3<int32_t>(a, b, c));
+ edge = edge->getNextEdgeOfFace();
+ b = c;
+ c = edge->getTargetVertex();
+ }
+ }
+}
+void VoxelSet::GetPoints(const Voxel& voxel,
+ Vec3<double>* 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<Vec3<double> >* const positivePts,
+ SArray<Vec3<double> >* 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<double> pts[8];
+ Vec3<double> pt;
+ Voxel voxel;
+ size_t sp = 0;
+ size_t sn = 0;
+ for (size_t v = 0; v < nVoxels; ++v) {
+ voxel = m_voxels[v];
+ pt = GetPoint(voxel);
+ d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d;
+ // if (d >= 0.0 && d <= d0) positivePts->PushBack(pt);
+ // else if (d < 0.0 && -d <= d0) negativePts->PushBack(pt);
+ if (d >= 0.0) {
+ if (d <= d0) {
+ GetPoints(voxel, pts);
+ for (int32_t k = 0; k < 8; ++k) {
+ positivePts->PushBack(pts[k]);
+ }
+ }
+ else {
+ if (++sp == sampling) {
+ // positivePts->PushBack(pt);
+ GetPoints(voxel, pts);
+ for (int32_t k = 0; k < 8; ++k) {
+ positivePts->PushBack(pts[k]);
+ }
+ sp = 0;
+ }
+ }
+ }
+ else {
+ if (-d <= d0) {
+ GetPoints(voxel, pts);
+ for (int32_t k = 0; k < 8; ++k) {
+ negativePts->PushBack(pts[k]);
+ }
+ }
+ else {
+ if (++sn == sampling) {
+ // negativePts->PushBack(pt);
+ GetPoints(voxel, pts);
+ for (int32_t k = 0; k < 8; ++k) {
+ negativePts->PushBack(pts[k]);
+ }
+ sn = 0;
+ }
+ }
+ }
+ }
+}
+void VoxelSet::ComputeExteriorPoints(const Plane& plane,
+ const Mesh& mesh,
+ SArray<Vec3<double> >* const exteriorPts) const
+{
+ const size_t nVoxels = m_voxels.Size();
+ if (nVoxels == 0)
+ return;
+ double d;
+ Vec3<double> pt;
+ Vec3<double> pts[8];
+ Voxel voxel;
+ for (size_t v = 0; v < nVoxels; ++v) {
+ voxel = m_voxels[v];
+ pt = GetPoint(voxel);
+ d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d;
+ if (d >= 0.0) {
+ if (!mesh.IsInside(pt)) {
+ GetPoints(voxel, pts);
+ for (int32_t k = 0; k < 8; ++k) {
+ exteriorPts->PushBack(pts[k]);
+ }
+ }
+ }
+ }
+}
+void VoxelSet::ComputeClippedVolumes(const Plane& plane,
+ double& positiveVolume,
+ double& negativeVolume) const
+{
+ negativeVolume = 0.0;
+ positiveVolume = 0.0;
+ const size_t nVoxels = m_voxels.Size();
+ if (nVoxels == 0)
+ return;
+ double d;
+ Vec3<double> pt;
+ size_t nPositiveVoxels = 0;
+ for (size_t v = 0; v < nVoxels; ++v) {
+ pt = GetPoint(m_voxels[v]);
+ d = plane.m_a * pt[0] + plane.m_b * pt[1] + plane.m_c * pt[2] + plane.m_d;
+ nPositiveVoxels += (d >= 0.0);
+ }
+ size_t nNegativeVoxels = nVoxels - nPositiveVoxels;
+ positiveVolume = m_unitVolume * nPositiveVoxels;
+ negativeVolume = m_unitVolume * nNegativeVoxels;
+}
+void VoxelSet::SelectOnSurface(PrimitiveSet* const onSurfP) const
+{
+ VoxelSet* const onSurf = (VoxelSet*)onSurfP;
+ const size_t nVoxels = m_voxels.Size();
+ if (nVoxels == 0)
+ return;
+
+ for (int32_t h = 0; h < 3; ++h) {
+ onSurf->m_minBB[h] = m_minBB[h];
+ }
+ onSurf->m_voxels.Resize(0);
+ onSurf->m_scale = m_scale;
+ onSurf->m_unitVolume = m_unitVolume;
+ onSurf->m_numVoxelsOnSurface = 0;
+ onSurf->m_numVoxelsInsideSurface = 0;
+ Voxel voxel;
+ for (size_t v = 0; v < nVoxels; ++v) {
+ voxel = m_voxels[v];
+ if (voxel.m_data == PRIMITIVE_ON_SURFACE) {
+ onSurf->m_voxels.PushBack(voxel);
+ ++onSurf->m_numVoxelsOnSurface;
+ }
+ }
+}
+void VoxelSet::Clip(const Plane& plane,
+ PrimitiveSet* const positivePartP,
+ PrimitiveSet* const negativePartP) const
+{
+ VoxelSet* const positivePart = (VoxelSet*)positivePartP;
+ VoxelSet* const negativePart = (VoxelSet*)negativePartP;
+ const size_t nVoxels = m_voxels.Size();
+ if (nVoxels == 0)
+ return;
+
+ for (int32_t h = 0; h < 3; ++h) {
+ negativePart->m_minBB[h] = positivePart->m_minBB[h] = m_minBB[h];
+ }
+ positivePart->m_voxels.Resize(0);
+ negativePart->m_voxels.Resize(0);
+ positivePart->m_voxels.Allocate(nVoxels);
+ negativePart->m_voxels.Allocate(nVoxels);
+ negativePart->m_scale = positivePart->m_scale = m_scale;
+ negativePart->m_unitVolume = positivePart->m_unitVolume = m_unitVolume;
+ negativePart->m_numVoxelsOnSurface = positivePart->m_numVoxelsOnSurface = 0;
+ negativePart->m_numVoxelsInsideSurface = positivePart->m_numVoxelsInsideSurface = 0;
+
+ double d;
+ Vec3<double> 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<double> pts[8];
+ for (size_t v = 0; v < nVoxels; ++v) {
+ voxel = m_voxels[v];
+ if (voxel.m_data == value) {
+ GetPoints(voxel, pts);
+ int32_t s = (int32_t)mesh.GetNPoints();
+ for (int32_t k = 0; k < 8; ++k) {
+ mesh.AddPoint(pts[k]);
+ }
+ mesh.AddTriangle(Vec3<int32_t>(s + 0, s + 2, s + 1));
+ mesh.AddTriangle(Vec3<int32_t>(s + 0, s + 3, s + 2));
+ mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 5, s + 6));
+ mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 6, s + 7));
+ mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 6, s + 2));
+ mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 2, s + 3));
+ mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 1, s + 5));
+ mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 0, s + 1));
+ mesh.AddTriangle(Vec3<int32_t>(s + 6, s + 5, s + 1));
+ mesh.AddTriangle(Vec3<int32_t>(s + 6, s + 1, s + 2));
+ mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 0, s + 4));
+ mesh.AddTriangle(Vec3<int32_t>(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<Vec3<short> > fifo;
+ Vec3<short> current;
+ short a, b, c;
+ for (size_t i = i0; i < i1; ++i) {
+ for (size_t j = j0; j < j1; ++j) {
+ for (size_t k = k0; k < k1; ++k) {
+
+ if (GetVoxel(i, j, k) == PRIMITIVE_UNDEFINED) {
+ current[0] = (short)i;
+ current[1] = (short)j;
+ current[2] = (short)k;
+ fifo.push(current);
+ GetVoxel(current[0], current[1], current[2]) = PRIMITIVE_OUTSIDE_SURFACE;
+ ++m_numVoxelsOutsideSurface;
+ while (fifo.size() > 0) {
+ current = fifo.front();
+ fifo.pop();
+ for (int32_t h = 0; h < 6; ++h) {
+ a = current[0] + neighbours[h][0];
+ b = current[1] + neighbours[h][1];
+ c = current[2] + neighbours[h][2];
+ if (a < 0 || a >= (int32_t)m_dim[0] || b < 0 || b >= (int32_t)m_dim[1] || c < 0 || c >= (int32_t)m_dim[2]) {
+ continue;
+ }
+ unsigned char& v = GetVoxel(a, b, c);
+ if (v == PRIMITIVE_UNDEFINED) {
+ v = PRIMITIVE_OUTSIDE_SURFACE;
+ ++m_numVoxelsOutsideSurface;
+ fifo.push(Vec3<short>(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<double> p0((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale);
+ Vec3<double> p1((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k - 0.5) * m_scale);
+ Vec3<double> p2((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale);
+ Vec3<double> p3((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k - 0.5) * m_scale);
+ Vec3<double> p4((i - 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale);
+ Vec3<double> p5((i + 0.5) * m_scale, (j - 0.5) * m_scale, (k + 0.5) * m_scale);
+ Vec3<double> p6((i + 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale);
+ Vec3<double> p7((i - 0.5) * m_scale, (j + 0.5) * m_scale, (k + 0.5) * m_scale);
+ int32_t s = (int32_t)mesh.GetNPoints();
+ mesh.AddPoint(p0 + m_minBB);
+ mesh.AddPoint(p1 + m_minBB);
+ mesh.AddPoint(p2 + m_minBB);
+ mesh.AddPoint(p3 + m_minBB);
+ mesh.AddPoint(p4 + m_minBB);
+ mesh.AddPoint(p5 + m_minBB);
+ mesh.AddPoint(p6 + m_minBB);
+ mesh.AddPoint(p7 + m_minBB);
+ mesh.AddTriangle(Vec3<int32_t>(s + 0, s + 2, s + 1));
+ mesh.AddTriangle(Vec3<int32_t>(s + 0, s + 3, s + 2));
+ mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 5, s + 6));
+ mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 6, s + 7));
+ mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 6, s + 2));
+ mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 2, s + 3));
+ mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 1, s + 5));
+ mesh.AddTriangle(Vec3<int32_t>(s + 4, s + 0, s + 1));
+ mesh.AddTriangle(Vec3<int32_t>(s + 6, s + 5, s + 1));
+ mesh.AddTriangle(Vec3<int32_t>(s + 6, s + 1, s + 2));
+ mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 0, s + 4));
+ mesh.AddTriangle(Vec3<int32_t>(s + 7, s + 3, s + 0));
+ }
+ }
+ }
+ }
+}
+void Volume::Convert(VoxelSet& vset) const
+{
+ for (int32_t h = 0; h < 3; ++h) {
+ vset.m_minBB[h] = m_minBB[h];
+ }
+ vset.m_voxels.Allocate(m_numVoxelsInsideSurface + m_numVoxelsOnSurface);
+ vset.m_scale = m_scale;
+ vset.m_unitVolume = m_scale * m_scale * m_scale;
+ const short i0 = (short)m_dim[0];
+ const short j0 = (short)m_dim[1];
+ const short k0 = (short)m_dim[2];
+ Voxel voxel;
+ vset.m_numVoxelsOnSurface = 0;
+ vset.m_numVoxelsInsideSurface = 0;
+ for (short i = 0; i < i0; ++i) {
+ for (short j = 0; j < j0; ++j) {
+ for (short k = 0; k < k0; ++k) {
+ const unsigned char& value = GetVoxel(i, j, k);
+ if (value == PRIMITIVE_INSIDE_SURFACE) {
+ voxel.m_coord[0] = i;
+ voxel.m_coord[1] = j;
+ voxel.m_coord[2] = k;
+ voxel.m_data = PRIMITIVE_INSIDE_SURFACE;
+ vset.m_voxels.PushBack(voxel);
+ ++vset.m_numVoxelsInsideSurface;
+ }
+ else if (value == PRIMITIVE_ON_SURFACE) {
+ voxel.m_coord[0] = i;
+ voxel.m_coord[1] = j;
+ voxel.m_coord[2] = k;
+ voxel.m_data = PRIMITIVE_ON_SURFACE;
+ vset.m_voxels.PushBack(voxel);
+ ++vset.m_numVoxelsOnSurface;
+ }
+ }
+ }
+ }
+}
+
+void Volume::Convert(TetrahedronSet& tset) const
+{
+ tset.m_tetrahedra.Allocate(5 * (m_numVoxelsInsideSurface + m_numVoxelsOnSurface));
+ tset.m_scale = m_scale;
+ const short i0 = (short)m_dim[0];
+ const short j0 = (short)m_dim[1];
+ const short k0 = (short)m_dim[2];
+ tset.m_numTetrahedraOnSurface = 0;
+ tset.m_numTetrahedraInsideSurface = 0;
+ Tetrahedron tetrahedron;
+ for (short i = 0; i < i0; ++i) {
+ for (short j = 0; j < j0; ++j) {
+ for (short k = 0; k < k0; ++k) {
+ const unsigned char& value = GetVoxel(i, j, k);
+ if (value == PRIMITIVE_INSIDE_SURFACE || value == PRIMITIVE_ON_SURFACE) {
+ tetrahedron.m_data = value;
+ Vec3<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> barycenter(0.0);
+ size_t nVoxels = 0;
+ for (short i = 0; i < i0; ++i) {
+ for (short j = 0; j < j0; ++j) {
+ for (short k = 0; k < k0; ++k) {
+ const unsigned char& value = GetVoxel(i, j, k);
+ if (value == PRIMITIVE_INSIDE_SURFACE || value == PRIMITIVE_ON_SURFACE) {
+ barycenter[0] += i;
+ barycenter[1] += j;
+ barycenter[2] += k;
+ ++nVoxels;
+ }
+ }
+ }
+ }
+ barycenter /= (double)nVoxels;
+
+ double covMat[3][3] = { { 0.0, 0.0, 0.0 },
+ { 0.0, 0.0, 0.0 },
+ { 0.0, 0.0, 0.0 } };
+ double x, y, z;
+ for (short i = 0; i < i0; ++i) {
+ for (short j = 0; j < j0; ++j) {
+ for (short k = 0; k < k0; ++k) {
+ const unsigned char& value = GetVoxel(i, j, k);
+ if (value == PRIMITIVE_INSIDE_SURFACE || value == PRIMITIVE_ON_SURFACE) {
+ x = i - barycenter[0];
+ y = j - barycenter[1];
+ z = k - barycenter[2];
+ covMat[0][0] += x * x;
+ covMat[1][1] += y * y;
+ covMat[2][2] += z * z;
+ covMat[0][1] += x * y;
+ covMat[0][2] += x * z;
+ covMat[1][2] += y * z;
+ }
+ }
+ }
+ }
+ covMat[1][0] = covMat[0][1];
+ covMat[2][0] = covMat[0][2];
+ covMat[2][1] = covMat[1][2];
+ double D[3][3];
+ Diagonalize(covMat, rot, D);
+}
+TetrahedronSet::TetrahedronSet()
+{
+ m_minBB[0] = m_minBB[1] = m_minBB[2] = 0.0;
+ m_maxBB[0] = m_maxBB[1] = m_maxBB[2] = 1.0;
+ m_barycenter[0] = m_barycenter[1] = m_barycenter[2] = 0.0;
+ m_scale = 1.0;
+ m_numTetrahedraOnSurface = 0;
+ m_numTetrahedraInsideSurface = 0;
+ memset(m_Q, 0, sizeof(double) * 9);
+ memset(m_D, 0, sizeof(double) * 9);
+}
+TetrahedronSet::~TetrahedronSet(void)
+{
+}
+void TetrahedronSet::ComputeBB()
+{
+ const size_t nTetrahedra = m_tetrahedra.Size();
+ if (nTetrahedra == 0)
+ return;
+
+ for (int32_t h = 0; h < 3; ++h) {
+ m_minBB[h] = m_maxBB[h] = m_tetrahedra[0].m_pts[0][h];
+ m_barycenter[h] = 0.0;
+ }
+ for (size_t p = 0; p < nTetrahedra; ++p) {
+ for (int32_t i = 0; i < 4; ++i) {
+ for (int32_t h = 0; h < 3; ++h) {
+ if (m_minBB[h] > m_tetrahedra[p].m_pts[i][h])
+ m_minBB[h] = m_tetrahedra[p].m_pts[i][h];
+ if (m_maxBB[h] < m_tetrahedra[p].m_pts[i][h])
+ m_maxBB[h] = m_tetrahedra[p].m_pts[i][h];
+ m_barycenter[h] += m_tetrahedra[p].m_pts[i][h];
+ }
+ }
+ }
+ m_barycenter /= (double)(4 * nTetrahedra);
+}
+void TetrahedronSet::ComputeConvexHull(Mesh& meshCH, const size_t sampling) const
+{
+ const size_t CLUSTER_SIZE = 65536;
+ const size_t nTetrahedra = m_tetrahedra.Size();
+ if (nTetrahedra == 0)
+ return;
+
+ SArray<Vec3<double> > cpoints;
+
+ Vec3<double>* points = new Vec3<double>[CLUSTER_SIZE];
+ size_t p = 0;
+ while (p < nTetrahedra) {
+ size_t q = 0;
+ size_t s = 0;
+ while (q < CLUSTER_SIZE && p < nTetrahedra) {
+ if (m_tetrahedra[p].m_data == PRIMITIVE_ON_SURFACE) {
+ ++s;
+ if (s == sampling) {
+ s = 0;
+ for (int32_t a = 0; a < 4; ++a) {
+ points[q++] = m_tetrahedra[p].m_pts[a];
+ for (int32_t xx = 0; xx < 3; ++xx) {
+ assert(m_tetrahedra[p].m_pts[a][xx] + EPS >= m_minBB[xx]);
+ assert(m_tetrahedra[p].m_pts[a][xx] <= m_maxBB[xx] + EPS);
+ }
+ }
+ }
+ }
+ ++p;
+ }
+ btConvexHullComputer ch;
+ ch.compute((double*)points, 3 * sizeof(double), (int32_t)q, -1.0, -1.0);
+ for (int32_t v = 0; v < ch.vertices.size(); v++) {
+ cpoints.PushBack(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ()));
+ }
+ }
+ delete[] points;
+
+ points = cpoints.Data();
+ btConvexHullComputer ch;
+ ch.compute((double*)points, 3 * sizeof(double), (int32_t)cpoints.Size(), -1.0, -1.0);
+ meshCH.ResizePoints(0);
+ meshCH.ResizeTriangles(0);
+ for (int32_t v = 0; v < ch.vertices.size(); v++) {
+ meshCH.AddPoint(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ()));
+ }
+ const int32_t nt = ch.faces.size();
+ for (int32_t t = 0; t < nt; ++t) {
+ const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]);
+ int32_t a = sourceEdge->getSourceVertex();
+ int32_t b = sourceEdge->getTargetVertex();
+ const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace();
+ int32_t c = edge->getTargetVertex();
+ while (c != a) {
+ meshCH.AddTriangle(Vec3<int32_t>(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<double> tmp = tetrahedron.m_pts[0];
+ tetrahedron.m_pts[0] = tetrahedron.m_pts[1];
+ tetrahedron.m_pts[1] = tmp;
+ }
+
+ for (int32_t a = 0; a < 4; ++a) {
+ for (int32_t xx = 0; xx < 3; ++xx) {
+ assert(tetrahedron.m_pts[a][xx] + EPS >= m_minBB[xx]);
+ assert(tetrahedron.m_pts[a][xx] <= m_maxBB[xx] + EPS);
+ }
+ }
+ m_tetrahedra.PushBack(tetrahedron);
+ return true;
+}
+
+void TetrahedronSet::AddClippedTetrahedra(const Vec3<double> (&pts)[10], const int32_t nPts)
+{
+ const int32_t tetF[4][3] = { { 0, 1, 2 }, { 2, 1, 3 }, { 3, 1, 0 }, { 3, 0, 2 } };
+ if (nPts < 4) {
+ return;
+ }
+ else if (nPts == 4) {
+ Tetrahedron tetrahedron;
+ tetrahedron.m_data = PRIMITIVE_ON_SURFACE;
+ tetrahedron.m_pts[0] = pts[0];
+ tetrahedron.m_pts[1] = pts[1];
+ tetrahedron.m_pts[2] = pts[2];
+ tetrahedron.m_pts[3] = pts[3];
+ if (Add(tetrahedron)) {
+ ++m_numTetrahedraOnSurface;
+ }
+ }
+ else if (nPts == 5) {
+ const int32_t tet[15][4] = {
+ { 0, 1, 2, 3 }, { 1, 2, 3, 4 }, { 0, 2, 3, 4 }, { 0, 1, 3, 4 }, { 0, 1, 2, 4 },
+ };
+ const int32_t rem[5] = { 4, 0, 1, 2, 3 };
+ double maxVol = 0.0;
+ int32_t h0 = -1;
+ Tetrahedron tetrahedron0;
+ tetrahedron0.m_data = PRIMITIVE_ON_SURFACE;
+ for (int32_t h = 0; h < 5; ++h) {
+ double v = ComputeVolume4(pts[tet[h][0]], pts[tet[h][1]], pts[tet[h][2]], pts[tet[h][3]]);
+ if (v > maxVol) {
+ h0 = h;
+ tetrahedron0.m_pts[0] = pts[tet[h][0]];
+ tetrahedron0.m_pts[1] = pts[tet[h][1]];
+ tetrahedron0.m_pts[2] = pts[tet[h][2]];
+ tetrahedron0.m_pts[3] = pts[tet[h][3]];
+ maxVol = v;
+ }
+ else if (-v > maxVol) {
+ h0 = h;
+ tetrahedron0.m_pts[0] = pts[tet[h][1]];
+ tetrahedron0.m_pts[1] = pts[tet[h][0]];
+ tetrahedron0.m_pts[2] = pts[tet[h][2]];
+ tetrahedron0.m_pts[3] = pts[tet[h][3]];
+ maxVol = -v;
+ }
+ }
+ if (h0 == -1)
+ return;
+ if (Add(tetrahedron0)) {
+ ++m_numTetrahedraOnSurface;
+ }
+ else {
+ return;
+ }
+ int32_t a = rem[h0];
+ maxVol = 0.0;
+ int32_t h1 = -1;
+ Tetrahedron tetrahedron1;
+ tetrahedron1.m_data = PRIMITIVE_ON_SURFACE;
+ for (int32_t h = 0; h < 4; ++h) {
+ double v = ComputeVolume4(pts[a], tetrahedron0.m_pts[tetF[h][0]], tetrahedron0.m_pts[tetF[h][1]], tetrahedron0.m_pts[tetF[h][2]]);
+ if (v > maxVol) {
+ h1 = h;
+ tetrahedron1.m_pts[0] = pts[a];
+ tetrahedron1.m_pts[1] = tetrahedron0.m_pts[tetF[h][0]];
+ tetrahedron1.m_pts[2] = tetrahedron0.m_pts[tetF[h][1]];
+ tetrahedron1.m_pts[3] = tetrahedron0.m_pts[tetF[h][2]];
+ maxVol = v;
+ }
+ }
+ if (h1 == -1 && Add(tetrahedron1)) {
+ ++m_numTetrahedraOnSurface;
+ }
+ }
+ else if (nPts == 6) {
+
+ const int32_t tet[15][4] = { { 2, 3, 4, 5 }, { 1, 3, 4, 5 }, { 1, 2, 4, 5 }, { 1, 2, 3, 5 }, { 1, 2, 3, 4 },
+ { 0, 3, 4, 5 }, { 0, 2, 4, 5 }, { 0, 2, 3, 5 }, { 0, 2, 3, 4 }, { 0, 1, 4, 5 },
+ { 0, 1, 3, 5 }, { 0, 1, 3, 4 }, { 0, 1, 2, 5 }, { 0, 1, 2, 4 }, { 0, 1, 2, 3 } };
+ const int32_t rem[15][2] = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 0, 4 }, { 0, 5 },
+ { 1, 2 }, { 1, 3 }, { 1, 4 }, { 1, 5 }, { 2, 3 },
+ { 2, 4 }, { 2, 5 }, { 3, 4 }, { 3, 5 }, { 4, 5 } };
+ double maxVol = 0.0;
+ int32_t h0 = -1;
+ Tetrahedron tetrahedron0;
+ tetrahedron0.m_data = PRIMITIVE_ON_SURFACE;
+ for (int32_t h = 0; h < 15; ++h) {
+ double v = ComputeVolume4(pts[tet[h][0]], pts[tet[h][1]], pts[tet[h][2]], pts[tet[h][3]]);
+ if (v > maxVol) {
+ h0 = h;
+ tetrahedron0.m_pts[0] = pts[tet[h][0]];
+ tetrahedron0.m_pts[1] = pts[tet[h][1]];
+ tetrahedron0.m_pts[2] = pts[tet[h][2]];
+ tetrahedron0.m_pts[3] = pts[tet[h][3]];
+ maxVol = v;
+ }
+ else if (-v > maxVol) {
+ h0 = h;
+ tetrahedron0.m_pts[0] = pts[tet[h][1]];
+ tetrahedron0.m_pts[1] = pts[tet[h][0]];
+ tetrahedron0.m_pts[2] = pts[tet[h][2]];
+ tetrahedron0.m_pts[3] = pts[tet[h][3]];
+ maxVol = -v;
+ }
+ }
+ if (h0 == -1)
+ return;
+ if (Add(tetrahedron0)) {
+ ++m_numTetrahedraOnSurface;
+ }
+ else {
+ return;
+ }
+
+ int32_t a0 = rem[h0][0];
+ int32_t a1 = rem[h0][1];
+ int32_t h1 = -1;
+ Tetrahedron tetrahedron1;
+ tetrahedron1.m_data = PRIMITIVE_ON_SURFACE;
+ maxVol = 0.0;
+ for (int32_t h = 0; h < 4; ++h) {
+ double v = ComputeVolume4(pts[a0], tetrahedron0.m_pts[tetF[h][0]], tetrahedron0.m_pts[tetF[h][1]], tetrahedron0.m_pts[tetF[h][2]]);
+ if (v > maxVol) {
+ h1 = h;
+ tetrahedron1.m_pts[0] = pts[a0];
+ tetrahedron1.m_pts[1] = tetrahedron0.m_pts[tetF[h][0]];
+ tetrahedron1.m_pts[2] = tetrahedron0.m_pts[tetF[h][1]];
+ tetrahedron1.m_pts[3] = tetrahedron0.m_pts[tetF[h][2]];
+ maxVol = v;
+ }
+ }
+ if (h1 != -1 && Add(tetrahedron1)) {
+ ++m_numTetrahedraOnSurface;
+ }
+ else {
+ h1 = -1;
+ }
+ maxVol = 0.0;
+ int32_t h2 = -1;
+ Tetrahedron tetrahedron2;
+ tetrahedron2.m_data = PRIMITIVE_ON_SURFACE;
+ for (int32_t h = 0; h < 4; ++h) {
+ double v = ComputeVolume4(pts[a0], tetrahedron0.m_pts[tetF[h][0]], tetrahedron0.m_pts[tetF[h][1]], tetrahedron0.m_pts[tetF[h][2]]);
+ if (h == h1)
+ continue;
+ if (v > maxVol) {
+ h2 = h;
+ tetrahedron2.m_pts[0] = pts[a1];
+ tetrahedron2.m_pts[1] = tetrahedron0.m_pts[tetF[h][0]];
+ tetrahedron2.m_pts[2] = tetrahedron0.m_pts[tetF[h][1]];
+ tetrahedron2.m_pts[3] = tetrahedron0.m_pts[tetF[h][2]];
+ maxVol = v;
+ }
+ }
+ if (h1 != -1) {
+ for (int32_t h = 0; h < 4; ++h) {
+ double v = ComputeVolume4(pts[a1], tetrahedron1.m_pts[tetF[h][0]], tetrahedron1.m_pts[tetF[h][1]], tetrahedron1.m_pts[tetF[h][2]]);
+ if (h == 1)
+ continue;
+ if (v > maxVol) {
+ h2 = h;
+ tetrahedron2.m_pts[0] = pts[a1];
+ tetrahedron2.m_pts[1] = tetrahedron1.m_pts[tetF[h][0]];
+ tetrahedron2.m_pts[2] = tetrahedron1.m_pts[tetF[h][1]];
+ tetrahedron2.m_pts[3] = tetrahedron1.m_pts[tetF[h][2]];
+ maxVol = v;
+ }
+ }
+ }
+ if (h2 != -1 && Add(tetrahedron2)) {
+ ++m_numTetrahedraOnSurface;
+ }
+ }
+ else {
+ assert(0);
+ }
+}
+
+void TetrahedronSet::Intersect(const Plane& plane,
+ SArray<Vec3<double> >* const positivePts,
+ SArray<Vec3<double> >* 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<Vec3<double> >* const exteriorPts) const
+{
+}
+void TetrahedronSet::ComputeClippedVolumes(const Plane& plane,
+ double& positiveVolume,
+ double& negativeVolume) const
+{
+ const size_t nTetrahedra = m_tetrahedra.Size();
+ if (nTetrahedra == 0)
+ return;
+}
+
+void TetrahedronSet::SelectOnSurface(PrimitiveSet* const onSurfP) const
+{
+ TetrahedronSet* const onSurf = (TetrahedronSet*)onSurfP;
+ const size_t nTetrahedra = m_tetrahedra.Size();
+ if (nTetrahedra == 0)
+ return;
+ onSurf->m_tetrahedra.Resize(0);
+ onSurf->m_scale = m_scale;
+ onSurf->m_numTetrahedraOnSurface = 0;
+ onSurf->m_numTetrahedraInsideSurface = 0;
+ onSurf->m_barycenter = m_barycenter;
+ onSurf->m_minBB = m_minBB;
+ onSurf->m_maxBB = m_maxBB;
+ for (int32_t i = 0; i < 3; ++i) {
+ for (int32_t j = 0; j < 3; ++j) {
+ onSurf->m_Q[i][j] = m_Q[i][j];
+ onSurf->m_D[i][j] = m_D[i][j];
+ }
+ }
+ Tetrahedron tetrahedron;
+ for (size_t v = 0; v < nTetrahedra; ++v) {
+ tetrahedron = m_tetrahedra[v];
+ if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) {
+ onSurf->m_tetrahedra.PushBack(tetrahedron);
+ ++onSurf->m_numTetrahedraOnSurface;
+ }
+ }
+}
+void TetrahedronSet::Clip(const Plane& plane,
+ PrimitiveSet* const positivePartP,
+ PrimitiveSet* const negativePartP) const
+{
+ TetrahedronSet* const positivePart = (TetrahedronSet*)positivePartP;
+ TetrahedronSet* const negativePart = (TetrahedronSet*)negativePartP;
+ const size_t nTetrahedra = m_tetrahedra.Size();
+ if (nTetrahedra == 0)
+ return;
+ positivePart->m_tetrahedra.Resize(0);
+ negativePart->m_tetrahedra.Resize(0);
+ positivePart->m_tetrahedra.Allocate(nTetrahedra);
+ negativePart->m_tetrahedra.Allocate(nTetrahedra);
+ negativePart->m_scale = positivePart->m_scale = m_scale;
+ negativePart->m_numTetrahedraOnSurface = positivePart->m_numTetrahedraOnSurface = 0;
+ negativePart->m_numTetrahedraInsideSurface = positivePart->m_numTetrahedraInsideSurface = 0;
+ negativePart->m_barycenter = m_barycenter;
+ positivePart->m_barycenter = m_barycenter;
+ negativePart->m_minBB = m_minBB;
+ positivePart->m_minBB = m_minBB;
+ negativePart->m_maxBB = m_maxBB;
+ positivePart->m_maxBB = m_maxBB;
+ for (int32_t i = 0; i < 3; ++i) {
+ for (int32_t j = 0; j < 3; ++j) {
+ negativePart->m_Q[i][j] = positivePart->m_Q[i][j] = m_Q[i][j];
+ negativePart->m_D[i][j] = positivePart->m_D[i][j] = m_D[i][j];
+ }
+ }
+
+ Tetrahedron tetrahedron;
+ double delta, alpha;
+ int32_t sign[4];
+ int32_t npos, nneg;
+ Vec3<double> posPts[10];
+ Vec3<double> negPts[10];
+ Vec3<double> P0, P1, M;
+ const Vec3<double> n(plane.m_a, plane.m_b, plane.m_c);
+ const int32_t edges[6][2] = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 1, 2 }, { 1, 3 }, { 2, 3 } };
+ double dist;
+ for (size_t v = 0; v < nTetrahedra; ++v) {
+ tetrahedron = m_tetrahedra[v];
+ npos = nneg = 0;
+ for (int32_t i = 0; i < 4; ++i) {
+ dist = plane.m_a * tetrahedron.m_pts[i][0] + plane.m_b * tetrahedron.m_pts[i][1] + plane.m_c * tetrahedron.m_pts[i][2] + plane.m_d;
+ if (dist > 0.0) {
+ sign[i] = 1;
+ posPts[npos] = tetrahedron.m_pts[i];
+ ++npos;
+ }
+ else {
+ sign[i] = -1;
+ negPts[nneg] = tetrahedron.m_pts[i];
+ ++nneg;
+ }
+ }
+
+ if (npos == 4) {
+ positivePart->Add(tetrahedron);
+ if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) {
+ ++positivePart->m_numTetrahedraOnSurface;
+ }
+ else {
+ ++positivePart->m_numTetrahedraInsideSurface;
+ }
+ }
+ else if (nneg == 4) {
+ negativePart->Add(tetrahedron);
+ if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) {
+ ++negativePart->m_numTetrahedraOnSurface;
+ }
+ else {
+ ++negativePart->m_numTetrahedraInsideSurface;
+ }
+ }
+ else {
+ int32_t nnew = 0;
+ for (int32_t j = 0; j < 6; ++j) {
+ if (sign[edges[j][0]] * sign[edges[j][1]] == -1) {
+ P0 = tetrahedron.m_pts[edges[j][0]];
+ P1 = tetrahedron.m_pts[edges[j][1]];
+ delta = (P0 - P1) * n;
+ alpha = -(plane.m_d + (n * P1)) / delta;
+ assert(alpha >= 0.0 && alpha <= 1.0);
+ M = alpha * P0 + (1 - alpha) * P1;
+ for (int32_t xx = 0; xx < 3; ++xx) {
+ assert(M[xx] + EPS >= m_minBB[xx]);
+ assert(M[xx] <= m_maxBB[xx] + EPS);
+ }
+ posPts[npos++] = M;
+ negPts[nneg++] = M;
+ ++nnew;
+ }
+ }
+ negativePart->AddClippedTetrahedra(negPts, nneg);
+ positivePart->AddClippedTetrahedra(posPts, npos);
+ }
+ }
+}
+void TetrahedronSet::Convert(Mesh& mesh, const VOXEL_VALUE value) const
+{
+ const size_t nTetrahedra = m_tetrahedra.Size();
+ if (nTetrahedra == 0)
+ return;
+ for (size_t v = 0; v < nTetrahedra; ++v) {
+ const Tetrahedron& tetrahedron = m_tetrahedra[v];
+ if (tetrahedron.m_data == value) {
+ int32_t s = (int32_t)mesh.GetNPoints();
+ mesh.AddPoint(tetrahedron.m_pts[0]);
+ mesh.AddPoint(tetrahedron.m_pts[1]);
+ mesh.AddPoint(tetrahedron.m_pts[2]);
+ mesh.AddPoint(tetrahedron.m_pts[3]);
+ mesh.AddTriangle(Vec3<int32_t>(s + 0, s + 1, s + 2));
+ mesh.AddTriangle(Vec3<int32_t>(s + 2, s + 1, s + 3));
+ mesh.AddTriangle(Vec3<int32_t>(s + 3, s + 1, s + 0));
+ mesh.AddTriangle(Vec3<int32_t>(s + 3, s + 0, s + 2));
+ }
+ }
+}
+const double TetrahedronSet::ComputeVolume() const
+{
+ const size_t nTetrahedra = m_tetrahedra.Size();
+ if (nTetrahedra == 0)
+ return 0.0;
+ double volume = 0.0;
+ for (size_t v = 0; v < nTetrahedra; ++v) {
+ const Tetrahedron& tetrahedron = m_tetrahedra[v];
+ volume += fabs(ComputeVolume4(tetrahedron.m_pts[0], tetrahedron.m_pts[1], tetrahedron.m_pts[2], tetrahedron.m_pts[3]));
+ }
+ return volume / 6.0;
+}
+const double TetrahedronSet::ComputeMaxVolumeError() const
+{
+ const size_t nTetrahedra = m_tetrahedra.Size();
+ if (nTetrahedra == 0)
+ return 0.0;
+ double volume = 0.0;
+ for (size_t v = 0; v < nTetrahedra; ++v) {
+ const Tetrahedron& tetrahedron = m_tetrahedra[v];
+ if (tetrahedron.m_data == PRIMITIVE_ON_SURFACE) {
+ volume += fabs(ComputeVolume4(tetrahedron.m_pts[0], tetrahedron.m_pts[1], tetrahedron.m_pts[2], tetrahedron.m_pts[3]));
+ }
+ }
+ return volume / 6.0;
+}
+void TetrahedronSet::RevertAlignToPrincipalAxes()
+{
+ const size_t nTetrahedra = m_tetrahedra.Size();
+ if (nTetrahedra == 0)
+ return;
+ double x, y, z;
+ for (size_t v = 0; v < nTetrahedra; ++v) {
+ Tetrahedron& tetrahedron = m_tetrahedra[v];
+ for (int32_t i = 0; i < 4; ++i) {
+ x = tetrahedron.m_pts[i][0] - m_barycenter[0];
+ y = tetrahedron.m_pts[i][1] - m_barycenter[1];
+ z = tetrahedron.m_pts[i][2] - m_barycenter[2];
+ tetrahedron.m_pts[i][0] = m_Q[0][0] * x + m_Q[0][1] * y + m_Q[0][2] * z + m_barycenter[0];
+ tetrahedron.m_pts[i][1] = m_Q[1][0] * x + m_Q[1][1] * y + m_Q[1][2] * z + m_barycenter[1];
+ tetrahedron.m_pts[i][2] = m_Q[2][0] * x + m_Q[2][1] * y + m_Q[2][2] * z + m_barycenter[2];
+ }
+ }
+ ComputeBB();
+}
+void TetrahedronSet::ComputePrincipalAxes()
+{
+ const size_t nTetrahedra = m_tetrahedra.Size();
+ if (nTetrahedra == 0)
+ return;
+ double covMat[3][3] = { { 0.0, 0.0, 0.0 },
+ { 0.0, 0.0, 0.0 },
+ { 0.0, 0.0, 0.0 } };
+ double x, y, z;
+ for (size_t v = 0; v < nTetrahedra; ++v) {
+ Tetrahedron& tetrahedron = m_tetrahedra[v];
+ for (int32_t i = 0; i < 4; ++i) {
+ x = tetrahedron.m_pts[i][0] - m_barycenter[0];
+ y = tetrahedron.m_pts[i][1] - m_barycenter[1];
+ z = tetrahedron.m_pts[i][2] - m_barycenter[2];
+ covMat[0][0] += x * x;
+ covMat[1][1] += y * y;
+ covMat[2][2] += z * z;
+ covMat[0][1] += x * y;
+ covMat[0][2] += x * z;
+ covMat[1][2] += y * z;
+ }
+ }
+ double n = nTetrahedra * 4.0;
+ covMat[0][0] /= n;
+ covMat[1][1] /= n;
+ covMat[2][2] /= n;
+ covMat[0][1] /= n;
+ covMat[0][2] /= n;
+ covMat[1][2] /= n;
+ covMat[1][0] = covMat[0][1];
+ covMat[2][0] = covMat[0][2];
+ covMat[2][1] = covMat[1][2];
+ Diagonalize(covMat, m_Q, m_D);
+}
+void TetrahedronSet::AlignToPrincipalAxes()
+{
+ const size_t nTetrahedra = m_tetrahedra.Size();
+ if (nTetrahedra == 0)
+ return;
+ double x, y, z;
+ for (size_t v = 0; v < nTetrahedra; ++v) {
+ Tetrahedron& tetrahedron = m_tetrahedra[v];
+ for (int32_t i = 0; i < 4; ++i) {
+ x = tetrahedron.m_pts[i][0] - m_barycenter[0];
+ y = tetrahedron.m_pts[i][1] - m_barycenter[1];
+ z = tetrahedron.m_pts[i][2] - m_barycenter[2];
+ tetrahedron.m_pts[i][0] = m_Q[0][0] * x + m_Q[1][0] * y + m_Q[2][0] * z + m_barycenter[0];
+ tetrahedron.m_pts[i][1] = m_Q[0][1] * x + m_Q[1][1] * y + m_Q[2][1] * z + m_barycenter[1];
+ tetrahedron.m_pts[i][2] = m_Q[0][2] * x + m_Q[1][2] * y + m_Q[2][2] * z + m_barycenter[2];
+ }
+ }
+ ComputeBB();
+}
+}
diff --git a/sdk/extensions/physx/include/NvBlastExtPxAsset.h b/sdk/extensions/physx/include/NvBlastExtPxAsset.h
index 136f0d2..1de4d42 100644
--- a/sdk/extensions/physx/include/NvBlastExtPxAsset.h
+++ b/sdk/extensions/physx/include/NvBlastExtPxAsset.h
@@ -46,6 +46,8 @@ class PxFileBuf;
}
}
+class NvBlastExtDamageAccelerator;
+
namespace Nv
{
@@ -228,6 +230,16 @@ public:
virtual void setUniformHealth(bool enabled) = 0;
/**
+ Set damage accelerator associated with this asset.
+ */
+ virtual void setAccelerator(NvBlastExtDamageAccelerator* accelerator) = 0;
+
+ /**
+ Set damage accelerator associated with this asset.
+ */
+ virtual NvBlastExtDamageAccelerator* getAccelerator() const = 0;
+
+ /**
Pointer field available to the user.
*/
void* userData;
diff --git a/sdk/extensions/physx/include/NvBlastExtPxFamily.h b/sdk/extensions/physx/include/NvBlastExtPxFamily.h
index ae48769..83b50b3 100644
--- a/sdk/extensions/physx/include/NvBlastExtPxFamily.h
+++ b/sdk/extensions/physx/include/NvBlastExtPxFamily.h
@@ -41,6 +41,8 @@ class PxScene;
class PxTransform;
}
+struct NvBlastExtMaterial;
+
namespace Nv
{
@@ -163,7 +165,7 @@ public:
\return a pointer to the (const) ExtPxAsset object.
*/
- virtual const ExtPxAsset& getPxAsset() const = 0;
+ virtual ExtPxAsset& getPxAsset() const = 0;
/**
Set the default material to be used for PxRigidDynamic creation.
@@ -203,6 +205,20 @@ public:
virtual const ExtPxActorDescTemplate* getPxActorDesc() const = 0;
/**
+ The default material associated with this actor family.
+
+ \return a pointer to the default material.
+ */
+ virtual const NvBlastExtMaterial* getMaterial() const = 0;
+
+ /**
+ Set the default material associated with this actor family.
+
+ \param[in] material The material to be the new default.
+ */
+ virtual void setMaterial(const NvBlastExtMaterial* material) = 0;
+
+ /**
Add a user implementation of ExtPxListener to this family's list of listeners.
\param[in] listener The event listener to add.
diff --git a/sdk/extensions/physx/include/NvBlastExtPxManager.h b/sdk/extensions/physx/include/NvBlastExtPxManager.h
index d4dd50c..53d4fa4 100644
--- a/sdk/extensions/physx/include/NvBlastExtPxManager.h
+++ b/sdk/extensions/physx/include/NvBlastExtPxManager.h
@@ -72,7 +72,7 @@ Used to create Physics Family.
*/
struct ExtPxFamilyDesc
{
- const ExtPxAsset* pxAsset; //!< px asset to create from, pointer will be stored in family.
+ ExtPxAsset* pxAsset; //!< px asset to create from, pointer will be stored in family.
const NvBlastActorDesc* actorDesc; //!< actor descriptor to be used when creating TkActor. If nullptr, default NvBlastActorDesc from ExtPxAsset will be used.
TkGroup* group; //!< if not nullptr, created TkActor will be placed in group
};
diff --git a/sdk/extensions/physx/source/physics/NvBlastExtImpactDamageManager.cpp b/sdk/extensions/physx/source/physics/NvBlastExtImpactDamageManager.cpp
index b7831cb..ec0cf20 100644
--- a/sdk/extensions/physx/source/physics/NvBlastExtImpactDamageManager.cpp
+++ b/sdk/extensions/physx/source/physics/NvBlastExtImpactDamageManager.cpp
@@ -30,6 +30,7 @@
#include "NvBlastExtPxManager.h"
#include "NvBlastExtPxFamily.h"
#include "NvBlastExtPxActor.h"
+#include "NvBlastExtPxAsset.h"
#include "NvBlastExtPxListener.h"
#include "NvBlastAssert.h"
@@ -365,13 +366,13 @@ void ExtImpactDamageManagerImpl::damageActor(ExtPxActor* actor, PxShape* /*shape
const float damage = m_settings.hardness > 0.f ? force.magnitude() / m_settings.hardness : 0.f;
- const void* material = actor->getTkActor().getFamily().getMaterial();
+ const NvBlastExtMaterial* material = actor->getFamily().getMaterial();
if (!material)
{
return;
}
- float normalizedDamage = reinterpret_cast<const NvBlastExtMaterial*>(material)->getNormalizedDamage(damage);
+ float normalizedDamage = material->getNormalizedDamage(damage);
if (normalizedDamage == 0.f || normalizedDamage < m_settings.damageThresholdMin)
{
return;
@@ -382,24 +383,22 @@ void ExtImpactDamageManagerImpl::damageActor(ExtPxActor* actor, PxShape* /*shape
const float minDistance = m_settings.damageRadiusMax * normalizedDamage;
const float maxDistance = minDistance * PxClamp<float>(m_settings.damageFalloffRadiusFactor, 1, 32);
- NvBlastProgramParams programParams;
- programParams.damageDescCount = 1;
- programParams.material = nullptr;
+ NvBlastExtProgramParams programParams(nullptr);
+ programParams.material = material;
+ programParams.accelerator = actor->getFamily().getPxAsset().getAccelerator();
NvBlastDamageProgram program;
if (m_settings.shearDamage)
{
- NvBlastExtShearDamageDesc desc[] = {
- {
- normalizedDamage,
- { normal[0], normal[1], normal[2] },
- { position[0], position[1], position[2] },
- minDistance,
- maxDistance
- }
+ NvBlastExtShearDamageDesc desc = {
+ normalizedDamage,
+ { normal[0], normal[1], normal[2] },
+ { position[0], position[1], position[2] },
+ minDistance,
+ maxDistance
};
- programParams.damageDescBuffer = &desc;
+ programParams.damageDesc = &desc;
program.graphShaderFunction = NvBlastExtShearGraphShader;
program.subgraphShaderFunction = NvBlastExtShearSubgraphShader;
@@ -410,19 +409,17 @@ void ExtImpactDamageManagerImpl::damageActor(ExtPxActor* actor, PxShape* /*shape
}
else
{
- NvBlastExtRadialDamageDesc desc[] = {
- {
- normalizedDamage,
- { position[0], position[1], position[2] },
- minDistance,
- maxDistance
- }
+ NvBlastExtImpactSpreadDamageDesc desc = {
+ normalizedDamage,
+ { position[0], position[1], position[2] },
+ minDistance,
+ maxDistance
};
- programParams.damageDescBuffer = &desc;
+ programParams.damageDesc = &desc;
- program.graphShaderFunction = NvBlastExtFalloffGraphShader;
- program.subgraphShaderFunction = NvBlastExtFalloffSubgraphShader;
+ program.graphShaderFunction = NvBlastExtImpactSpreadGraphShader;
+ program.subgraphShaderFunction = NvBlastExtImpactSpreadSubgraphShader;
NvBlastFractureBuffers fractureEvents = m_fractureBuffers;
actor->getTkActor().generateFracture(&fractureEvents, program, &programParams);
diff --git a/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.cpp b/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.cpp
index 948cd84..45e60d5 100644
--- a/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.cpp
+++ b/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.cpp
@@ -52,8 +52,9 @@ namespace Blast
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ExtPxAssetImpl::ExtPxAssetImpl(const ExtPxAssetDesc& desc, TkFramework& framework)
+ : m_accelerator(nullptr)
#if !defined(NV_VC) || NV_VC >= 14
- : m_defaultActorDesc { 1.0f, nullptr, 1.0f, nullptr }
+ , m_defaultActorDesc { 1.0f, nullptr, 1.0f, nullptr }
{
#else
{
@@ -65,8 +66,9 @@ ExtPxAssetImpl::ExtPxAssetImpl(const ExtPxAssetDesc& desc, TkFramework& framewor
}
ExtPxAssetImpl::ExtPxAssetImpl(const TkAssetDesc& desc, ExtPxChunk* pxChunks, ExtPxSubchunk* pxSubchunks, TkFramework& framework)
+ : m_accelerator(nullptr)
#if !defined(NV_VC) || NV_VC >= 14
- : m_defaultActorDesc{ 1.0f, nullptr, 1.0f, nullptr }
+ , m_defaultActorDesc{ 1.0f, nullptr, 1.0f, nullptr }
{
#else
{
@@ -79,6 +81,7 @@ ExtPxAssetImpl::ExtPxAssetImpl(const TkAssetDesc& desc, ExtPxChunk* pxChunks, Ex
ExtPxAssetImpl::ExtPxAssetImpl(TkAsset* asset, ExtPxAssetDesc::ChunkDesc* chunks, uint32_t chunkCount)
: m_tkAsset(asset)
+ , m_accelerator(nullptr)
#if !defined(NV_VC) || NV_VC >= 14
, m_defaultActorDesc{ 1.0f, nullptr, 1.0f, nullptr }
{
@@ -94,6 +97,7 @@ ExtPxAssetImpl::ExtPxAssetImpl(TkAsset* asset, ExtPxAssetDesc::ChunkDesc* chunks
ExtPxAssetImpl::ExtPxAssetImpl(TkAsset* asset)
: m_tkAsset(asset)
+ , m_accelerator(nullptr)
#if !defined(NV_VC) || NV_VC >= 14
, m_defaultActorDesc { 1.0f, nullptr, 1.0f, nullptr }
{
diff --git a/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.h b/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.h
index 11a36b3..7d43c53 100644
--- a/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.h
+++ b/sdk/extensions/physx/source/physics/NvBlastExtPxAssetImpl.h
@@ -105,6 +105,16 @@ public:
virtual void setUniformHealth(bool enabled) override;
+ virtual void setAccelerator(NvBlastExtDamageAccelerator* accelerator) override
+ {
+ m_accelerator = accelerator;
+ }
+
+ virtual NvBlastExtDamageAccelerator* getAccelerator() const override
+ {
+ return m_accelerator;
+ }
+
//////// internal public methods ////////
@@ -137,12 +147,13 @@ private:
//////// data ////////
- TkAsset* m_tkAsset;
- Array<ExtPxChunk>::type m_chunks;
- Array<ExtPxSubchunk>::type m_subchunks;
- Array<float>::type m_bondHealths;
- Array<float>::type m_supportChunkHealths;
- NvBlastActorDesc m_defaultActorDesc;
+ TkAsset* m_tkAsset;
+ Array<ExtPxChunk>::type m_chunks;
+ Array<ExtPxSubchunk>::type m_subchunks;
+ Array<float>::type m_bondHealths;
+ Array<float>::type m_supportChunkHealths;
+ NvBlastExtDamageAccelerator* m_accelerator;
+ NvBlastActorDesc m_defaultActorDesc;
};
} // namespace Blast
diff --git a/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.cpp b/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.cpp
index 09e26d4..1682d5e 100644
--- a/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.cpp
+++ b/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.cpp
@@ -50,8 +50,14 @@ namespace Blast
{
-ExtPxFamilyImpl::ExtPxFamilyImpl(ExtPxManagerImpl& manager, TkFamily& tkFamily, const ExtPxAsset& pxAsset)
- : m_manager(manager), m_tkFamily(tkFamily), m_pxAsset(pxAsset), m_pxShapeDescTemplate(nullptr), m_pxActorDescTemplate(nullptr), m_isSpawned(false)
+ExtPxFamilyImpl::ExtPxFamilyImpl(ExtPxManagerImpl& manager, TkFamily& tkFamily, ExtPxAsset& pxAsset)
+ : m_manager(manager)
+ , m_tkFamily(tkFamily)
+ , m_pxAsset(pxAsset)
+ , m_pxShapeDescTemplate(nullptr)
+ , m_pxActorDescTemplate(nullptr)
+ , m_material(nullptr)
+ , m_isSpawned(false)
{
m_subchunkShapes.resize(static_cast<uint32_t>(m_pxAsset.getSubchunkCount()));
diff --git a/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.h b/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.h
index 356e2c7..bab8a60 100644
--- a/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.h
+++ b/sdk/extensions/physx/source/physics/NvBlastExtPxFamilyImpl.h
@@ -60,7 +60,7 @@ public:
//////// ctor ////////
- ExtPxFamilyImpl(ExtPxManagerImpl& manager, TkFamily& tkFamily, const ExtPxAsset& pxAsset);
+ ExtPxFamilyImpl(ExtPxManagerImpl& manager, TkFamily& tkFamily, ExtPxAsset& pxAsset);
~ExtPxFamilyImpl();
virtual void release() override;
@@ -98,7 +98,7 @@ public:
return m_subchunkShapes.begin();
}
- virtual const ExtPxAsset& getPxAsset() const override
+ virtual ExtPxAsset& getPxAsset() const override
{
return m_pxAsset;
}
@@ -128,6 +128,16 @@ public:
return m_pxActorDescTemplate;
}
+ virtual const NvBlastExtMaterial* getMaterial() const override
+ {
+ return m_material;
+ }
+
+ virtual void setMaterial(const NvBlastExtMaterial* material) override
+ {
+ m_material = material;
+ }
+
virtual void subscribe(ExtPxListener& listener) override
{
m_listeners.pushBack(&listener);
@@ -161,10 +171,11 @@ private:
ExtPxManagerImpl& m_manager;
TkFamily& m_tkFamily;
- const ExtPxAsset& m_pxAsset;
+ ExtPxAsset& m_pxAsset;
ExtPxSpawnSettings m_spawnSettings;
const ExtPxShapeDescTemplate* m_pxShapeDescTemplate;
const ExtPxActorDescTemplate* m_pxActorDescTemplate;
+ const NvBlastExtMaterial* m_material;
bool m_isSpawned;
PxTransform m_initialTransform;
PxVec3 m_initialScale;
diff --git a/sdk/extensions/shaders/include/NvBlastExtDamageShaders.h b/sdk/extensions/shaders/include/NvBlastExtDamageShaders.h
index 8daf9cf..b7962cd 100644
--- a/sdk/extensions/shaders/include/NvBlastExtDamageShaders.h
+++ b/sdk/extensions/shaders/include/NvBlastExtDamageShaders.h
@@ -30,6 +30,7 @@
#define NVBLASTEXTDAMAGESHADERS_H
#include "NvBlastTypes.h"
+#include "NvBlastDebugRender.h"
/**
@@ -38,6 +39,49 @@ A few example damage shader implementations.
///////////////////////////////////////////////////////////////////////////////
+// Damage Accelerator
+///////////////////////////////////////////////////////////////////////////////
+
+class NvBlastExtDamageAccelerator
+{
+public:
+ virtual void release() = 0;
+
+ virtual Nv::Blast::DebugBuffer fillDebugRender(int depth = -1, bool segments = false) = 0;
+};
+
+NVBLAST_API NvBlastExtDamageAccelerator* NvBlastExtDamageAcceleratorCreate(const NvBlastAsset* asset, int type);
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Damage Program
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+Damage program params.
+
+Custom user params to be passed in shader functions. This structure hints recommended parameters layout, but it
+doesn't required to be this way.
+
+The idea of this 'hint' is that damage parameters are basically 2 entities: material + damage description.
+1. Material is something that describes an actor properties (e.g. mass, stiffness, fragility) which are not expected to be changed often.
+2. Damage description is something that describes particular damage event (e.g. position, radius and force of explosion).
+
+Also this damage program hints that there could be more than one damage event happening and processed per one shader call (for efficiency reasons).
+So different damage descriptions can be stacked and passed in one shader call (while material is kept the same obviously).
+*/
+struct NvBlastExtProgramParams
+{
+ NvBlastExtProgramParams(const void* desc, const void* material_ = nullptr, NvBlastExtDamageAccelerator* accelerator_ = nullptr)
+ : damageDesc(desc), material(material_), accelerator(accelerator_) {}
+
+ const void* damageDesc; //!< array of damage descriptions
+ const void* material; //!< pointer to material
+ NvBlastExtDamageAccelerator* accelerator;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
// Common Material
///////////////////////////////////////////////////////////////////////////////
@@ -95,38 +139,20 @@ NOTE: The signature of shader functions are equal to NvBlastGraphShaderFunction
They are not expected to be called directly.
@see NvBlastGraphShaderFunction, NvBlastSubgraphShaderFunction
*/
-NVBLAST_API void NvBlastExtFalloffGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const NvBlastProgramParams* params);
-NVBLAST_API void NvBlastExtFalloffSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const NvBlastProgramParams* params);
-NVBLAST_API void NvBlastExtCutterGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const NvBlastProgramParams* params);
-NVBLAST_API void NvBlastExtCutterSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const NvBlastProgramParams* params);
-
-
-/**
-Helper Radial Falloff Damage function.
-
-Basically it calls NvBlastActorGenerateFracture and then NvBlastActorApplyFracture with Radial Falloff shader.
-
-\param[in,out] actor The NvBlastActor to apply fracture to.
-\param[in,out] buffers Target buffers to hold applied command events.
-\param[in] damageDescBuffer Damage descriptors array.
-\param[in] damageDescCount Size of damage descriptors array.
-\param[in] material Material to use.
-\param[in] logFn User-supplied message function (see NvBlastLog definition). May be NULL.
-\param[in,out] timers If non-NULL this struct will be filled out with profiling information for the step, in profile build configurations.
-
-\return true iff any fracture was applied.
-*/
-NVBLAST_API bool NvBlastExtDamageActorRadialFalloff(NvBlastActor* actor, NvBlastFractureBuffers* buffers, const NvBlastExtRadialDamageDesc* damageDescBuffer, uint32_t damageDescCount, const NvBlastExtMaterial* material, NvBlastLog logFn, NvBlastTimers* timers);
+NVBLAST_API void NvBlastExtFalloffGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* params);
+NVBLAST_API void NvBlastExtFalloffSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* params);
+NVBLAST_API void NvBlastExtCutterGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* params);
+NVBLAST_API void NvBlastExtCutterSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* params);
///////////////////////////////////////////////////////////////////////////////
-// Segment Radial Damage
+// Capsule Radial Damage
///////////////////////////////////////////////////////////////////////////////
/**
-Segment Radial Damage Desc
+Capsule Radial Damage Desc
*/
-struct NvBlastExtSegmentRadialDamageDesc
+struct NvBlastExtCapsuleRadialDamageDesc
{
float damage; //!< normalized damage amount, range: [0, 1] (maximum health value to be reduced)
float position0[3]; //!< damage segment point A position
@@ -136,17 +162,17 @@ struct NvBlastExtSegmentRadialDamageDesc
};
/**
-Segment Radial Falloff damage for both graph and subgraph shaders.
+Capsule Radial Falloff damage for both graph and subgraph shaders.
-For every bond/chunk damage is calculated from the distance to line segment AB described in NvBlastExtSegmentRadialDamageDesc.
+For every bond/chunk damage is calculated from the distance to line segment AB described in NvBlastExtCapsuleRadialDamageDesc.
If distance is smaller then minRadius, full compressive amount of damage is applied. From minRadius to maxRaidus it linearly falls off to zero.
NOTE: The signature of shader functions are equal to NvBlastGraphShaderFunction and NvBlastSubgraphShaderFunction respectively.
They are not expected to be called directly.
@see NvBlastGraphShaderFunction, NvBlastSubgraphShaderFunction
*/
-NVBLAST_API void NvBlastExtSegmentFalloffGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const NvBlastProgramParams* params);
-NVBLAST_API void NvBlastExtSegmentFalloffSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const NvBlastProgramParams* params);
+NVBLAST_API void NvBlastExtCapsuleFalloffGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* params);
+NVBLAST_API void NvBlastExtCapsuleFalloffSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* params);
///////////////////////////////////////////////////////////////////////////////
@@ -174,8 +200,78 @@ NOTE: The signature of shader functions are equal to NvBlastGraphShaderFunction
They are not expected to be called directly.
@see NvBlastGraphShaderFunction, NvBlastSubgraphShaderFunction
*/
-NVBLAST_API void NvBlastExtShearGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const NvBlastProgramParams* params);
-NVBLAST_API void NvBlastExtShearSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const NvBlastProgramParams* params);
+NVBLAST_API void NvBlastExtShearGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* params);
+NVBLAST_API void NvBlastExtShearSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* params);
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Triangle Intersection Damage
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+Triangle Intersection Damage Desc
+*/
+struct NvBlastExtTriangleIntersectionDamageDesc
+{
+ float damage; //!< normalized damage amount, range: [0, 1] (maximum health value to be reduced)
+ NvcVec3 position0; //!< triangle point A position
+ NvcVec3 position1; //!< triangle point B position
+ NvcVec3 position2; //!< triangle point C position
+};
+
+/**
+Triangle Intersection damage for both graph and subgraph shaders.
+
+Every bond is considered to be a segment connecting two chunk centroids. For every bond (segment) intersection with passed triangle is checked. If intersects
+full damage is applied on bond.
+For subgraph shader segments are formed as connections between it's subchunks centroids. Intersection is check in the same fashion.
+
+The idea is that if you want to cut an object say with the laser sword, you can form a triangle by taking the position of a sword on this timeframe and on previous one.
+So that nothing will be missed in terms of space and time. By sweeping sword through whole object it will be cut in halves inevitably, since all bonds segments form connected graph.
+
+NOTE: The signature of shader functions are equal to NvBlastGraphShaderFunction and NvBlastSubgraphShaderFunction respectively.
+They are not expected to be called directly.
+@see NvBlastGraphShaderFunction, NvBlastSubgraphShaderFunction
+*/
+NVBLAST_API void NvBlastExtTriangleIntersectionGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* params);
+NVBLAST_API void NvBlastExtTriangleIntersectionSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* params);
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Impact Spread
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+Impact Spread Damage Desc
+*/
+struct NvBlastExtImpactSpreadDamageDesc
+{
+ float damage; //!< normalized damage amount, range: [0, 1] (maximum health value to be reduced)
+ float position[3]; //!< origin of damage action
+
+ float minRadius; //!< inner radius of damage action
+ float maxRadius; //!< outer radius of damage action
+};
+
+/**
+Impact Spread Damage Shaders.
+
+It assumes that position is somewhere on the chunk and looks for nearest chunk to this position and damages it.
+Then it does breadth-first support graph traversal. For radial falloff metric distance is measured along the edges of the graph.
+That allows to avoid damaging parts which are near in space but disjointed topologically. For example if you hit one column of an arc
+it would take much bigger radius for damage to travel to the other column than in the simple radial damage.
+
+Shader is designed to be used with impact damage, where it is know in advance that actual hit happened.
+
+This shader requires NvBlastExtDamageAccelerator passed in, it request scratch memory from it, therefore it is also designed to work
+only in single threaded mode. It can easily be changed by passing scratch memory as a part of NvBlastExtProgramParams if required.
+
+NOTE: The signature of shader functions are equal to NvBlastGraphShaderFunction and NvBlastSubgraphShaderFunction respectively.
+They are not expected to be called directly.
+@see NvBlastGraphShaderFunction, NvBlastSubgraphShaderFunction
+*/
+NVBLAST_API void NvBlastExtImpactSpreadGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* params);
+NVBLAST_API void NvBlastExtImpactSpreadSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* params);
#endif // NVBLASTEXTDAMAGESHADERS_H
diff --git a/sdk/extensions/shaders/source/NvBlastExtDamageAcceleratorAABBTree.cpp b/sdk/extensions/shaders/source/NvBlastExtDamageAcceleratorAABBTree.cpp
new file mode 100644
index 0000000..bf2eb59
--- /dev/null
+++ b/sdk/extensions/shaders/source/NvBlastExtDamageAcceleratorAABBTree.cpp
@@ -0,0 +1,380 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2016-2017 NVIDIA Corporation. All rights reserved.
+
+#include "NvBlastExtDamageAcceleratorAABBTree.h"
+#include "NvBlastIndexFns.h"
+#include "NvBlastAssert.h"
+#include "PxVec4.h"
+#include <algorithm>
+
+using namespace physx;
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Creation
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ExtDamageAcceleratorAABBTree* ExtDamageAcceleratorAABBTree::create(const NvBlastAsset* asset)
+{
+ ExtDamageAcceleratorAABBTree* tree = NVBLAST_NEW(Nv::Blast::ExtDamageAcceleratorAABBTree) ();
+ tree->build(asset);
+ return tree;
+}
+
+
+void ExtDamageAcceleratorAABBTree::release()
+{
+ NVBLAST_DELETE(this, ExtDamageAcceleratorAABBTree);
+}
+
+
+void ExtDamageAcceleratorAABBTree::build(const NvBlastAsset* asset)
+{
+ NVBLAST_ASSERT(m_root == nullptr);
+
+ const NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(asset, logLL);
+ const NvBlastBond* bonds = NvBlastAssetGetBonds(asset, logLL);
+ const NvBlastChunk* chunks = NvBlastAssetGetChunks(asset, logLL);
+ const uint32_t N = NvBlastAssetGetBondCount(asset, logLL);
+
+ m_indices.resizeUninitialized(N);
+ m_points.resizeUninitialized(N);
+ m_segments.resizeUninitialized(N);
+ m_bonds.resizeUninitialized(N);
+ m_nodes.reserve(2 * N);
+
+ for (uint32_t node0 = 0; node0 < graph.nodeCount; ++node0)
+ {
+ for (uint32_t j = graph.adjacencyPartition[node0]; j < graph.adjacencyPartition[node0 + 1]; ++j)
+ {
+ uint32_t bondIndex = graph.adjacentBondIndices[j];
+ uint32_t node1 = graph.adjacentNodeIndices[j];
+ if (node0 < node1)
+ {
+ const NvBlastBond& bond = bonds[bondIndex];
+ const PxVec3& p = (reinterpret_cast<const PxVec3&>(bond.centroid));
+ m_points[bondIndex] = p;
+ m_indices[bondIndex] = bondIndex;
+ m_bonds[bondIndex].node0 = node0;
+ m_bonds[bondIndex].node1 = node1;
+
+ // filling bond segments as a connection of 2 chunk centroids
+ const uint32_t chunk0 = graph.chunkIndices[node0];
+ const uint32_t chunk1 = graph.chunkIndices[node1];
+ if (isInvalidIndex(chunk1))
+ {
+ // for world node we don't have it's centroid, so approximate with projection on bond normal
+ m_segments[bondIndex].p0 = (reinterpret_cast<const PxVec3&>(chunks[chunk0].centroid));
+ const PxVec3 normal = (reinterpret_cast<const PxVec3&>(bond.normal));
+ m_segments[bondIndex].p1 = m_segments[bondIndex].p0 + normal * (p - m_segments[bondIndex].p0).dot(normal) * 2;
+
+ }
+ else
+ {
+ m_segments[bondIndex].p0 = (reinterpret_cast<const PxVec3&>(chunks[chunk0].centroid));
+ m_segments[bondIndex].p1 = (reinterpret_cast<const PxVec3&>(chunks[chunk1].centroid));
+ }
+ }
+ }
+
+ }
+
+ int rootIndex = N > 0 ? createNode(0, N - 1, 0) : -1;
+ m_root = rootIndex >= 0 ? &m_nodes[rootIndex] : nullptr;
+}
+
+int ExtDamageAcceleratorAABBTree::createNode(uint32_t startIdx, uint32_t endIdx, uint32_t depth)
+{
+ if (startIdx > endIdx)
+ return -1;
+
+ Node node;
+ node.first = startIdx;
+ node.last = endIdx;
+
+ // calc node bounds
+ node.pointsBound = PxBounds3::empty();
+ node.segmentsBound = PxBounds3::empty();
+ for (uint32_t i = node.first; i <= node.last; i++)
+ {
+ const uint32_t idx = m_indices[i];
+ node.pointsBound.include(m_points[idx]);
+ node.segmentsBound.include(m_segments[idx].p0);
+ node.segmentsBound.include(m_segments[idx].p1);
+ }
+
+ // select axis of biggest extent
+ const PxVec3 ext = node.pointsBound.getExtents();
+ uint32_t axis = 0;
+ for (uint32_t k = 1; k < 3; k++)
+ {
+ if (ext[k] > ext[axis])
+ {
+ axis = k;
+ }
+ }
+
+ // split on selected axis and partially sort around the middle
+ const uint32_t mid = startIdx + (endIdx - startIdx) / 2;
+ std::nth_element(m_indices.begin() + startIdx, m_indices.begin() + mid, m_indices.begin() + endIdx + 1, [&](uint32_t lhs, uint32_t rhs)
+ {
+ return m_points[lhs][axis] < m_points[rhs][axis];
+ });
+
+ const uint32_t BUCKET = 32;
+ if (endIdx - startIdx > BUCKET && mid > startIdx && mid < endIdx)
+ {
+ node.child[0] = createNode(startIdx, mid, depth + 1);
+ node.child[1] = createNode(mid + 1, endIdx, depth + 1);
+ }
+ else
+ {
+ node.child[0] = -1;
+ node.child[1] = -1;
+ }
+
+ m_nodes.pushBack(node);
+
+ return m_nodes.size() - 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Queries
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void ExtDamageAcceleratorAABBTree::findInBounds(const physx::PxBounds3& bounds, ResultCallback& callback, bool segments) const
+{
+ if (m_root)
+ {
+ if (segments)
+ findSegmentsInBounds(*m_root, callback, bounds);
+ else
+ findPointsInBounds(*m_root, callback, bounds);
+ callback.dispatch();
+ }
+}
+
+void ExtDamageAcceleratorAABBTree::findPointsInBounds(const Node& node, ResultCallback& callback, const physx::PxBounds3& bounds) const
+{
+ if (!bounds.intersects(node.pointsBound))
+ {
+ return;
+ }
+
+ // if search bound contains node bound, simply add all point indexes.
+ if (node.pointsBound.isInside(bounds))
+ {
+ for (uint32_t i = node.first; i <= node.last; i++)
+ pushResult(callback, m_indices[i]);
+ return; // early pruning.
+ }
+
+ if (node.child[0] < 0)
+ {
+ for (uint32_t i = node.first; i <= node.last; i++)
+ {
+ const uint32_t idx = m_indices[i];
+ if (bounds.contains(m_points[idx]))
+ pushResult(callback, idx);
+ }
+
+ return;
+ }
+
+ // check whether child nodes are in range.
+ for (uint32_t c = 0; c < 2; ++c)
+ {
+ findPointsInBounds(m_nodes[node.child[c]], callback, bounds);
+ }
+}
+
+void ExtDamageAcceleratorAABBTree::findSegmentsInBounds(const Node& node, ResultCallback& callback, const physx::PxBounds3& bounds) const
+{
+ if (!bounds.intersects(node.segmentsBound))
+ {
+ return;
+ }
+
+ // if search bound contains node bound, simply add all point indexes.
+ if (node.segmentsBound.isInside(bounds))
+ {
+ for (uint32_t i = node.first; i <= node.last; i++)
+ pushResult(callback, m_indices[i]);
+ return; // early pruning.
+ }
+
+ if (node.child[0] < 0)
+ {
+ for (uint32_t i = node.first; i <= node.last; i++)
+ {
+ const uint32_t idx = m_indices[i];
+ if (bounds.contains(m_segments[idx].p0) || bounds.contains(m_segments[idx].p1))
+ pushResult(callback, idx);
+ }
+
+ return;
+ }
+
+ // check whether child nodes are in range.
+ for (uint32_t c = 0; c < 2; ++c)
+ {
+ findSegmentsInBounds(m_nodes[node.child[c]], callback, bounds);
+ }
+}
+
+bool intersectSegmentPlane(const PxVec3& v1, const PxVec3& v2, const PxPlane& p)
+{
+ const bool s1 = p.distance(v1) > 0.f;
+ const bool s2 = p.distance(v2) > 0.f;
+ return (s1 && !s2) || (s2 && !s1);
+}
+
+bool intersectBoundsPlane(const PxBounds3& b, const PxPlane& p)
+{
+ const PxVec3 extents = b.getExtents();
+ const PxVec3 center = b.getCenter();
+
+ float r = extents.x * PxAbs(p.n.x) + extents.y * PxAbs(p.n.y) + extents.z * PxAbs(p.n.z);
+ float s = p.n.dot(center) + p.d;
+
+ return PxAbs(s) <= r;
+}
+
+void ExtDamageAcceleratorAABBTree::findBondSegmentsPlaneIntersected(const physx::PxPlane& plane, ResultCallback& resultCallback) const
+{
+ if (m_root)
+ {
+ findSegmentsPlaneIntersected(*m_root, resultCallback, plane);
+ resultCallback.dispatch();
+ }
+}
+
+void ExtDamageAcceleratorAABBTree::findSegmentsPlaneIntersected(const Node& node, ResultCallback& callback, const physx::PxPlane& plane) const
+{
+ if (!intersectBoundsPlane(node.segmentsBound, plane))
+ {
+ return;
+ }
+
+ if (node.child[0] < 0)
+ {
+ for (uint32_t i = node.first; i <= node.last; i++)
+ {
+ const uint32_t idx = m_indices[i];
+ if (intersectSegmentPlane(m_segments[idx].p0, m_segments[idx].p1, plane))
+ pushResult(callback, idx);
+ }
+
+ return;
+ }
+
+ // check whether child nodes are in range.
+ for (uint32_t c = 0; c < 2; ++c)
+ {
+ findSegmentsPlaneIntersected(m_nodes[node.child[c]], callback, plane);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Debug Render
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static uint32_t PxVec4ToU32Color(const PxVec4& color)
+{
+ uint32_t c = 0;
+ c |= (int)(color.w * 255); c <<= 8;
+ c |= (int)(color.z * 255); c <<= 8;
+ c |= (int)(color.y * 255); c <<= 8;
+ c |= (int)(color.x * 255);
+ return c;
+}
+
+Nv::Blast::DebugBuffer ExtDamageAcceleratorAABBTree::fillDebugRender(int depth, bool segments)
+{
+ Nv::Blast::DebugBuffer debugBuffer = { nullptr, 0 };
+
+ m_debugLineBuffer.clear();
+
+ if (m_root)
+ {
+ fillDebugBuffer(*m_root, 0, depth, segments);
+ }
+
+ debugBuffer.lines = m_debugLineBuffer.begin();
+ debugBuffer.lineCount = m_debugLineBuffer.size();
+
+ return debugBuffer;
+}
+
+void ExtDamageAcceleratorAABBTree::fillDebugBuffer(const Node& node, int currentDepth, int depth, bool segments)
+{
+ if (depth < 0 || currentDepth == depth)
+ {
+ const PxVec4 LEAF_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
+ const PxVec4 NON_LEAF_COLOR(0.3f, 0.3f, 0.3f, 1.0f);
+
+ // draw box
+ const PxBounds3 bounds = segments ? node.segmentsBound : node.pointsBound;
+ const PxVec3 center = bounds.getCenter();
+ const PxVec3 extents = bounds.getExtents();
+
+ const int vs[] = { 0,3,5,6 };
+ for (int i = 0; i < 4; i++)
+ {
+ int v = vs[i];
+ for (int d = 1; d < 8; d <<= 1)
+ {
+ auto flip = [](int x, int k) { return ((x >> k) & 1) * 2.f - 1.f; };
+ const float s = std::pow(0.99f, currentDepth);
+ PxVec3 p0 = center + s * extents.multiply(PxVec3(flip(v, 0), flip(v, 1), flip(v, 2)));
+ PxVec3 p1 = center + s * extents.multiply(PxVec3(flip(v^d, 0), flip(v^d, 1), flip(v^d, 2)));
+ m_debugLineBuffer.pushBack(Nv::Blast::DebugLine(
+ reinterpret_cast<NvcVec3&>(p0),
+ reinterpret_cast<NvcVec3&>(p1),
+ PxVec4ToU32Color(LEAF_COLOR * (1.f - (currentDepth + 1) * 0.1f)))
+ );
+ }
+ }
+ }
+
+ for (uint32_t i = 0; i < 2; ++i)
+ {
+ if (node.child[i] >= 0)
+ {
+ fillDebugBuffer(m_nodes[node.child[i]], currentDepth + 1, depth, segments);
+ }
+ }
+}
+
+
+} // namespace Blast
+} // namespace Nv
diff --git a/sdk/extensions/shaders/source/NvBlastExtDamageAcceleratorAABBTree.h b/sdk/extensions/shaders/source/NvBlastExtDamageAcceleratorAABBTree.h
new file mode 100644
index 0000000..872885e
--- /dev/null
+++ b/sdk/extensions/shaders/source/NvBlastExtDamageAcceleratorAABBTree.h
@@ -0,0 +1,148 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2016-2017 NVIDIA Corporation. All rights reserved.
+
+#pragma once
+
+#include "NvBlastExtDamageAcceleratorInternal.h"
+#include "NvBlast.h"
+#include "NvBlastArray.h"
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+class ExtDamageAcceleratorAABBTree final : public ExtDamageAcceleratorInternal
+{
+public:
+ //////// ctor ////////
+
+ ExtDamageAcceleratorAABBTree() :
+ m_root(nullptr)
+ {
+ }
+
+ virtual ~ExtDamageAcceleratorAABBTree()
+ {
+ }
+
+ static ExtDamageAcceleratorAABBTree* create(const NvBlastAsset* asset);
+
+
+ //////// interface ////////
+
+ virtual void release() override;
+
+ virtual void findBondCentroidsInBounds(const physx::PxBounds3& bounds, ResultCallback& resultCallback) const override
+ {
+ const_cast<ExtDamageAcceleratorAABBTree*>(this)->findInBounds(bounds, resultCallback, false);
+ }
+
+ virtual void findBondSegmentsInBounds(const physx::PxBounds3& bounds, ResultCallback& resultCallback) const override
+ {
+ const_cast<ExtDamageAcceleratorAABBTree*>(this)->findInBounds(bounds, resultCallback, true);
+
+ }
+
+ virtual void findBondSegmentsPlaneIntersected(const physx::PxPlane& plane, ResultCallback& resultCallback) const override;
+
+ virtual Nv::Blast::DebugBuffer fillDebugRender(int depth, bool segments) override;
+
+ virtual void* getImmediateScratch(size_t size) override
+ {
+ m_scratch.resizeUninitialized(size);
+ return m_scratch.begin();
+ }
+
+
+private:
+ // no copy/assignment
+ ExtDamageAcceleratorAABBTree(ExtDamageAcceleratorAABBTree&);
+ ExtDamageAcceleratorAABBTree& operator=(const ExtDamageAcceleratorAABBTree& tree);
+
+ // Tree node
+ struct Node
+ {
+ int child[2];
+ uint32_t first;
+ uint32_t last;
+ physx::PxBounds3 pointsBound;
+ physx::PxBounds3 segmentsBound;
+ };
+
+
+ void build(const NvBlastAsset* asset);
+
+ int createNode(uint32_t startIdx, uint32_t endIdx, uint32_t depth);
+
+ void pushResult(ResultCallback& callback, uint32_t pointIndex) const
+ {
+ callback.push(pointIndex, m_bonds[pointIndex].node0, m_bonds[pointIndex].node1);
+ }
+
+ void findInBounds(const physx::PxBounds3& bounds, ResultCallback& callback, bool segments) const;
+
+ void findPointsInBounds(const Node& node, ResultCallback& callback, const physx::PxBounds3& bounds) const;
+
+ void findSegmentsInBounds(const Node& node, ResultCallback& callback, const physx::PxBounds3& bounds) const;
+
+ void findSegmentsPlaneIntersected(const Node& node, ResultCallback& callback, const physx::PxPlane& plane) const;
+
+ void fillDebugBuffer(const Node& node, int currentDepth, int depth, bool segments);
+
+
+ //////// data ////////
+
+ Node* m_root;
+ Array<Node>::type m_nodes;
+ Array<uint32_t>::type m_indices;
+
+ Array<physx::PxVec3>::type m_points;
+
+ struct Segment
+ {
+ physx::PxVec3 p0;
+ physx::PxVec3 p1;
+ };
+ Array<Segment>::type m_segments;
+
+ struct BondData
+ {
+ uint32_t node0;
+ uint32_t node1;
+ };
+ Array<BondData>::type m_bonds;
+
+ Array<Nv::Blast::DebugLine>::type m_debugLineBuffer;
+
+ Array<char>::type m_scratch;
+};
+
+
+} // namespace Blast
+} // namespace Nv
diff --git a/sdk/extensions/shaders/source/NvBlastExtDamageAcceleratorInternal.h b/sdk/extensions/shaders/source/NvBlastExtDamageAcceleratorInternal.h
new file mode 100644
index 0000000..2769f4e
--- /dev/null
+++ b/sdk/extensions/shaders/source/NvBlastExtDamageAcceleratorInternal.h
@@ -0,0 +1,96 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2016-2017 NVIDIA Corporation. All rights reserved.
+
+#pragma once
+
+#include "NvBlastExtDamageShaders.h"
+#include "PxBounds3.h"
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+
+class ExtDamageAcceleratorInternal : public NvBlastExtDamageAccelerator
+{
+public:
+ struct QueryBondData
+ {
+ uint32_t bond;
+ uint32_t node0;
+ uint32_t node1;
+ };
+
+ class ResultCallback
+ {
+ public:
+ ResultCallback(QueryBondData* buffer, uint32_t count) :
+ m_bondBuffer(buffer), m_bondMaxCount(count), m_bondCount(0) {}
+
+ virtual void processResults(const QueryBondData* bondBuffer, uint32_t count) = 0;
+
+ void push(uint32_t bond, uint32_t node0, uint32_t node1)
+ {
+ m_bondBuffer[m_bondCount].bond = bond;
+ m_bondBuffer[m_bondCount].node0 = node0;
+ m_bondBuffer[m_bondCount].node1 = node1;
+ m_bondCount++;
+ if (m_bondCount == m_bondMaxCount)
+ {
+ dispatch();
+ }
+ }
+
+ void dispatch()
+ {
+ if (m_bondCount)
+ {
+ processResults(m_bondBuffer, m_bondCount);
+ m_bondCount = 0;
+ }
+ }
+
+ private:
+ QueryBondData* m_bondBuffer;
+ uint32_t m_bondMaxCount;
+
+ uint32_t m_bondCount;
+ };
+
+ virtual void findBondCentroidsInBounds(const physx::PxBounds3& bounds, ResultCallback& resultCallback) const = 0;
+ virtual void findBondSegmentsInBounds(const physx::PxBounds3& bounds, ResultCallback& resultCallback) const = 0;
+ virtual void findBondSegmentsPlaneIntersected(const physx::PxPlane& plane, ResultCallback& resultCallback) const = 0;
+
+ // Non-thread safe! Multiple calls return the same memory.
+ virtual void* getImmediateScratch(size_t size) = 0;
+};
+
+
+} // namespace Blast
+} // namespace Nv
diff --git a/sdk/extensions/shaders/source/NvBlastExtDamageAccelerators.cpp b/sdk/extensions/shaders/source/NvBlastExtDamageAccelerators.cpp
new file mode 100644
index 0000000..4213004
--- /dev/null
+++ b/sdk/extensions/shaders/source/NvBlastExtDamageAccelerators.cpp
@@ -0,0 +1,43 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2016-2017 NVIDIA Corporation. All rights reserved.
+
+
+//#include "NvBlastExtDamageAcceleratorOctree.h"
+//#include "NvBlastExtDamageAcceleratorKdtree.h"
+#include "NvBlastExtDamageAcceleratorAABBTree.h"
+
+NvBlastExtDamageAccelerator* NvBlastExtDamageAcceleratorCreate(const NvBlastAsset* asset, int type)
+{
+ switch (type)
+ {
+ case 0:
+ return nullptr;
+ default:
+ return Nv::Blast::ExtDamageAcceleratorAABBTree::create(asset);
+ break;
+ }
+}
diff --git a/sdk/extensions/shaders/source/NvBlastExtDamageShaders.cpp b/sdk/extensions/shaders/source/NvBlastExtDamageShaders.cpp
index 8027447..84d3345 100644
--- a/sdk/extensions/shaders/source/NvBlastExtDamageShaders.cpp
+++ b/sdk/extensions/shaders/source/NvBlastExtDamageShaders.cpp
@@ -27,15 +27,21 @@
#include "NvBlastExtDamageShaders.h"
+#include "NvBlastExtDamageAcceleratorInternal.h"
#include "NvBlastIndexFns.h"
#include "NvBlastMath.h"
#include "NvBlastGeometry.h"
#include "NvBlastAssert.h"
+#include "NvBlastFixedQueue.h"
+#include "NvBlastFixedBitmap.h"
#include "NvBlast.h"
#include <cmath> // for abs() on linux
+#include <new>
+
using namespace Nv::Blast;
using namespace Nv::Blast::VecMath;
+using namespace physx;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Profiles
@@ -64,13 +70,12 @@ float cutterProfile(float min, float max, float x, float f = 1.0f)
// Damage Functions
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-typedef float(*DamageFunction)(const float pos[3], const void* damageDescBuffer, uint32_t damageIndex);
+typedef float(*DamageFunction)(const float pos[3], const void* damageDescBuffer);
template <ProfileFunction profileFn, typename DescT = NvBlastExtRadialDamageDesc>
-float pointDistanceDamage(const float pos[3], const void* damageDescBuffer, uint32_t damageIndex)
+float pointDistanceDamage(const float pos[3], const void* damageDescBuffer)
{
- const DescT* damageData = reinterpret_cast<const DescT*>(damageDescBuffer);
- const DescT& desc = damageData[damageIndex];
+ const DescT& desc = *static_cast<const DescT*>(damageDescBuffer);
float relativePosition[3];
sub(desc.position, pos, relativePosition);
@@ -103,23 +108,47 @@ float distanceToSegment(const float p[3], const float a[3], const float b[3])
}
template <ProfileFunction profileFn>
-float segmentDistanceDamage(const float pos[3], const void* damageDescBuffer, uint32_t damageIndex)
+float capsuleDistanceDamage(const float pos[3], const void* damageDesc)
{
- const NvBlastExtSegmentRadialDamageDesc* damageData = reinterpret_cast<const NvBlastExtSegmentRadialDamageDesc*>(damageDescBuffer);
- const NvBlastExtSegmentRadialDamageDesc& desc = damageData[damageIndex];
+ const NvBlastExtCapsuleRadialDamageDesc& desc = *static_cast<const NvBlastExtCapsuleRadialDamageDesc*>(damageDesc);
const float distance = distanceToSegment(pos, desc.position0, desc.position1);
const float damage = profileFn(desc.minRadius, desc.maxRadius, distance, desc.damage);
return damage;
}
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// AABB Functions
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+typedef PxBounds3(*BoundFunction)(const void* damageDesc);
+
+PxBounds3 sphereBounds(const void* damageDesc)
+{
+ const NvBlastExtRadialDamageDesc& desc = *static_cast<const NvBlastExtRadialDamageDesc*>(damageDesc);
+ const physx::PxVec3& p = (reinterpret_cast<const physx::PxVec3&>(desc.position));
+ return physx::PxBounds3::centerExtents(p, physx::PxVec3(desc.maxRadius, desc.maxRadius, desc.maxRadius));
+}
+
+PxBounds3 capsuleBounds(const void* damageDesc)
+{
+ const NvBlastExtCapsuleRadialDamageDesc& desc = *static_cast<const NvBlastExtCapsuleRadialDamageDesc*>(damageDesc);
+ const physx::PxVec3& p0 = (reinterpret_cast<const physx::PxVec3&>(desc.position0));
+ const physx::PxVec3& p1 = (reinterpret_cast<const physx::PxVec3&>(desc.position1));
+ PxBounds3 b = PxBounds3::empty();
+ b.include(p0);
+ b.include(p1);
+ b.fattenFast(desc.maxRadius);
+ return b;
+}
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Radial Graph Shader Template
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-template <DamageFunction damageFn>
-void RadialProfileGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const NvBlastProgramParams* params)
+template <DamageFunction damageFn, BoundFunction boundsFn>
+void RadialProfileGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* params)
{
const uint32_t* graphNodeIndexLinks = actor->graphNodeIndexLinks;
const uint32_t firstGraphNodeIndex = actor->firstGraphNodeIndex;
@@ -128,47 +157,103 @@ void RadialProfileGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBl
const uint32_t* adjacentBondIndices = actor->adjacentBondIndices;
const NvBlastBond* assetBonds = actor->assetBonds;
const float* familyBondHealths = actor->familyBondHealths;
-
- const uint32_t damageCount = params->damageDescCount;
+ const NvBlastExtProgramParams* programParams = static_cast<const NvBlastExtProgramParams*>(params);
uint32_t outCount = 0;
- uint32_t currentNodeIndex = firstGraphNodeIndex;
- while (!Nv::Blast::isInvalidIndex(currentNodeIndex))
+ auto processBondFn = [&](uint32_t bondIndex, uint32_t node0, uint32_t node1)
{
- for (uint32_t adj = adjacencyPartition[currentNodeIndex]; adj < adjacencyPartition[currentNodeIndex + 1]; adj++)
+ // skip bonds that are already broken or were visited already
+ // TODO: investigate why testing against health > -1.0f seems slower
+ // could reuse the island edge bitmap instead
+ if ((familyBondHealths[bondIndex] > 0.0f))
{
- uint32_t adjacentNodeIndex = adjacentNodeIndices[adj];
- if (currentNodeIndex < adjacentNodeIndex)
+ const NvBlastBond& bond = assetBonds[bondIndex];
+
+ const float totalBondDamage = damageFn(bond.centroid, programParams->damageDesc);
+ if (totalBondDamage > 0.0f)
{
- uint32_t bondIndex = adjacentBondIndices[adj];
+ NvBlastBondFractureData& outCommand = commandBuffers->bondFractures[outCount++];
+ outCommand.nodeIndex0 = node0;
+ outCommand.nodeIndex1 = node1;
+ outCommand.health = totalBondDamage;
+ }
+ }
+ };
- // skip bonds that are already broken or were visited already
- // TODO: investigate why testing against health > -1.0f seems slower
- // could reuse the island edge bitmap instead
- if ((familyBondHealths[bondIndex] > 0.0f))
- {
+ const ExtDamageAcceleratorInternal* damageAccelerator = programParams->accelerator ? static_cast<const ExtDamageAcceleratorInternal*>(programParams->accelerator) : nullptr;
+ const uint32_t ACTOR_MINIMUM_NODE_COUNT_TO_ACCELERATE = actor->assetNodeCount / 3;
+ if (damageAccelerator && actor->graphNodeCount > ACTOR_MINIMUM_NODE_COUNT_TO_ACCELERATE)
+ {
+ physx::PxBounds3 bounds = boundsFn(programParams->damageDesc);
- const NvBlastBond& bond = assetBonds[bondIndex];
+ const uint32_t CALLBACK_BUFFER_SIZE = 1000;
- float totalBondDamage = 0.0f;
+ class AcceleratorCallback : public ExtDamageAcceleratorInternal::ResultCallback
+ {
+ public:
+ AcceleratorCallback(NvBlastFractureBuffers* commandBuffers, uint32_t& outCount, const NvBlastGraphShaderActor* actor, const NvBlastExtProgramParams* programParams) :
+ ExtDamageAcceleratorInternal::ResultCallback(m_buffer, CALLBACK_BUFFER_SIZE),
+ m_actor(actor),
+ m_commandBuffers(commandBuffers),
+ m_outCount(outCount),
+ m_programParams(programParams)
+ {
+ }
- for (uint32_t damageIndex = 0; damageIndex < damageCount; damageIndex++)
+ virtual void processResults(const ExtDamageAcceleratorInternal::QueryBondData* bondBuffer, uint32_t count) override
+ {
+ for (uint32_t i = 0; i < count; i++)
+ {
+ const ExtDamageAcceleratorInternal::QueryBondData& bondData = bondBuffer[i];
+ if (m_actor->nodeActorIndices[bondData.node0] == m_actor->actorIndex)
{
- totalBondDamage += damageFn(bond.centroid, params->damageDescBuffer, damageIndex);
+ if ((m_actor->familyBondHealths[bondData.bond] > 0.0f))
+ {
+ const NvBlastBond& bond = m_actor->assetBonds[bondData.bond];
+
+ const float totalBondDamage = damageFn(bond.centroid, m_programParams->damageDesc);
+ if (totalBondDamage > 0.0f)
+ {
+ NvBlastBondFractureData& outCommand = m_commandBuffers->bondFractures[m_outCount++];
+ outCommand.nodeIndex0 = bondData.node0;
+ outCommand.nodeIndex1 = bondData.node1;
+ outCommand.health = totalBondDamage;
+ }
+ }
}
+ }
+ }
- if (totalBondDamage > 0.0f)
- {
- NvBlastBondFractureData& outCommand = commandBuffers->bondFractures[outCount++];
- outCommand.nodeIndex0 = currentNodeIndex;
- outCommand.nodeIndex1 = adjacentNodeIndex;
- outCommand.health = totalBondDamage;
- }
+ private:
+ const NvBlastGraphShaderActor* m_actor;
+ NvBlastFractureBuffers* m_commandBuffers;
+ uint32_t& m_outCount;
+ const NvBlastExtProgramParams* m_programParams;
+
+ ExtDamageAcceleratorInternal::QueryBondData m_buffer[CALLBACK_BUFFER_SIZE];
+ };
+
+ AcceleratorCallback cb(commandBuffers, outCount, actor, programParams);
+
+ damageAccelerator->findBondCentroidsInBounds(bounds, cb);
+ }
+ else
+ {
+ uint32_t currentNodeIndex = firstGraphNodeIndex;
+ while (!Nv::Blast::isInvalidIndex(currentNodeIndex))
+ {
+ for (uint32_t adj = adjacencyPartition[currentNodeIndex]; adj < adjacencyPartition[currentNodeIndex + 1]; adj++)
+ {
+ uint32_t adjacentNodeIndex = adjacentNodeIndices[adj];
+ if (currentNodeIndex < adjacentNodeIndex)
+ {
+ uint32_t bondIndex = adjacentBondIndices[adj];
+ processBondFn(bondIndex, currentNodeIndex, adjacentNodeIndex);
}
}
+ currentNodeIndex = graphNodeIndexLinks[currentNodeIndex];
}
- currentNodeIndex = graphNodeIndexLinks[currentNodeIndex];
}
commandBuffers->bondFractureCount = outCount;
@@ -181,20 +266,16 @@ void RadialProfileGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBl
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <DamageFunction damageFn>
-void RadialProfileSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const NvBlastProgramParams* params)
+void RadialProfileSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* params)
{
uint32_t chunkFractureCount = 0;
uint32_t chunkFractureCountMax = commandBuffers->chunkFractureCount;
const uint32_t chunkIndex = actor->chunkIndex;
const NvBlastChunk* assetChunks = actor->assetChunks;
const NvBlastChunk& chunk = assetChunks[chunkIndex];
+ const NvBlastExtProgramParams* programParams = static_cast<const NvBlastExtProgramParams*>(params);
- float totalDamage = 0.0f;
- for (uint32_t damageIndex = 0; damageIndex < params->damageDescCount; ++damageIndex)
- {
- totalDamage += damageFn(chunk.centroid, params->damageDescBuffer, damageIndex);
- }
-
+ const float totalDamage = damageFn(chunk.centroid, programParams->damageDesc);
if (totalDamage > 0.0f && chunkFractureCount < chunkFractureCountMax)
{
NvBlastChunkFractureData& frac = commandBuffers->chunkFractures[chunkFractureCount++];
@@ -211,34 +292,34 @@ void RadialProfileSubgraphShader(NvBlastFractureBuffers* commandBuffers, const N
// Radial Shaders Instantiation
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-void NvBlastExtFalloffGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const NvBlastProgramParams* params)
+void NvBlastExtFalloffGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* params)
{
- RadialProfileGraphShader<pointDistanceDamage<falloffProfile>>(commandBuffers, actor, params);
+ RadialProfileGraphShader<pointDistanceDamage<falloffProfile>, sphereBounds>(commandBuffers, actor, params);
}
-void NvBlastExtFalloffSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const NvBlastProgramParams* params)
+void NvBlastExtFalloffSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* params)
{
RadialProfileSubgraphShader<pointDistanceDamage<falloffProfile>>(commandBuffers, actor, params);
}
-void NvBlastExtCutterGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const NvBlastProgramParams* params)
+void NvBlastExtCutterGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* params)
{
- RadialProfileGraphShader<pointDistanceDamage<cutterProfile>>(commandBuffers, actor, params);
+ RadialProfileGraphShader<pointDistanceDamage<cutterProfile>, sphereBounds>(commandBuffers, actor, params);
}
-void NvBlastExtCutterSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const NvBlastProgramParams* params)
+void NvBlastExtCutterSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* params)
{
RadialProfileSubgraphShader<pointDistanceDamage<cutterProfile>>(commandBuffers, actor, params);
}
-void NvBlastExtSegmentFalloffGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const NvBlastProgramParams* params)
+void NvBlastExtCapsuleFalloffGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* params)
{
- RadialProfileGraphShader<segmentDistanceDamage<falloffProfile>>(commandBuffers, actor, params);
+ RadialProfileGraphShader<capsuleDistanceDamage<falloffProfile>, capsuleBounds>(commandBuffers, actor, params);
}
-void NvBlastExtSegmentFalloffSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const NvBlastProgramParams* params)
+void NvBlastExtCapsuleFalloffSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* params)
{
- RadialProfileSubgraphShader<segmentDistanceDamage<falloffProfile>>(commandBuffers, actor, params);
+ RadialProfileSubgraphShader<capsuleDistanceDamage<falloffProfile>>(commandBuffers, actor, params);
}
@@ -246,123 +327,437 @@ void NvBlastExtSegmentFalloffSubgraphShader(NvBlastFractureBuffers* commandBuffe
// Shear Shader
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-void NvBlastExtShearGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const NvBlastProgramParams* params)
+void NvBlastExtShearGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* params)
{
uint32_t chunkFractureCount = 0;
uint32_t chunkFractureCountMax = commandBuffers->chunkFractureCount;
uint32_t bondFractureCount = 0;
uint32_t bondFractureCountMax = commandBuffers->bondFractureCount;
+ const NvBlastExtProgramParams* programParams = static_cast<const NvBlastExtProgramParams*>(params);
+ const NvBlastExtShearDamageDesc& desc = *static_cast<const NvBlastExtShearDamageDesc*>(programParams->damageDesc);
+ const uint32_t* graphNodeIndexLinks = actor->graphNodeIndexLinks;
+ const uint32_t firstGraphNodeIndex = actor->firstGraphNodeIndex;
+ const uint32_t* chunkIndices = actor->chunkIndices;
+ const uint32_t* adjacencyPartition = actor->adjacencyPartition;
+ const uint32_t* adjacentNodeIndices = actor->adjacentNodeIndices;
+ const uint32_t* adjacentBondIndices = actor->adjacentBondIndices;
+ const NvBlastBond* assetBonds = actor->assetBonds;
+ const NvBlastChunk* assetChunks = actor->assetChunks;
+ const float* familyBondHealths = actor->familyBondHealths;
+ const float* supportChunkHealths = actor->supportChunkHealths;
- for (uint32_t i = 0; i < params->damageDescCount; ++i)
+ uint32_t closestNode = findClosestNode(desc.position
+ , firstGraphNodeIndex, graphNodeIndexLinks
+ , adjacencyPartition, adjacentNodeIndices, adjacentBondIndices
+ , assetBonds, familyBondHealths
+ , assetChunks, supportChunkHealths, chunkIndices);
+
+ uint32_t nodeIndex = closestNode;
+ float maxDist = 0.0f;
+ uint32_t nextNode = invalidIndex<uint32_t>();
+
+ if (chunkFractureCount < chunkFractureCountMax)
{
- const NvBlastExtShearDamageDesc& desc = reinterpret_cast<const NvBlastExtShearDamageDesc*>(params->damageDescBuffer)[i];
- const uint32_t* graphNodeIndexLinks = actor->graphNodeIndexLinks;
- const uint32_t firstGraphNodeIndex = actor->firstGraphNodeIndex;
- const uint32_t* chunkIndices = actor->chunkIndices;
- const uint32_t* adjacencyPartition = actor->adjacencyPartition;
- const uint32_t* adjacentNodeIndices = actor->adjacentNodeIndices;
- const uint32_t* adjacentBondIndices = actor->adjacentBondIndices;
- const NvBlastBond* assetBonds = actor->assetBonds;
- const NvBlastChunk* assetChunks = actor->assetChunks;
- const float* familyBondHealths = actor->familyBondHealths;
- const float* supportChunkHealths = actor->supportChunkHealths;
-
- uint32_t closestNode = findClosestNode(desc.position
- , firstGraphNodeIndex, graphNodeIndexLinks
- , adjacencyPartition, adjacentNodeIndices, adjacentBondIndices
- , assetBonds, familyBondHealths
- , assetChunks, supportChunkHealths, chunkIndices);
-
- uint32_t nodeIndex = closestNode;
- float maxDist = 0.0f;
- uint32_t nextNode = invalidIndex<uint32_t>();
-
- if (chunkFractureCount < chunkFractureCountMax)
+ const uint32_t chunkIndex = chunkIndices[nodeIndex];
+ const NvBlastChunk& chunk = assetChunks[chunkIndex];
+ NvBlastChunkFractureData& frac = commandBuffers->chunkFractures[chunkFractureCount++];
+ frac.chunkIndex = chunkIndex;
+ frac.health = pointDistanceDamage<falloffProfile, NvBlastExtShearDamageDesc>(chunk.centroid, programParams->damageDesc);
+ }
+
+ do {
+ const uint32_t startIndex = adjacencyPartition[nodeIndex];
+ const uint32_t stopIndex = adjacencyPartition[nodeIndex + 1];
+
+
+ for (uint32_t adjacentNodeIndex = startIndex; adjacentNodeIndex < stopIndex; adjacentNodeIndex++)
{
- const uint32_t chunkIndex = chunkIndices[nodeIndex];
- const NvBlastChunk& chunk = assetChunks[chunkIndex];
- NvBlastChunkFractureData& frac = commandBuffers->chunkFractures[chunkFractureCount++];
- frac.chunkIndex = chunkIndex;
- frac.health = pointDistanceDamage<falloffProfile, NvBlastExtShearDamageDesc>(chunk.centroid, params->damageDescBuffer, i);
- }
+ const uint32_t neighbourIndex = adjacentNodeIndices[adjacentNodeIndex];
+ const uint32_t bondIndex = adjacentBondIndices[adjacentNodeIndex];
+ const NvBlastBond& bond = assetBonds[bondIndex];
- do {
- const uint32_t startIndex = adjacencyPartition[nodeIndex];
- const uint32_t stopIndex = adjacencyPartition[nodeIndex + 1];
+ if (!(familyBondHealths[bondIndex] > 0.0f))
+ continue;
+ float shear = 1 * std::abs(1 - std::abs(VecMath::dot(desc.normal, bond.normal)));
- for (uint32_t adjacentNodeIndex = startIndex; adjacentNodeIndex < stopIndex; adjacentNodeIndex++)
+ float d[3]; VecMath::sub(bond.centroid, desc.position, d);
+ float ahead = VecMath::dot(d, desc.normal);
+ if (ahead > maxDist)
{
- const uint32_t neighbourIndex = adjacentNodeIndices[adjacentNodeIndex];
- const uint32_t bondIndex = adjacentBondIndices[adjacentNodeIndex];
- const NvBlastBond& bond = assetBonds[bondIndex];
+ maxDist = ahead;
+ nextNode = neighbourIndex;
+ }
- if (!(familyBondHealths[bondIndex] > 0.0f))
- continue;
+ const float damage = pointDistanceDamage<falloffProfile, NvBlastExtShearDamageDesc>(bond.centroid, programParams->damageDesc);
+ if (damage > 0.0f && bondFractureCount < bondFractureCountMax)
+ {
+ NvBlastBondFractureData& frac = commandBuffers->bondFractures[bondFractureCount++];
+ frac.userdata = bond.userData;
+ frac.nodeIndex0 = nodeIndex;
+ frac.nodeIndex1 = neighbourIndex;
+ frac.health = shear * damage;
+ }
+ }
- float shear = 1 * std::abs(1 - std::abs(VecMath::dot(desc.normal, bond.normal)));
+ if (nodeIndex == nextNode)
+ break;
+
+ nodeIndex = nextNode;
+ } while (!isInvalidIndex(nextNode));
+
+ commandBuffers->bondFractureCount = bondFractureCount;
+ commandBuffers->chunkFractureCount = chunkFractureCount;
+}
+
+void NvBlastExtShearSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* params)
+{
+ RadialProfileSubgraphShader<pointDistanceDamage<falloffProfile, NvBlastExtShearDamageDesc>>(commandBuffers, actor, params);
+}
- float d[3]; VecMath::sub(bond.centroid, desc.position, d);
- float ahead = VecMath::dot(d, desc.normal);
- if (ahead > maxDist)
- {
- maxDist = ahead;
- nextNode = neighbourIndex;
- }
- const float damage = pointDistanceDamage<falloffProfile, NvBlastExtShearDamageDesc>(bond.centroid, params->damageDescBuffer, i);
- if (damage > 0.0f && bondFractureCount < bondFractureCountMax)
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Triangle Intersection Damage
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#define SMALL_NUMBER (1.e-4f)
+
+bool intersectSegmentTriangle(const PxVec3& p, const PxVec3& q, const PxVec3& a, const PxVec3& b, const PxVec3& c, const PxPlane& trianglePlane)
+{
+ const PxVec3 N = trianglePlane.n;
+ const float D = trianglePlane.d;
+
+ PxVec3 intersectPoint;
+ float t = (-D - (p.dot(N))) / ((q - p).dot(N));
+ // If the parameter value is not between 0 and 1, there is no intersection
+ if (t > -SMALL_NUMBER && t < 1.f + SMALL_NUMBER)
+ {
+ intersectPoint = p + t * (q - p);
+ }
+ else
+ {
+ return false;
+ }
+
+ // Compute the normal of the triangle
+ const PxVec3 TriNorm = (b - a).cross(c - a);
+
+ // Compute twice area of triangle ABC
+ const float AreaABCInv = 1.0f / (N.dot(TriNorm));
+
+ // Compute v contribution
+ const float AreaPBC = N.dot((b - intersectPoint).cross(c - intersectPoint));
+ const float v = AreaPBC * AreaABCInv;
+ if (v <= 0.f)
+ return false;
+
+ // Compute w contribution
+ const float AreaPCA = N.dot((c - intersectPoint).cross(a - intersectPoint));
+ const float w = AreaPCA * AreaABCInv;
+ if (w <= 0.f)
+ return false;
+
+ const float u = 1.0f - v - w;
+ return u > 0.f;
+}
+
+void NvBlastExtTriangleIntersectionGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* params)
+{
+ const uint32_t* graphNodeIndexLinks = actor->graphNodeIndexLinks;
+ const uint32_t firstGraphNodeIndex = actor->firstGraphNodeIndex;
+ const uint32_t* adjacencyPartition = actor->adjacencyPartition;
+ const uint32_t* adjacentNodeIndices = actor->adjacentNodeIndices;
+ const uint32_t* adjacentBondIndices = actor->adjacentBondIndices;
+ const NvBlastBond* assetBonds = actor->assetBonds;
+ const NvBlastChunk* assetChunks = actor->assetChunks;
+ const uint32_t* chunkIndices = actor->chunkIndices;
+ const float* familyBondHealths = actor->familyBondHealths;
+ const NvBlastExtProgramParams* programParams = static_cast<const NvBlastExtProgramParams*>(params);
+ const NvBlastExtTriangleIntersectionDamageDesc& desc = *static_cast<const NvBlastExtTriangleIntersectionDamageDesc*>(programParams->damageDesc);
+ const physx::PxVec3& t0 = (reinterpret_cast<const physx::PxVec3&>(desc.position0));
+ const physx::PxVec3& t1 = (reinterpret_cast<const physx::PxVec3&>(desc.position1));
+ const physx::PxVec3& t2 = (reinterpret_cast<const physx::PxVec3&>(desc.position2));
+ const PxPlane trianglePlane(t0, t1, t2);
+
+ uint32_t outCount = 0;
+
+ const ExtDamageAcceleratorInternal* damageAccelerator = programParams->accelerator ? static_cast<const ExtDamageAcceleratorInternal*>(programParams->accelerator) : nullptr;
+ const uint32_t ACTOR_MINIMUM_NODE_COUNT_TO_ACCELERATE = actor->assetNodeCount / 3;
+ if (damageAccelerator && actor->graphNodeCount > ACTOR_MINIMUM_NODE_COUNT_TO_ACCELERATE)
+ {
+ const uint32_t CALLBACK_BUFFER_SIZE = 1000;
+
+ class AcceleratorCallback : public ExtDamageAcceleratorInternal::ResultCallback
+ {
+ public:
+ AcceleratorCallback(NvBlastFractureBuffers* commandBuffers, uint32_t& outCount, const NvBlastGraphShaderActor* actor, const NvBlastExtTriangleIntersectionDamageDesc& desc) :
+ ExtDamageAcceleratorInternal::ResultCallback(m_buffer, CALLBACK_BUFFER_SIZE),
+ m_actor(actor),
+ m_commandBuffers(commandBuffers),
+ m_outCount(outCount),
+ m_desc(desc)
+ {
+ }
+
+ virtual void processResults(const ExtDamageAcceleratorInternal::QueryBondData* bondBuffer, uint32_t count) override
+ {
+ const physx::PxVec3& t0 = (reinterpret_cast<const physx::PxVec3&>(m_desc.position0));
+ const physx::PxVec3& t1 = (reinterpret_cast<const physx::PxVec3&>(m_desc.position1));
+ const physx::PxVec3& t2 = (reinterpret_cast<const physx::PxVec3&>(m_desc.position2));
+ const PxPlane trianglePlane(t0, t1, t2);
+
+ for (uint32_t i = 0; i < count; i++)
{
- NvBlastBondFractureData& frac = commandBuffers->bondFractures[bondFractureCount++];
- frac.userdata = bond.userData;
- frac.nodeIndex0 = nodeIndex;
- frac.nodeIndex1 = neighbourIndex;
- frac.health = shear * damage;
+ const ExtDamageAcceleratorInternal::QueryBondData& bondData = bondBuffer[i];
+ if (m_actor->nodeActorIndices[bondData.node0] == m_actor->actorIndex)
+ {
+ if ((m_actor->familyBondHealths[bondData.bond] > 0.0f))
+ {
+ const NvBlastBond& bond = m_actor->assetBonds[bondData.bond];
+ const uint32_t chunkIndex0 = m_actor->chunkIndices[bondData.node0];
+ const uint32_t chunkIndex1 = m_actor->chunkIndices[bondData.node1];
+ const physx::PxVec3& c0 = (reinterpret_cast<const physx::PxVec3&>(m_actor->assetChunks[chunkIndex0].centroid));
+ const PxVec3& normal = (reinterpret_cast<const PxVec3&>(bond.normal));
+ const PxVec3& bondCentroid = (reinterpret_cast<const PxVec3&>(bond.centroid));
+ const physx::PxVec3& c1 = isInvalidIndex(chunkIndex1) ? (c0 + normal * (bondCentroid - c0).dot(normal)) :
+ (reinterpret_cast<const physx::PxVec3&>(m_actor->assetChunks[chunkIndex1].centroid));
+
+ if(intersectSegmentTriangle(c0, c1, t0, t1, t2, trianglePlane))
+ {
+ NvBlastBondFractureData& outCommand = m_commandBuffers->bondFractures[m_outCount++];
+ outCommand.nodeIndex0 = bondData.node0;
+ outCommand.nodeIndex1 = bondData.node1;
+ outCommand.health = m_desc.damage;
+ }
+ }
+ }
}
}
- if (nodeIndex == nextNode)
- break;
+ private:
+ const NvBlastGraphShaderActor* m_actor;
+ NvBlastFractureBuffers* m_commandBuffers;
+ uint32_t& m_outCount;
+ const NvBlastExtTriangleIntersectionDamageDesc& m_desc;
+
+ ExtDamageAcceleratorInternal::QueryBondData m_buffer[CALLBACK_BUFFER_SIZE];
+ };
- nodeIndex = nextNode;
- } while (!isInvalidIndex(nextNode));
+ AcceleratorCallback cb(commandBuffers, outCount, actor, desc);
+
+ damageAccelerator->findBondSegmentsPlaneIntersected(trianglePlane, cb);
+ }
+ else
+ {
+ uint32_t currentNodeIndex = firstGraphNodeIndex;
+ while (!Nv::Blast::isInvalidIndex(currentNodeIndex))
+ {
+ for (uint32_t adj = adjacencyPartition[currentNodeIndex]; adj < adjacencyPartition[currentNodeIndex + 1]; adj++)
+ {
+ uint32_t adjacentNodeIndex = adjacentNodeIndices[adj];
+ if (currentNodeIndex < adjacentNodeIndex)
+ {
+ uint32_t bondIndex = adjacentBondIndices[adj];
+ // skip bonds that are already broken or were visited already
+ // TODO: investigate why testing against health > -1.0f seems slower
+ // could reuse the island edge bitmap instead
+ if ((familyBondHealths[bondIndex] > 0.0f))
+ {
+ const NvBlastBond& bond = assetBonds[bondIndex];
+ const uint32_t chunkIndex0 = chunkIndices[currentNodeIndex];
+ const uint32_t chunkIndex1 = chunkIndices[adjacentNodeIndex];
+ const physx::PxVec3& c0 = (reinterpret_cast<const physx::PxVec3&>(assetChunks[chunkIndex0].centroid));
+ const PxVec3& normal = (reinterpret_cast<const PxVec3&>(bond.normal));
+ const PxVec3& bondCentroid = (reinterpret_cast<const PxVec3&>(bond.centroid));
+ const physx::PxVec3& c1 = isInvalidIndex(chunkIndex1) ? (c0 + normal * (bondCentroid - c0).dot(normal)) :
+ (reinterpret_cast<const physx::PxVec3&>(assetChunks[chunkIndex1].centroid));
+
+ if (intersectSegmentTriangle(c0, c1, t0, t1, t2, trianglePlane))
+ {
+ NvBlastBondFractureData& outCommand = commandBuffers->bondFractures[outCount++];
+ outCommand.nodeIndex0 = currentNodeIndex;
+ outCommand.nodeIndex1 = adjacentNodeIndex;
+ outCommand.health = desc.damage;
+ }
+ }
+ }
+ }
+ currentNodeIndex = graphNodeIndexLinks[currentNodeIndex];
+ }
}
- commandBuffers->bondFractureCount = bondFractureCount;
- commandBuffers->chunkFractureCount = chunkFractureCount;
+ commandBuffers->bondFractureCount = outCount;
+ commandBuffers->chunkFractureCount = 0;
}
-void NvBlastExtShearSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const NvBlastProgramParams* params)
+void NvBlastExtTriangleIntersectionSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* params)
{
- RadialProfileSubgraphShader<pointDistanceDamage<falloffProfile, NvBlastExtShearDamageDesc>>(commandBuffers, actor, params);
+ uint32_t chunkFractureCount = 0;
+ uint32_t chunkFractureCountMax = commandBuffers->chunkFractureCount;
+ const uint32_t chunkIndex = actor->chunkIndex;
+ const NvBlastChunk* assetChunks = actor->assetChunks;
+ const NvBlastChunk& chunk = assetChunks[chunkIndex];
+ const NvBlastExtProgramParams* programParams = static_cast<const NvBlastExtProgramParams*>(params);
+ const NvBlastExtTriangleIntersectionDamageDesc& desc = *static_cast<const NvBlastExtTriangleIntersectionDamageDesc*>(programParams->damageDesc);
+ const physx::PxVec3& t0 = (reinterpret_cast<const physx::PxVec3&>(desc.position0));
+ const physx::PxVec3& t1 = (reinterpret_cast<const physx::PxVec3&>(desc.position1));
+ const physx::PxVec3& t2 = (reinterpret_cast<const physx::PxVec3&>(desc.position2));
+ const PxPlane trianglePlane(t0, t1, t2);
+
+ for (uint32_t subChunkIndex = chunk.firstChildIndex; subChunkIndex < chunk.childIndexStop; subChunkIndex++)
+ {
+ const physx::PxVec3& c0 = (reinterpret_cast<const physx::PxVec3&>(assetChunks[subChunkIndex].centroid));
+ const physx::PxVec3& c1 = (reinterpret_cast<const physx::PxVec3&>(assetChunks[subChunkIndex + 1].centroid));
+ if (chunkFractureCount < chunkFractureCountMax && intersectSegmentTriangle(c0, c1, t0, t1, t2, trianglePlane))
+ {
+ NvBlastChunkFractureData& frac = commandBuffers->chunkFractures[chunkFractureCount++];
+ frac.chunkIndex = chunkIndex;
+ frac.health = desc.damage;
+ break;
+ }
+ }
+
+ commandBuffers->bondFractureCount = 0;
+ commandBuffers->chunkFractureCount = chunkFractureCount;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Helper Functions
+// Impact Spread Shader
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-bool NvBlastExtDamageActorRadialFalloff(NvBlastActor* actor, NvBlastFractureBuffers* buffers, const NvBlastExtRadialDamageDesc* damageDescBuffer, uint32_t damageDescCount, const NvBlastExtMaterial* material, NvBlastLog logFn, NvBlastTimers* timers)
+void NvBlastExtImpactSpreadGraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* params)
{
- NvBlastDamageProgram program =
+ uint32_t chunkFractureCount = 0;
+ uint32_t chunkFractureCountMax = commandBuffers->chunkFractureCount;
+ uint32_t bondFractureCount = 0;
+ uint32_t bondFractureCountMax = commandBuffers->bondFractureCount;
+ const NvBlastExtProgramParams* programParams = static_cast<const NvBlastExtProgramParams*>(params);
+ const NvBlastExtImpactSpreadDamageDesc& desc = *static_cast<const NvBlastExtImpactSpreadDamageDesc*>(programParams->damageDesc);
+ const uint32_t* graphNodeIndexLinks = actor->graphNodeIndexLinks;
+ const uint32_t firstGraphNodeIndex = actor->firstGraphNodeIndex;
+ const uint32_t* chunkIndices = actor->chunkIndices;
+ const uint32_t* adjacencyPartition = actor->adjacencyPartition;
+ const uint32_t* adjacentNodeIndices = actor->adjacentNodeIndices;
+ const uint32_t* adjacentBondIndices = actor->adjacentBondIndices;
+ const NvBlastBond* assetBonds = actor->assetBonds;
+ const NvBlastChunk* assetChunks = actor->assetChunks;
+ const float* familyBondHealths = actor->familyBondHealths;
+ const float* supportChunkHealths = actor->supportChunkHealths;
+
+ // Find nearest chunk.
+ uint32_t closestNode = findClosestNode(desc.position
+ , firstGraphNodeIndex, graphNodeIndexLinks
+ , adjacencyPartition, adjacentNodeIndices, adjacentBondIndices
+ , assetBonds, familyBondHealths
+ , assetChunks, supportChunkHealths, chunkIndices);
+
+ uint32_t nodeIndex = closestNode;
+
+ // Damage this chunk
+ if (chunkFractureCount < chunkFractureCountMax)
{
- NvBlastExtFalloffGraphShader,
- NvBlastExtFalloffSubgraphShader
- };
+ const uint32_t chunkIndex = chunkIndices[nodeIndex];
+ NvBlastChunkFractureData& frac = commandBuffers->chunkFractures[chunkFractureCount++];
+ frac.chunkIndex = chunkIndex;
+ frac.health = desc.damage;
+ }
- NvBlastProgramParams params =
+ // Breadth-first support graph traversal. For radial falloff metric distance is measured along the edges of the graph
+ ExtDamageAcceleratorInternal* damageAccelerator = programParams->accelerator ? static_cast<ExtDamageAcceleratorInternal*>(programParams->accelerator) : nullptr;
+ NVBLAST_ASSERT_WITH_MESSAGE(damageAccelerator, "This shader requires damage accelerator passed");
+ if (damageAccelerator)
{
- damageDescBuffer,
- damageDescCount,
- material
- };
+ struct NodeData
+ {
+ uint32_t index;
+ float distance;
+ };
+
+ // Calculating scratch size and requesting it from the accelerator
+ const uint32_t bondCount = actor->adjacencyPartition[actor->assetNodeCount];
+ const size_t nodeQueueSize = align16(FixedQueue<NodeData>::requiredMemorySize(actor->graphNodeCount));
+ const size_t visitedBitmapSize = align16(FixedBitmap::requiredMemorySize(bondCount));
+ const size_t scratchSize = 16 + nodeQueueSize + visitedBitmapSize;
+
+ void* scratch = damageAccelerator->getImmediateScratch(scratchSize);
+
+ // prepare intermediate data on scratch
+ scratch = (void*)align16((size_t)scratch); // Bump to 16-byte alignment
+ FixedQueue<NodeData>* nodeQueue = new (scratch)FixedQueue<NodeData>(actor->graphNodeCount);
+ scratch = pointerOffset(scratch, align16(nodeQueueSize));
+ FixedBitmap* visitedBitmap = new (scratch)FixedBitmap(bondCount);
+ scratch = pointerOffset(scratch, align16(FixedBitmap::requiredMemorySize(bondCount)));
+
+ // initalize traversal
+ nodeQueue->pushBack({ nodeIndex, 0.f });
+ visitedBitmap->clear();
+
+ while (!nodeQueue->empty())
+ {
+ NodeData currentNode = nodeQueue->popFront();
+ const uint32_t startIndex = adjacencyPartition[currentNode.index];
+ const uint32_t stopIndex = adjacencyPartition[currentNode.index + 1];
+
+ for (uint32_t adjacentNodeIndex = startIndex; adjacentNodeIndex < stopIndex; adjacentNodeIndex++)
+ {
+ const uint32_t neighbourIndex = adjacentNodeIndices[adjacentNodeIndex];
+ const uint32_t bondIndex = adjacentBondIndices[adjacentNodeIndex];
+ const NvBlastBond& bond = assetBonds[bondIndex];
+
+ const PxVec3& bondCentroid = (reinterpret_cast<const PxVec3&>(bond.centroid));
+
+ if (!(familyBondHealths[bondIndex] > 0.0f))
+ continue;
+
+ if (visitedBitmap->test(bondIndex))
+ continue;
+ visitedBitmap->set(bondIndex);
+
+ const uint32_t chunkIndex0 = chunkIndices[currentNode.index];
+ const uint32_t chunkIndex1 = chunkIndices[neighbourIndex];
+ const physx::PxVec3& c0 = reinterpret_cast<const physx::PxVec3&>(assetChunks[chunkIndex0].centroid);
+ bool isNeighbourWorldChunk = isInvalidIndex(chunkIndex1);
+ const physx::PxVec3& c1 = isNeighbourWorldChunk ? bondCentroid : (reinterpret_cast<const physx::PxVec3&>(assetChunks[chunkIndex1].centroid));
+
+ const float distance = (c1 - c0).magnitude() * (isNeighbourWorldChunk ? 2.f : 1.f);
+ float totalDistance = currentNode.distance + distance;
+ float totalDamage = falloffProfile(desc.minRadius, desc.maxRadius, totalDistance);
+ if (totalDamage > 0.0f && bondFractureCount < bondFractureCountMax)
+ {
+ NvBlastBondFractureData& frac = commandBuffers->bondFractures[bondFractureCount++];
+ frac.userdata = bond.userData;
+ frac.nodeIndex0 = currentNode.index;
+ frac.nodeIndex1 = neighbourIndex;
+ frac.health = totalDamage;
+ if (!isNeighbourWorldChunk)
+ {
+ nodeQueue->pushBack({ neighbourIndex, totalDistance });
+ }
+ }
+ }
+ }
+ }
+
+ commandBuffers->bondFractureCount = bondFractureCount;
+ commandBuffers->chunkFractureCount = chunkFractureCount;
+}
- NvBlastActorGenerateFracture(buffers, actor, program, &params, logFn, timers);
- if (buffers->bondFractureCount > 0 || buffers->chunkFractureCount > 0)
+void NvBlastExtImpactSpreadSubgraphShader(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* params)
+{
+ uint32_t chunkFractureCount = 0;
+ uint32_t chunkFractureCountMax = commandBuffers->chunkFractureCount;
+ const uint32_t chunkIndex = actor->chunkIndex;
+ const NvBlastExtProgramParams* programParams = static_cast<const NvBlastExtProgramParams*>(params);
+ const NvBlastExtImpactSpreadDamageDesc& desc = *static_cast<const NvBlastExtImpactSpreadDamageDesc*>(programParams->damageDesc);
+
+ if (chunkFractureCount < chunkFractureCountMax)
{
- NvBlastActorApplyFracture(nullptr, actor, buffers, logFn, timers);
- return true;
+ NvBlastChunkFractureData& frac = commandBuffers->chunkFractures[chunkFractureCount++];
+ frac.chunkIndex = chunkIndex;
+ frac.health = desc.damage;
}
- return false;
-} \ No newline at end of file
+ commandBuffers->bondFractureCount = 0;
+ commandBuffers->chunkFractureCount = chunkFractureCount;
+}
diff --git a/sdk/globals/include/NvBlastDebugRender.h b/sdk/globals/include/NvBlastDebugRender.h
new file mode 100644
index 0000000..793e8ce
--- /dev/null
+++ b/sdk/globals/include/NvBlastDebugRender.h
@@ -0,0 +1,64 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2016-2017 NVIDIA Corporation. All rights reserved.
+
+#pragma once
+
+#include <NvCTypes.h>
+
+namespace Nv
+{
+namespace Blast
+{
+
+
+/**
+Used to store a single line and colour for debug rendering.
+*/
+struct DebugLine
+{
+ DebugLine(const NvcVec3& p0, const NvcVec3& p1, uint32_t c)
+ : pos0(p0), color0(c), pos1(p1), color1(c) {}
+
+ NvcVec3 pos0;
+ uint32_t color0;
+ NvcVec3 pos1;
+ uint32_t color1;
+};
+
+
+/**
+Debug Buffer
+*/
+struct DebugBuffer
+{
+ const DebugLine* lines;
+ uint32_t lineCount;
+};
+
+
+} // namespace Blast
+} // namespace Nv
diff --git a/sdk/lowlevel/include/NvBlast.h b/sdk/lowlevel/include/NvBlast.h
index 0a4872f..bc01253 100644
--- a/sdk/lowlevel/include/NvBlast.h
+++ b/sdk/lowlevel/include/NvBlast.h
@@ -740,7 +740,7 @@ NVBLAST_API void NvBlastActorGenerateFracture
NvBlastFractureBuffers* commandBuffers,
const NvBlastActor* actor,
const NvBlastDamageProgram program,
- const NvBlastProgramParams* programParams,
+ const void* programParams,
NvBlastLog logFn,
NvBlastTimers* timers
);
diff --git a/sdk/lowlevel/include/NvBlastTypes.h b/sdk/lowlevel/include/NvBlastTypes.h
index 9fbeef9..91a3544 100644
--- a/sdk/lowlevel/include/NvBlastTypes.h
+++ b/sdk/lowlevel/include/NvBlastTypes.h
@@ -490,6 +490,9 @@ A single actor's representation used by NvBlastGraphShaderFunction.
*/
struct NvBlastGraphShaderActor
{
+ uint32_t actorIndex; //!< Actor's index.
+ uint32_t graphNodeCount; //!< Actor's graph node count.
+ uint32_t assetNodeCount; //!< Asset node count.
uint32_t firstGraphNodeIndex; //!< Entry index for graphNodeIndexLinks
const uint32_t* graphNodeIndexLinks; //!< Linked index list of connected nodes. Traversable with nextIndex = graphNodeIndexLinks[currentIndex], terminates with 0xFFFFFFFF.
const uint32_t* chunkIndices; //!< Graph's map from node index to support chunk index.
@@ -500,27 +503,7 @@ struct NvBlastGraphShaderActor
const NvBlastChunk* assetChunks; //!< NvBlastChunks geometry in the NvBlastAsset.
const float* familyBondHealths; //!< Actual bond health values for broken bond detection.
const float* supportChunkHealths; //!< Actual chunk health values for dead chunk detection.
-};
-
-
-/**
-Damage program params.
-
-Custom user params to be passed in shader functions. This structure hints recommended parameters layout, but it
-doesn't required to be this way.
-
-The idea of this 'hint' is that damage parameters are basically 2 entities: material + damage description.
-1. Material is something that describes an actor properties (e.g. mass, stiffness, fragility) which are not expected to be changed often.
-2. Damage description is something that describes particular damage event (e.g. position, radius and force of explosion).
-
-Also this damage program hints that there could be more than one damage event happening and processed per one shader call (for efficiency reasons).
-So different damage descriptions can be stacked and passed in one shader call (while material is kept the same obviously).
-*/
-struct NvBlastProgramParams
-{
- const void* damageDescBuffer; //!< array of damage descriptions
- uint32_t damageDescCount; //!< number of damage descriptions in array
- const void* material; //!< pointer to material
+ const uint32_t* nodeActorIndices; //!< Family's map from node index to actor index.
};
@@ -544,7 +527,7 @@ creates a list of NvBlastFractureCommand to be applied to the respective NvBlast
Typically requires an array of size (number of support chunks) + (number of bonds) of the processed asset
but may depend on the actual implementation.
\param[in] actor The actor representation used for creating commands.
-\param[in] params A set of parameters defined by the damage shader implementer.
+\param[in] programParams A set of parameters defined by the damage shader implementer.
Interpretation of NvBlastFractureBuffers:
As input:
@@ -559,7 +542,7 @@ Health values denote how much damage is to be applied.
@see NvBlastFractureBuffers NvBlastGraphShaderActor
*/
-typedef void(*NvBlastGraphShaderFunction)(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const NvBlastProgramParams* params);
+typedef void(*NvBlastGraphShaderFunction)(NvBlastFractureBuffers* commandBuffers, const NvBlastGraphShaderActor* actor, const void* programParams);
/**
@@ -572,7 +555,7 @@ creates a list of NvBlastFractureCommand to be applied to the respective NvBlast
Typically requires an array of size (number of support chunks) + (number of bonds) of the processed asset
but may depend on the actual implementation.
\param[in] actor The actor representation used for creating commands.
-\param[in] params A set of parameters defined by the damage shader implementer.
+\param[in] programParams A set of parameters defined by the damage shader implementer.
Interpretation of NvBlastFractureBuffers:
As input:
@@ -587,7 +570,7 @@ Health values denote how much damage is to be applied.
@see NvBlastFractureBuffers NvBlastSubgraphShaderActor
*/
-typedef void(*NvBlastSubgraphShaderFunction)(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const NvBlastProgramParams* params);
+typedef void(*NvBlastSubgraphShaderFunction)(NvBlastFractureBuffers* commandBuffers, const NvBlastSubgraphShaderActor* actor, const void* programParams);
/**
diff --git a/sdk/lowlevel/source/NvBlastActor.cpp b/sdk/lowlevel/source/NvBlastActor.cpp
index afed7bb..8c90d0f 100644
--- a/sdk/lowlevel/source/NvBlastActor.cpp
+++ b/sdk/lowlevel/source/NvBlastActor.cpp
@@ -187,7 +187,7 @@ uint32_t Actor::damageBond(const NvBlastBondFractureData& cmd)
}
-void Actor::generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams,
+void Actor::generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBlastDamageProgram& program, const void* programParams,
NvBlastLog logFn, NvBlastTimers* timers) const
{
NVBLASTLL_CHECK(commandBuffers != nullptr, logFn, "Actor::generateFracture: NULL commandBuffers pointer input.", return);
@@ -214,6 +214,9 @@ void Actor::generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBla
if (graphNodeCount > 1 && program.graphShaderFunction != nullptr)
{
const NvBlastGraphShaderActor shaderActor = {
+ getIndex(),
+ getGraphNodeCount(),
+ graph->m_nodeCount,
getFirstGraphNodeIndex(),
getGraphNodeIndexLinks(),
graph->getChunkIndices(),
@@ -223,7 +226,8 @@ void Actor::generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBla
getBonds(),
getChunks(),
getBondHealths(),
- getLowerSupportChunkHealths()
+ getLowerSupportChunkHealths(),
+ getFamilyHeader()->getFamilyGraph()->getIslandIds()
};
program.graphShaderFunction(commandBuffers, &shaderActor, programParams);
@@ -817,7 +821,7 @@ void NvBlastActorGenerateFracture
NvBlastFractureBuffers* commandBuffers,
const NvBlastActor* actor,
const NvBlastDamageProgram program,
- const NvBlastProgramParams* programParams,
+ const void* programParams,
NvBlastLog logFn,
NvBlastTimers* timers
)
diff --git a/sdk/lowlevel/source/NvBlastActor.h b/sdk/lowlevel/source/NvBlastActor.h
index 7e30660..8474e83 100644
--- a/sdk/lowlevel/source/NvBlastActor.h
+++ b/sdk/lowlevel/source/NvBlastActor.h
@@ -331,7 +331,7 @@ public:
/**
See NvBlastActorGenerateFracture
*/
- void generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams, NvBlastLog logFn, NvBlastTimers* timers) const;
+ void generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBlastDamageProgram& program, const void* programParams, NvBlastLog logFn, NvBlastTimers* timers) const;
/**
Damage bond between two chunks by health amount (instance graph also will be notified in case bond is broken after).
diff --git a/sdk/toolkit/include/NvBlastTkActor.h b/sdk/toolkit/include/NvBlastTkActor.h
index 73e83cd..12db51f 100644
--- a/sdk/toolkit/include/NvBlastTkActor.h
+++ b/sdk/toolkit/include/NvBlastTkActor.h
@@ -174,38 +174,7 @@ public:
\param[in] program A NvBlastDamageProgram containing damage shaders.
\param[in] programParams Parameters for the NvBlastDamageProgram.
*/
- virtual void damage(const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams) = 0;
-
- /**
- Apply damage to this actor.
-
- Actual damage processing is deferred till the group worker process() call. Sets actor in 'pending' state.
-
- Damage Desc will be stacked into NvBlastProgramParams. NvBlastProgramParams will be passed into shader.
-
- Material set on actor's family will be passed into NvBlastProgramParams.
-
- \param[in] program A NvBlastDamageProgram containing damage shaders.
- \param[in] damageDesc Parameters to be put in NvBlastProgramParams, have to be POD type (will be copied).
- \param[in] descSize Size of damageDesc in bytes. Required to copy and store Damage Desc.
- */
- virtual void damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize) = 0;
-
- /**
- Apply damage to this actor.
-
- Actual damage processing is deferred till the group worker process() call. Sets actor in 'pending' state.
-
- Damage Desc will be stacked into NvBlastDamageProgram. NvBlastDamageProgram will be passed into shader.
-
- This function overload explicitly sets a material to be passed into NvBlastProgramParams, it must be valid until the group endProcess() call.
-
- \param[in] program A NvBlastDamageProgram containing damage shaders.
- \param[in] damageDesc Parameters to be put in NvBlastDamageProgram, have to be POD type (will be copied).
- \param[in] descSize Size of damageDesc in bytes. Required to copy and store Damage Desc.
- \param[in] material Material to be passed into NvBlastProgramParams. Must be valid until the group endProcess() call.
- */
- virtual void damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize, const void* material) = 0;
+ virtual void damage(const NvBlastDamageProgram& program, const void* programParams) = 0;
/**
Creates fracture commands for the actor using an NvBlastMaterialFunction.
@@ -219,7 +188,7 @@ public:
\param[in] program A NvBlastDamageProgram containing damage shaders.
\param[in] programParams Parameters for the NvBlastDamageProgram.
*/
- virtual void generateFracture(NvBlastFractureBuffers* commands, const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams) const = 0;
+ virtual void generateFracture(NvBlastFractureBuffers* commands, const NvBlastDamageProgram& program, const void* programParams) const = 0;
/**
Function applies the direct fracture and breaks graph bonds/edges as necessary. Sets actor in 'pending' state if any bonds or chunks were damaged. Dispatches FractureCommand events.
diff --git a/sdk/toolkit/include/NvBlastTkFamily.h b/sdk/toolkit/include/NvBlastTkFamily.h
index 02b91cb..5549099 100644
--- a/sdk/toolkit/include/NvBlastTkFamily.h
+++ b/sdk/toolkit/include/NvBlastTkFamily.h
@@ -119,20 +119,6 @@ public:
\param[in] group The group for new actors to be placed in.
*/
virtual void reinitialize(const NvBlastFamily* newFamily, TkGroup* group = nullptr) = 0;
-
- /**
- The default material to be passed into NvBlastDamageProgram when a TkActor in this family is damaged.
-
- \return a pointer to the default material.
- */
- virtual const void* getMaterial() const = 0;
-
- /**
- Set the default material to be passed into NvBlastDamageProgram when a TkActor in this family is damaged. Must be valid till group endProcess() call.
-
- \param[in] material The material to be the new default.
- */
- virtual void setMaterial(const void* material) = 0;
};
} // namespace Blast
diff --git a/sdk/toolkit/source/NvBlastTkActorImpl.cpp b/sdk/toolkit/source/NvBlastTkActorImpl.cpp
index cc2e322..c4515f1 100644
--- a/sdk/toolkit/source/NvBlastTkActorImpl.cpp
+++ b/sdk/toolkit/source/NvBlastTkActorImpl.cpp
@@ -62,9 +62,9 @@ TkActorImpl* TkActorImpl::create(const TkActorDesc& desc)
TkActorImpl* actor = family->addActor(actorLL);
- // Add internal joints
if (actor != nullptr)
{
+ // Add internal joints
const uint32_t internalJointCount = asset->getJointDescCountInternal();
const TkAssetJointDesc* jointDescs = asset->getJointDescsInternal();
const NvBlastSupportGraph graph = asset->getGraph();
@@ -82,6 +82,9 @@ TkActorImpl* TkActorImpl::create(const TkActorDesc& desc)
TkJointImpl* joint = new (joints + jointNum) TkJointImpl(jointDesc, family);
actor->addJoint(joint->m_links[0]);
}
+
+ // Mark as damaged to trigger first split call. It could be the case that asset is already split into few actors initially.
+ actor->markAsDamaged();
}
return actor;
@@ -277,7 +280,7 @@ TkActorImpl::operator Nv::Blast::TkActorData() const
}
-void TkActorImpl::damage(const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams)
+void TkActorImpl::damage(const NvBlastDamageProgram& program, const void* programParams)
{
BLAST_PROFILE_SCOPE_L("TkActor::damage");
@@ -295,57 +298,13 @@ void TkActorImpl::damage(const NvBlastDamageProgram& program, const NvBlastProgr
if (NvBlastActorCanFracture(m_actorLL, logLL))
{
- m_damageBuffer.pushBack(DamageData(program, programParams));
+ m_damageBuffer.pushBack(DamageData{ program, programParams});
makePending();
}
}
-void TkActorImpl::damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize)
-{
- damage(program, damageDesc, descSize, m_family->getMaterial());
-}
-
-
-void TkActorImpl::damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize, const void* material)
-{
- BLAST_PROFILE_SCOPE_L("TkActor::damage");
-
- if (m_group == nullptr)
- {
- NVBLAST_LOG_WARNING("TkActor::damage: actor is not in a group, cannot fracture.");
- return;
- }
-
- if (m_group->isProcessing())
- {
- NVBLAST_LOG_WARNING("TkActor::damage: group is being processed, cannot fracture this actor.");
- return;
- }
-
- if (NvBlastActorCanFracture(m_actorLL, logLL))
- {
- bool appended = false;
- for (auto& damageData : m_damageBuffer)
- {
- if (damageData.tryAppend(program, material, damageDesc, descSize))
- {
- appended = true;
- break;
- }
- }
-
- if (!appended)
- {
- m_damageBuffer.pushBack(DamageData(program, material, damageDesc, descSize));
- }
-
- makePending();
- }
-}
-
-
-void TkActorImpl::generateFracture(NvBlastFractureBuffers* commands, const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams) const
+void TkActorImpl::generateFracture(NvBlastFractureBuffers* commands, const NvBlastDamageProgram& program, const void* programParams) const
{
BLAST_PROFILE_SCOPE_L("TkActor::generateFracture");
@@ -410,42 +369,5 @@ bool TkActorImpl::isBoundToWorld() const
}
-//////// TkActorImpl::DamageData methods ////////
-
-static bool operator==(const NvBlastDamageProgram& lhs, const NvBlastDamageProgram& rhs)
-{
- return lhs.graphShaderFunction == rhs.graphShaderFunction && lhs.subgraphShaderFunction == rhs.subgraphShaderFunction;
-}
-
-
-TkActorImpl::DamageData::DamageData(const NvBlastDamageProgram& program, const NvBlastProgramParams* params)
- : m_program(program), m_programParams(params), m_damageDescCount(0)
-{
-}
-
-
-TkActorImpl::DamageData::DamageData(const NvBlastDamageProgram& program, const void* material, const void* desc, uint32_t descSize)
- : m_program(program), m_material(material), m_damageDescs((char*)desc, (char*)desc + descSize), m_damageDescCount(1)
-{
-}
-
-
-bool TkActorImpl::DamageData::tryAppend(const NvBlastDamageProgram& program, const void* material, const void* desc, uint32_t descSize)
-{
- if (getType() == Buffered && m_program == program && m_material == material)
- {
- const uint32_t currentDescSize = m_damageDescs.size() / m_damageDescCount;
- if (descSize == currentDescSize)
- {
- const uint32_t s = m_damageDescs.size();
- m_damageDescs.resizeUninitialized(s + static_cast<uint32_t>(descSize));
- memcpy(m_damageDescs.begin() + s, desc, descSize);
- m_damageDescCount++;
- return true;
- }
- }
- return false;
-}
-
} // namespace Blast
} // namespace Nv
diff --git a/sdk/toolkit/source/NvBlastTkActorImpl.h b/sdk/toolkit/source/NvBlastTkActorImpl.h
index 33fde68..2076654 100644
--- a/sdk/toolkit/source/NvBlastTkActorImpl.h
+++ b/sdk/toolkit/source/NvBlastTkActorImpl.h
@@ -103,13 +103,11 @@ public:
virtual uint32_t getSplitMaxActorCount() const override;
- virtual void damage(const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams) override;
- virtual void damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize) override;
- virtual void damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize, const void* material) override;
+ virtual void damage(const NvBlastDamageProgram& program, const void* programParams) override;
virtual bool isPending() const override;
- virtual void generateFracture(NvBlastFractureBuffers* commands, const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams) const override;
+ virtual void generateFracture(NvBlastFractureBuffers* commands, const NvBlastDamageProgram& program, const void* programParams) const override;
virtual void applyFracture(NvBlastFractureBuffers* eventBuffers, const NvBlastFractureBuffers* commands) override;
@@ -211,36 +209,6 @@ public:
private:
/**
- Used to buffer damage for deferred fracture generation. Unifies 2 different ways to pass and store damage data.
- */
- struct DamageData
- {
- DamageData(const NvBlastDamageProgram& program, const NvBlastProgramParams* params);
- DamageData(const NvBlastDamageProgram& program, const void* material, const void* desc, uint32_t descSize);
-
- bool tryAppend(const NvBlastDamageProgram& program, const void* material, const void* desc, uint32_t descSize);
- void generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBlastActor* actorLL, NvBlastTimers* timers) const;
-
- enum Type
- {
- Plain,
- Buffered
- };
-
- Type getType() const;
-
- NvBlastDamageProgram m_program;
- union
- {
- const void* m_material; //!< for Buffered type
- const NvBlastProgramParams* m_programParams; //!< for Plain type
- };
- Array<char>::type m_damageDescs;
- uint32_t m_damageDescCount;
- };
-
-
- /**
Functions to raise or check 'damaged' state: this actor will take the split step.
'damaged' actors automatically become 'pending' also.
*/
@@ -259,17 +227,22 @@ private:
void addJoint(TkJointLink& jointLink);
void removeJoint(TkJointLink& jointLink);
+ struct DamageData
+ {
+ NvBlastDamageProgram program;
+ const void* programParams;
+ };
// Data
- NvBlastActor* m_actorLL; //!< The low-level actor associated with this actor
- TkFamilyImpl* m_family; //!< The TkFamilyImpl to which this actor belongs
- TkGroupImpl* m_group; //!< The TkGroupImpl (if any) to which this actor belongs
- uint32_t m_groupJobIndex; //!< The index of this actor's job within its group's job list
- physx::PxFlags<TkActorFlag::Enum, char> m_flags; //!< Status flags for this actor
- Array<DamageData>::type m_damageBuffer; //!< Buffered damage input
- uint32_t m_jointCount; //!< The number of joints referenced in m_jointList
- DList m_jointList; //!< A doubly-linked list of joint references
+ NvBlastActor* m_actorLL; //!< The low-level actor associated with this actor
+ TkFamilyImpl* m_family; //!< The TkFamilyImpl to which this actor belongs
+ TkGroupImpl* m_group; //!< The TkGroupImpl (if any) to which this actor belongs
+ uint32_t m_groupJobIndex; //!< The index of this actor's job within its group's job list
+ physx::PxFlags<TkActorFlag::Enum, char> m_flags; //!< Status flags for this actor
+ Array<DamageData>::type m_damageBuffer; //!< Buffered damage input
+ uint32_t m_jointCount; //!< The number of joints referenced in m_jointList
+ DList m_jointList; //!< A doubly-linked list of joint references
//#if NV_PROFILE
NvBlastTimers m_timers; //!< If profiling, each actor stores timing data
@@ -351,32 +324,6 @@ NV_INLINE void TkActorImpl::removeJoint(TkJointLink& jointLink)
}
-//////// TkActorImpl::DamageData inline methods ////////
-
-NV_INLINE TkActorImpl::DamageData::Type TkActorImpl::DamageData::getType() const
-{
- return m_damageDescCount > 0 ? Buffered : Plain;
-}
-
-
-NV_INLINE void TkActorImpl::DamageData::generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBlastActor* actorLL, NvBlastTimers* timers) const
-{
- if (getType() == Plain)
- {
- NvBlastActorGenerateFracture(commandBuffers, actorLL, m_program, m_programParams, logLL, timers);
- }
- else
- {
- const NvBlastProgramParams programParams = {
- m_damageDescs.begin(),
- m_damageDescCount,
- m_material,
- };
- NvBlastActorGenerateFracture(commandBuffers, actorLL, m_program, &programParams, logLL, timers);
- }
-}
-
-
//////// TkActorImpl::JointIt methods ////////
NV_INLINE TkActorImpl::JointIt::JointIt(const TkActorImpl& actor, Direction dir) : DList::It(actor.m_jointList, dir) {}
diff --git a/sdk/toolkit/source/NvBlastTkFamilyImpl.cpp b/sdk/toolkit/source/NvBlastTkFamilyImpl.cpp
index b62be2e..f12b1d5 100644
--- a/sdk/toolkit/source/NvBlastTkFamilyImpl.cpp
+++ b/sdk/toolkit/source/NvBlastTkFamilyImpl.cpp
@@ -49,12 +49,12 @@ NVBLASTTK_DEFINE_TYPE_IDENTIFIABLE(Family);
//////// Member functions ////////
-TkFamilyImpl::TkFamilyImpl() : m_familyLL(nullptr), m_internalJointCount(0), m_asset(nullptr), m_material(nullptr)
+TkFamilyImpl::TkFamilyImpl() : m_familyLL(nullptr), m_internalJointCount(0), m_asset(nullptr)
{
}
-TkFamilyImpl::TkFamilyImpl(const NvBlastID& id) : TkFamilyType(id), m_familyLL(nullptr), m_internalJointCount(0), m_asset(nullptr), m_material(nullptr)
+TkFamilyImpl::TkFamilyImpl(const NvBlastID& id) : TkFamilyType(id), m_familyLL(nullptr), m_internalJointCount(0), m_asset(nullptr)
{
}
diff --git a/sdk/toolkit/source/NvBlastTkFamilyImpl.h b/sdk/toolkit/source/NvBlastTkFamilyImpl.h
index 577326b..7210f0a 100644
--- a/sdk/toolkit/source/NvBlastTkFamilyImpl.h
+++ b/sdk/toolkit/source/NvBlastTkFamilyImpl.h
@@ -82,10 +82,6 @@ public:
virtual const TkAsset* getAsset() const override;
virtual void reinitialize(const NvBlastFamily* newFamily, TkGroup* group) override;
-
- virtual const void* getMaterial() const override;
-
- virtual void setMaterial(const void* material) override;
// End TkFamily
// Public methods
@@ -145,7 +141,6 @@ private:
Array<JointSet*>::type m_jointSets;
FamilyIDMap m_familyIDMap;
const TkAssetImpl* m_asset;
- const void* m_material;
TkEventQueue m_queue;
};
@@ -187,18 +182,6 @@ NV_INLINE TkActorImpl* TkFamilyImpl::getActorByActorLL(const NvBlastActor* actor
}
-NV_INLINE const void* TkFamilyImpl::getMaterial() const
-{
- return m_material;
-}
-
-
-NV_INLINE void TkFamilyImpl::setMaterial(const void* material)
-{
- m_material = material;
-}
-
-
NV_INLINE Array<TkActorImpl>::type& TkFamilyImpl::getActorsInternal()
{
return m_actors;
diff --git a/sdk/toolkit/source/NvBlastTkTaskImpl.cpp b/sdk/toolkit/source/NvBlastTkTaskImpl.cpp
index bc825a7..d88607c 100644
--- a/sdk/toolkit/source/NvBlastTkTaskImpl.cpp
+++ b/sdk/toolkit/source/NvBlastTkTaskImpl.cpp
@@ -180,7 +180,7 @@ void TkWorker::process(TkWorkerJob& j)
NvBlastFractureBuffers commandBuffer = m_tempBuffer;
BLAST_PROFILE_ZONE_BEGIN("Material");
- damage.generateFracture(&commandBuffer, actorLL, timers);
+ NvBlastActorGenerateFracture(&commandBuffer, actorLL, damage.program, damage.programParams, logLL, timers);
BLAST_PROFILE_ZONE_END("Material");
if (commandBuffer.chunkFractureCount > 0 || commandBuffer.bondFractureCount > 0)