aboutsummaryrefslogtreecommitdiff
path: root/sdk
diff options
context:
space:
mode:
authorBryan Galdrikian <[email protected]>2018-01-22 14:04:16 -0800
committerBryan Galdrikian <[email protected]>2018-01-22 14:04:16 -0800
commit1dc1a87fba520bb45c1ce8165e8ea2c83c0a308d (patch)
tree5f8ca75a6b92c60fb5cf3b14282fc4cc1c127eb2 /sdk
parentUpdating readme.md to show updated UE4 Blast integration branches (diff)
downloadblast-1dc1a87fba520bb45c1ce8165e8ea2c83c0a308d.tar.xz
blast-1dc1a87fba520bb45c1ce8165e8ea2c83c0a308d.zip
Changes for 1.1.2 release candidate
See README.md, docs/release_notes.txt
Diffstat (limited to 'sdk')
-rw-r--r--sdk/compiler/cmake/NvBlastExtAuthoring.cmake5
-rw-r--r--sdk/compiler/cmake/modules/FindApexSDK.cmake211
-rw-r--r--sdk/compiler/cmake/modules/FindBoostMultiprecision.cmake1
-rw-r--r--sdk/compiler/cmake/modules/FindCapnProtoSDK.cmake4
-rw-r--r--sdk/compiler/cmake/modules/FindFBXSDK.cmake4
-rw-r--r--sdk/compiler/cmake/modules/FindGoogleTestNV.cmake4
-rw-r--r--sdk/compiler/cmake/modules/FindPhysXSDK.cmake117
-rw-r--r--sdk/compiler/cmake/modules/FindPxSharedSDK.cmake34
-rw-r--r--sdk/extensions/authoring/include/NvBlastExtAuthoring.h53
-rw-r--r--sdk/extensions/authoring/include/NvBlastExtAuthoringBondGenerator.h4
-rw-r--r--sdk/extensions/authoring/include/NvBlastExtAuthoringCutout.h106
-rw-r--r--sdk/extensions/authoring/include/NvBlastExtAuthoringFractureTool.h141
-rw-r--r--sdk/extensions/authoring/include/NvBlastExtAuthoringTypes.h8
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp34
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.cpp33
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.h4
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringCutoutImpl.cpp2508
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringCutoutImpl.h129
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.cpp390
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.h63
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.cpp83
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.h12
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.cpp21
-rw-r--r--sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.h11
-rw-r--r--sdk/extensions/exporter/include/NvBlastExtExporter.h13
-rw-r--r--sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp31
-rw-r--r--sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h9
-rw-r--r--sdk/extensions/exporter/source/NvBlastExtExporterObjReader.cpp33
-rw-r--r--sdk/extensions/exporter/source/NvBlastExtExporterObjReader.h14
-rw-r--r--sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.cpp107
-rw-r--r--sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.h11
-rw-r--r--sdk/lowlevel/source/NvBlastActor.cpp53
-rw-r--r--sdk/lowlevel/source/NvBlastAsset.cpp8
33 files changed, 3936 insertions, 323 deletions
diff --git a/sdk/compiler/cmake/NvBlastExtAuthoring.cmake b/sdk/compiler/cmake/NvBlastExtAuthoring.cmake
index 00ecfc1..c0818c2 100644
--- a/sdk/compiler/cmake/NvBlastExtAuthoring.cmake
+++ b/sdk/compiler/cmake/NvBlastExtAuthoring.cmake
@@ -29,8 +29,9 @@ SET(PUBLIC_FILES
${AUTHORING_EXT_INCLUDE_DIR}/NvBlastExtAuthoringFractureTool.h
${AUTHORING_EXT_INCLUDE_DIR}/NvBlastExtAuthoringMesh.h
${AUTHORING_EXT_INCLUDE_DIR}/NvBlastExtAuthoringTypes.h
- ${AUTHORING_EXT_INCLUDE_DIR}/NvBlastExtAuthoring.h
+ ${AUTHORING_EXT_INCLUDE_DIR}/NvBlastExtAuthoring.h
${AUTHORING_EXT_INCLUDE_DIR}/NvBlastExtAuthoringMeshCleaner.h
+ ${AUTHORING_EXT_INCLUDE_DIR}/NvBlastExtAuthoringCutout.h
)
SET(EXT_AUTHORING_FILES
@@ -60,6 +61,8 @@ SET(EXT_AUTHORING_FILES
${AUTHORING_EXT_SOURCE_DIR}/NvBlastExtAuthoring.cpp
${AUTHORING_EXT_SOURCE_DIR}/NvBlastExtAuthoringMeshCleanerImpl.h
${AUTHORING_EXT_SOURCE_DIR}/NvBlastExtAuthoringMeshCleanerImpl.cpp
+ ${AUTHORING_EXT_SOURCE_DIR}/NvBlastExtAuthoringCutoutImpl.h
+ ${AUTHORING_EXT_SOURCE_DIR}/NvBlastExtAuthoringCutoutImpl.cpp
)
SET(VHACD_SOURCE_FILES
diff --git a/sdk/compiler/cmake/modules/FindApexSDK.cmake b/sdk/compiler/cmake/modules/FindApexSDK.cmake
index 9910236..cc45e53 100644
--- a/sdk/compiler/cmake/modules/FindApexSDK.cmake
+++ b/sdk/compiler/cmake/modules/FindApexSDK.cmake
@@ -7,9 +7,10 @@ include(FindPackageHandleStandardArgs)
# Find the includes
-# TODO: Do the version stuff properly!
find_path(APEXSDK_PATH include/Apex.h
PATHS
+ $ENV{PM_Apex_PATH}
+ $ENV{PM_PhysX_PATH}/APEX_1.4 # multipack
${GW_DEPS_ROOT}/$ENV{PM_Apex_NAME}/${ApexSDK_FIND_VERSION}
${GW_DEPS_ROOT}/Apex/${ApexSDK_FIND_VERSION}
)
@@ -33,14 +34,14 @@ if (TARGET_BUILD_PLATFORM STREQUAL "Windows")
# What compiler version do we want?
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 18.0.0.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0.0.0)
- SET(VS_STR "vc12")
+ SET(VS_STR "VS2013")
elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.0.0.0)
- SET(VS_STR "vc14")
+ SET(VS_STR "VS2015")
else()
MESSAGE(FATAL_ERROR "Failed to find compatible PxSharedSDK - Only supporting VS2013 and VS2015")
endif()
- SET(LIB_PATH ${APEXSDK_PATH}/lib/${VS_STR}${APEX_ARCH_FOLDER}-cmake${PHYSX_CRT_SUFFIX})
+ SET(LIB_PATH ${APEXSDK_PATH}/lib/${VS_STR}${APEX_ARCH_FOLDER}-cmake${PHYSX_CRT_SUFFIX} ${APEXSDK_PATH}/lib/${VS_STR}${APEX_ARCH_FOLDER}-NoPhysX ${APEXSDK_PATH}/../Lib/${APEX_ARCH_FOLDER}/${VS_STR} ${APEXSDK_PATH}/../Lib)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".dll")
elseif(TARGET_BUILD_PLATFORM STREQUAL "PS4")
@@ -58,232 +59,232 @@ elseif(TARGET_BUILD_PLATFORM STREQUAL "linux")
endif()
find_library(APEXCLOTHING_LIB
- NAMES APEX_Clothing${APEX_ARCH_FILE}
+ NAMES APEX_Clothing APEX_Clothing${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXDESTRUCTIBLE_LIB
- NAMES APEX_Destructible${APEX_ARCH_FILE}
+ NAMES APEX_Destructible APEX_Destructible${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXLEGACY_LIB
- NAMES APEX_Legacy${APEX_ARCH_FILE}
+ NAMES APEX_Legacy APEX_Legacy${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXLOADER_LIB
- NAMES APEX_Loader${APEX_ARCH_FILE}
+ NAMES APEX_Loader APEX_Loader${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXCOMMON_LIB
- NAMES APEXCommon${APEX_ARCH_FILE}
+ NAMES APEXCommon APEXCommon${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXFRAMEWORK_LIB
- NAMES APEXFramework${APEX_ARCH_FILE}
+ NAMES APEXFramework APEXFramework${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXSHARED_LIB
- NAMES APEXShared${APEX_ARCH_FILE}
+ NAMES APEXShared APEXShared${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXCLOTHING_LIB_DEBUG
- NAMES APEX_ClothingDEBUG${APEX_ARCH_FILE}
+ NAMES APEX_ClothingDEBUG APEX_ClothingDEBUG${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXDESTRUCTIBLE_LIB_DEBUG
- NAMES APEX_DestructibleDEBUG${APEX_ARCH_FILE}
+ NAMES APEX_DestructibleDEBUG APEX_DestructibleDEBUG${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXLEGACY_LIB_DEBUG
- NAMES APEX_LegacyDEBUG${APEX_ARCH_FILE}
+ NAMES APEX_LegacyDEBUG APEX_LegacyDEBUG${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXLOADER_LIB_DEBUG
- NAMES APEX_LoaderDEBUG${APEX_ARCH_FILE}
+ NAMES APEX_LoaderDEBUG APEX_LoaderDEBUG${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXCOMMON_LIB_DEBUG
- NAMES APEXCommonDEBUG${APEX_ARCH_FILE}
+ NAMES APEXCommonDEBUG APEXCommonDEBUG${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXFRAMEWORK_LIB_DEBUG
- NAMES APEXFrameworkDEBUG${APEX_ARCH_FILE}
+ NAMES APEXFrameworkDEBUG APEXFrameworkDEBUG${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXSHARED_LIB_DEBUG
- NAMES APEXSharedDEBUG${APEX_ARCH_FILE}
+ NAMES APEXSharedDEBUG APEXSharedDEBUG${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXCLOTHING_LIB_CHECKED
- NAMES APEX_ClothingCHECKED${APEX_ARCH_FILE}
+ NAMES APEX_ClothingCHECKED APEX_ClothingCHECKED${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXDESTRUCTIBLE_LIB_CHECKED
- NAMES APEX_DestructibleCHECKED${APEX_ARCH_FILE}
+ NAMES APEX_DestructibleCHECKED APEX_DestructibleCHECKED${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXLEGACY_LIB_CHECKED
- NAMES APEX_LegacyCHECKED${APEX_ARCH_FILE}
+ NAMES APEX_LegacyCHECKED APEX_LegacyCHECKED${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXLOADER_LIB_CHECKED
- NAMES APEX_LoaderCHECKED${APEX_ARCH_FILE}
+ NAMES APEX_LoaderCHECKED APEX_LoaderCHECKED${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXCOMMON_LIB_CHECKED
- NAMES APEXCommonCHECKED${APEX_ARCH_FILE}
+ NAMES APEXCommonCHECKED APEXCommonCHECKED${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXFRAMEWORK_LIB_CHECKED
- NAMES APEXFrameworkCHECKED${APEX_ARCH_FILE}
+ NAMES APEXFrameworkCHECKED APEXFrameworkCHECKED${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXSHARED_LIB_CHECKED
- NAMES APEXSharedCHECKED${APEX_ARCH_FILE}
+ NAMES APEXSharedCHECKED APEXSharedCHECKED${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXCLOTHING_LIB_PROFILE
- NAMES APEX_ClothingPROFILE${APEX_ARCH_FILE}
+ NAMES APEX_ClothingPROFILE APEX_ClothingPROFILE${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXDESTRUCTIBLE_LIB_PROFILE
- NAMES APEX_DestructiblePROFILE${APEX_ARCH_FILE}
+ NAMES APEX_DestructiblePROFILE APEX_DestructiblePROFILE${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXLEGACY_LIB_PROFILE
- NAMES APEX_LegacyPROFILE${APEX_ARCH_FILE}
+ NAMES APEX_LegacyPROFILE APEX_LegacyPROFILE${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXLOADER_LIB_PROFILE
- NAMES APEX_LoaderPROFILE${APEX_ARCH_FILE}
+ NAMES APEX_LoaderPROFILE APEX_LoaderPROFILE${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXCOMMON_LIB_PROFILE
- NAMES APEXCommonPROFILE${APEX_ARCH_FILE}
+ NAMES APEXCommonPROFILE APEXCommonPROFILE${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXFRAMEWORK_LIB_PROFILE
- NAMES APEXFrameworkPROFILE${APEX_ARCH_FILE}
+ NAMES APEXFrameworkPROFILE APEXFrameworkPROFILE${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(APEXSHARED_LIB_PROFILE
- NAMES APEXSharedPROFILE${APEX_ARCH_FILE}
+ NAMES APEXSharedPROFILE APEXSharedPROFILE${APEX_ARCH_FILE}
PATHS ${LIB_PATH}
)
if (TARGET_BUILD_PLATFORM STREQUAL "Windows")
- SET(DLL_PATH ${APEXSDK_PATH}/bin/${VS_STR}${APEX_ARCH_FOLDER}-cmake${APEX_CRT_SUFFIX})
+ SET(DLL_PATH ${APEXSDK_PATH}/bin/${VS_STR}${APEX_ARCH_FOLDER}-cmake${APEX_CRT_SUFFIX} ${APEXSDK_PATH}/bin/${VS_STR}${APEX_ARCH_FOLDER}-NoPhysX ${APEXSDK_PATH}/../Bin/${APEX_ARCH_FOLDER}/${VS_STR} ${APEXSDK_PATH}/../Bin)
- find_library(APEXCLOTHING_DLL
- NAMES APEX_Clothing${APEX_ARCH_FILE}
- PATHS ${DLL_PATH}
- )
- find_library(APEXDESTRUCTIBLE_DLL
- NAMES APEX_Destructible${APEX_ARCH_FILE}
- PATHS ${DLL_PATH}
- )
+# find_library(APEXCLOTHING_DLL
+# NAMES APEX_Clothing APEX_Clothing${APEX_ARCH_FILE}
+# PATHS ${DLL_PATH}
+# )
+# find_library(APEXDESTRUCTIBLE_DLL
+# NAMES APEX_Destructible APEX_Destructible${APEX_ARCH_FILE}
+# PATHS ${DLL_PATH}
+# )
find_library(APEXLEGACY_DLL
- NAMES APEX_Legacy${APEX_ARCH_FILE}
+ NAMES APEX_Legacy APEX_Legacy${APEX_ARCH_FILE}
PATHS ${DLL_PATH}
)
find_library(APEXLOADER_DLL
- NAMES APEX_Loader${APEX_ARCH_FILE}
+ NAMES APEX_Loader APEX_Loader${APEX_ARCH_FILE}
PATHS ${DLL_PATH}
)
find_library(APEXFRAMEWORK_DLL
- NAMES APEXFramework${APEX_ARCH_FILE}
+ NAMES APEXFramework APEXFramework${APEX_ARCH_FILE}
PATHS ${DLL_PATH}
)
- find_library(APEXCLOTHING_DLL_DEBUG
- NAMES APEX_ClothingDEBUG${APEX_ARCH_FILE}
- PATHS ${DLL_PATH}
- )
- find_library(APEXDESTRUCTIBLE_DLL_DEBUG
- NAMES APEX_DestructibleDEBUG${APEX_ARCH_FILE}
- PATHS ${DLL_PATH}
- )
+# find_library(APEXCLOTHING_DLL_DEBUG
+# NAMES APEX_ClothingDEBUG APEX_ClothingDEBUG${APEX_ARCH_FILE}
+# PATHS ${DLL_PATH}
+# )
+# find_library(APEXDESTRUCTIBLE_DLL_DEBUG
+# NAMES APEX_DestructibleDEBUG APEX_DestructibleDEBUG${APEX_ARCH_FILE}
+# PATHS ${DLL_PATH}
+# )
find_library(APEXLEGACY_DLL_DEBUG
- NAMES APEX_LegacyDEBUG${APEX_ARCH_FILE}
+ NAMES APEX_LegacyDEBUG APEX_LegacyDEBUG${APEX_ARCH_FILE}
PATHS ${DLL_PATH}
)
find_library(APEXLOADER_DLL_DEBUG
- NAMES APEX_LoaderDEBUG${APEX_ARCH_FILE}
+ NAMES APEX_LoaderDEBUG APEX_LoaderDEBUG${APEX_ARCH_FILE}
PATHS ${DLL_PATH}
)
find_library(APEXFRAMEWORK_DLL_DEBUG
- NAMES APEXFrameworkDEBUG${APEX_ARCH_FILE}
+ NAMES APEXFrameworkDEBUG APEXFrameworkDEBUG${APEX_ARCH_FILE}
PATHS ${DLL_PATH}
)
- find_library(APEXCLOTHING_DLL_CHECKED
- NAMES APEX_ClothingCHECKED${APEX_ARCH_FILE}
- PATHS ${DLL_PATH}
- )
- find_library(APEXDESTRUCTIBLE_DLL_CHECKED
- NAMES APEX_DestructibleCHECKED${APEX_ARCH_FILE}
- PATHS ${DLL_PATH}
- )
+# find_library(APEXCLOTHING_DLL_CHECKED
+# NAMES APEX_ClothingCHECKED APEX_ClothingCHECKED${APEX_ARCH_FILE}
+# PATHS ${DLL_PATH}
+# )
+# find_library(APEXDESTRUCTIBLE_DLL_CHECKED
+# NAMES APEX_DestructibleCHECKED APEX_DestructibleCHECKED${APEX_ARCH_FILE}
+# PATHS ${DLL_PATH}
+# )
find_library(APEXLEGACY_DLL_CHECKED
- NAMES APEX_LegacyCHECKED${APEX_ARCH_FILE}
+ NAMES APEX_LegacyCHECKED APEX_LegacyCHECKED${APEX_ARCH_FILE}
PATHS ${DLL_PATH}
)
find_library(APEXLOADER_DLL_CHECKED
- NAMES APEX_LoaderCHECKED${APEX_ARCH_FILE}
+ NAMES APEX_LoaderCHECKED APEX_LoaderCHECKED${APEX_ARCH_FILE}
PATHS ${DLL_PATH}
)
find_library(APEXFRAMEWORK_DLL_CHECKED
- NAMES APEXFrameworkCHECKED${APEX_ARCH_FILE}
+ NAMES APEXFrameworkCHECKED APEXFrameworkCHECKED${APEX_ARCH_FILE}
PATHS ${DLL_PATH}
)
- find_library(APEXCLOTHING_DLL_PROFILE
- NAMES APEX_ClothingPROFILE${APEX_ARCH_FILE}
- PATHS ${DLL_PATH}
- )
- find_library(APEXDESTRUCTIBLE_DLL_PROFILE
- NAMES APEX_DestructiblePROFILE${APEX_ARCH_FILE}
- PATHS ${DLL_PATH}
- )
+# find_library(APEXCLOTHING_DLL_PROFILE
+# NAMES APEX_ClothingPROFILE APEX_ClothingPROFILE${APEX_ARCH_FILE}
+# PATHS ${DLL_PATH}
+# )
+# find_library(APEXDESTRUCTIBLE_DLL_PROFILE
+# NAMES APEX_DestructiblePROFILE APEX_DestructiblePROFILE${APEX_ARCH_FILE}
+# PATHS ${DLL_PATH}
+# )
find_library(APEXLEGACY_DLL_PROFILE
- NAMES APEX_LegacyPROFILE${APEX_ARCH_FILE}
+ NAMES APEX_LegacyPROFILE APEX_LegacyPROFILE${APEX_ARCH_FILE}
PATHS ${DLL_PATH}
)
find_library(APEXLOADER_DLL_PROFILE
- NAMES APEX_LoaderPROFILE${APEX_ARCH_FILE}
+ NAMES APEX_LoaderPROFILE APEX_LoaderPROFILE${APEX_ARCH_FILE}
PATHS ${DLL_PATH}
)
find_library(APEXFRAMEWORK_DLL_PROFILE
- NAMES APEXFrameworkPROFILE${APEX_ARCH_FILE}
+ NAMES APEXFrameworkPROFILE APEXFrameworkPROFILE${APEX_ARCH_FILE}
PATHS ${DLL_PATH}
)
SET(DLL_VAR_LIST
- APEXCLOTHING_DLL
- APEXDESTRUCTIBLE_DLL
+ #APEXCLOTHING_DLL
+ #APEXDESTRUCTIBLE_DLL
APEXLEGACY_DLL
APEXLOADER_DLL
APEXFRAMEWORK_DLL
- APEXCLOTHING_DLL_DEBUG
- APEXDESTRUCTIBLE_DLL_DEBUG
+ #APEXCLOTHING_DLL_DEBUG
+ #APEXDESTRUCTIBLE_DLL_DEBUG
APEXLEGACY_DLL_DEBUG
APEXLOADER_DLL_DEBUG
APEXFRAMEWORK_DLL_DEBUG
- APEXCLOTHING_DLL_CHECKED
- APEXDESTRUCTIBLE_DLL_CHECKED
+ #APEXCLOTHING_DLL_CHECKED
+ #APEXDESTRUCTIBLE_DLL_CHECKED
APEXLEGACY_DLL_CHECKED
APEXLOADER_DLL_CHECKED
APEXFRAMEWORK_DLL_CHECKED
- APEXCLOTHING_DLL_PROFILE
- APEXDESTRUCTIBLE_DLL_PROFILE
+ #APEXCLOTHING_DLL_PROFILE
+ #APEXDESTRUCTIBLE_DLL_PROFILE
APEXLEGACY_DLL_PROFILE
APEXLOADER_DLL_PROFILE
APEXFRAMEWORK_DLL_PROFILE
@@ -295,32 +296,32 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(APEXSDK
DEFAULT_MSG
APEXSDK_PATH
- APEXCLOTHING_LIB
- APEXDESTRUCTIBLE_LIB
+ #APEXCLOTHING_LIB
+ #APEXDESTRUCTIBLE_LIB
APEXLEGACY_LIB
APEXLOADER_LIB
APEXCOMMON_LIB
APEXFRAMEWORK_LIB
APEXSHARED_LIB
- APEXCLOTHING_LIB_DEBUG
- APEXDESTRUCTIBLE_LIB_DEBUG
+ #APEXCLOTHING_LIB_DEBUG
+ #APEXDESTRUCTIBLE_LIB_DEBUG
APEXLEGACY_LIB_DEBUG
APEXLOADER_LIB_DEBUG
APEXCOMMON_LIB_DEBUG
APEXFRAMEWORK_LIB_DEBUG
APEXSHARED_LIB_DEBUG
- APEXCLOTHING_LIB_CHECKED
- APEXDESTRUCTIBLE_LIB_CHECKED
+ #APEXCLOTHING_LIB_CHECKED
+ #APEXDESTRUCTIBLE_LIB_CHECKED
APEXLEGACY_LIB_CHECKED
APEXLOADER_LIB_CHECKED
APEXCOMMON_LIB_CHECKED
APEXFRAMEWORK_LIB_CHECKED
APEXSHARED_LIB_CHECKED
- APEXCLOTHING_LIB_PROFILE
- APEXDESTRUCTIBLE_LIB_PROFILE
+ #APEXCLOTHING_LIB_PROFILE
+ #APEXDESTRUCTIBLE_LIB_PROFILE
APEXLEGACY_LIB_PROFILE
APEXLOADER_LIB_PROFILE
APEXCOMMON_LIB_PROFILE
@@ -347,41 +348,41 @@ if (APEXSDK_FOUND)
${APEXSDK_PATH}/shared/general/RenderDebug/public
)
- SET(APEXSDK_LIBS_RELEASE ${APEXCLOTHING_LIB} ${APEXDESTRUCTIBLE_LIB} ${APEXLEGACY_LIB} ${APEXLOADER_LIB} ${APEXCOMMON_LIB} ${APEXFRAMEWORK_LIB} ${APEXSHARED_LIB}
+ SET(APEXSDK_LIBS_RELEASE ${APEXLEGACY_LIB} ${APEXLOADER_LIB} ${APEXCOMMON_LIB} ${APEXFRAMEWORK_LIB} ${APEXSHARED_LIB}
CACHE STRING ""
)
- SET(APEXSDK_LIBS_DEBUG ${APEXCLOTHING_LIB_DEBUG} ${APEXDESTRUCTIBLE_LIB_DEBUG} ${APEXLEGACY_LIB_DEBUG} ${APEXLOADER_LIB_DEBUG} ${APEXCOMMON_LIB_DEBUG} ${APEXFRAMEWORK_LIB_DEBUG} ${APEXSHARED_LIB_DEBUG}
+ SET(APEXSDK_LIBS_DEBUG ${APEXLEGACY_LIB_DEBUG} ${APEXLOADER_LIB_DEBUG} ${APEXCOMMON_LIB_DEBUG} ${APEXFRAMEWORK_LIB_DEBUG} ${APEXSHARED_LIB_DEBUG}
CACHE STRING ""
)
- SET(APEXSDK_LIBS_CHECKED ${APEXCLOTHING_LIB_CHECKED} ${APEXDESTRUCTIBLE_LIB_CHECKED} ${APEXLEGACY_LIB_CHECKED} ${APEXLOADER_LIB_CHECKED} ${APEXCOMMON_LIB_CHECKED} ${APEXFRAMEWORK_LIB_CHECKED} ${APEXSHARED_LIB_CHECKED}
+ SET(APEXSDK_LIBS_CHECKED ${APEXLEGACY_LIB_CHECKED} ${APEXLOADER_LIB_CHECKED} ${APEXCOMMON_LIB_CHECKED} ${APEXFRAMEWORK_LIB_CHECKED} ${APEXSHARED_LIB_CHECKED}
CACHE STRING ""
)
- SET(APEXSDK_LIBS_PROFILE ${APEXCLOTHING_LIB_PROFILE} ${APEXDESTRUCTIBLE_LIB_PROFILE} ${APEXLEGACY_LIB_PROFILE} ${APEXLOADER_LIB_PROFILE} ${APEXCOMMON_LIB_PROFILE} ${APEXFRAMEWORK_LIB_PROFILE} ${APEXSHARED_LIB_PROFILE}
+ SET(APEXSDK_LIBS_PROFILE ${APEXLEGACY_LIB_PROFILE} ${APEXLOADER_LIB_PROFILE} ${APEXCOMMON_LIB_PROFILE} ${APEXFRAMEWORK_LIB_PROFILE} ${APEXSHARED_LIB_PROFILE}
CACHE STRING ""
)
SET(APEXSDK_DLLS
- ${APEXCLOTHING_DLL}
- ${APEXDESTRUCTIBLE_DLL}
+ #${APEXCLOTHING_DLL}
+ #${APEXDESTRUCTIBLE_DLL}
${APEXLEGACY_DLL}
${APEXLOADER_DLL}
${APEXFRAMEWORK_DLL}
- ${APEXCLOTHING_DLL_DEBUG}
- ${APEXDESTRUCTIBLE_DLL_DEBUG}
+ #${APEXCLOTHING_DLL_DEBUG}
+ #${APEXDESTRUCTIBLE_DLL_DEBUG}
${APEXLEGACY_DLL_DEBUG}
${APEXLOADER_DLL_DEBUG}
${APEXFRAMEWORK_DLL_DEBUG}
- ${APEXCLOTHING_DLL_CHECKED}
- ${APEXDESTRUCTIBLE_DLL_CHECKED}
+ #${APEXCLOTHING_DLL_CHECKED}
+ #${APEXDESTRUCTIBLE_DLL_CHECKED}
${APEXLEGACY_DLL_CHECKED}
${APEXLOADER_DLL_CHECKED}
${APEXFRAMEWORK_DLL_CHECKED}
- ${APEXCLOTHING_DLL_PROFILE}
- ${APEXDESTRUCTIBLE_DLL_PROFILE}
+ #${APEXCLOTHING_DLL_PROFILE}
+ #${APEXDESTRUCTIBLE_DLL_PROFILE}
${APEXLEGACY_DLL_PROFILE}
${APEXLOADER_DLL_PROFILE}
${APEXFRAMEWORK_DLL_PROFILE}
diff --git a/sdk/compiler/cmake/modules/FindBoostMultiprecision.cmake b/sdk/compiler/cmake/modules/FindBoostMultiprecision.cmake
index 84373ab..cea6142 100644
--- a/sdk/compiler/cmake/modules/FindBoostMultiprecision.cmake
+++ b/sdk/compiler/cmake/modules/FindBoostMultiprecision.cmake
@@ -8,6 +8,7 @@ INCLUDE(FindPackageHandleStandardArgs)
#TODO: Proper version support
FIND_PATH( BOOSTMULTIPRECISION_PATH boost/multiprecision
PATHS
+ $ENV{PM_BoostMultiprecision_PATH}
${GW_DEPS_ROOT}/BoostMultiprecision/${BoostMultiprecision_FIND_VERSION}
)
diff --git a/sdk/compiler/cmake/modules/FindCapnProtoSDK.cmake b/sdk/compiler/cmake/modules/FindCapnProtoSDK.cmake
index 9e2cd60..1813e87 100644
--- a/sdk/compiler/cmake/modules/FindCapnProtoSDK.cmake
+++ b/sdk/compiler/cmake/modules/FindCapnProtoSDK.cmake
@@ -9,7 +9,9 @@ include(FindPackageHandleStandardArgs)
# TODO: Do the version stuff properly!
find_path(CAPNPROTOSDK_PATH src/capnp/message.h
- PATHS ${GW_DEPS_ROOT}/$ENV{PM_CapnProto_NAME}/${CapnProtoSDK_FIND_VERSION}
+ PATHS
+ $ENV{PM_CapnProto_PATH}
+ ${GW_DEPS_ROOT}/$ENV{PM_CapnProto_NAME}/${CapnProtoSDK_FIND_VERSION}
)
if (TARGET_BUILD_PLATFORM STREQUAL "Windows")
diff --git a/sdk/compiler/cmake/modules/FindFBXSDK.cmake b/sdk/compiler/cmake/modules/FindFBXSDK.cmake
index 7e1bfb4..5b6b0a4 100644
--- a/sdk/compiler/cmake/modules/FindFBXSDK.cmake
+++ b/sdk/compiler/cmake/modules/FindFBXSDK.cmake
@@ -10,7 +10,9 @@ include(FindPackageHandleStandardArgs)
# TODO: Do the version stuff properly!
find_path(FBXSDK_PATH include/fbxsdk.h
- PATHS ${GW_DEPS_ROOT}/FBXSDK/${FBXSDK_FIND_VERSION}
+ PATHS
+ $ENV{PM_FBXSDK_PATH}
+ ${GW_DEPS_ROOT}/FBXSDK/${FBXSDK_FIND_VERSION}
)
if (STATIC_WINCRT)
diff --git a/sdk/compiler/cmake/modules/FindGoogleTestNV.cmake b/sdk/compiler/cmake/modules/FindGoogleTestNV.cmake
index 50e7edd..7f2dde4 100644
--- a/sdk/compiler/cmake/modules/FindGoogleTestNV.cmake
+++ b/sdk/compiler/cmake/modules/FindGoogleTestNV.cmake
@@ -8,7 +8,9 @@ include(FindPackageHandleStandardArgs)
# TODO: Do the version stuff properly!
find_path(GOOGLETEST_PATH include/gtest/gtest.h
- PATHS ${GW_DEPS_ROOT}/$ENV{PM_googletest_NAME}/${GoogleTestNV_FIND_VERSION}
+ PATHS
+ $ENV{PM_googletest_PATH}
+ ${GW_DEPS_ROOT}/$ENV{PM_googletest_NAME}/${GoogleTestNV_FIND_VERSION}
)
if (TARGET_BUILD_PLATFORM STREQUAL "Windows")
diff --git a/sdk/compiler/cmake/modules/FindPhysXSDK.cmake b/sdk/compiler/cmake/modules/FindPhysXSDK.cmake
index 4b9176f..0194edb 100644
--- a/sdk/compiler/cmake/modules/FindPhysXSDK.cmake
+++ b/sdk/compiler/cmake/modules/FindPhysXSDK.cmake
@@ -6,9 +6,10 @@
include(FindPackageHandleStandardArgs)
MESSAGE("Looking for PhysXSDK ${PhysXSDK_FIND_VERSION} Cached path: ${PHYSXSDK_PATH}")
-# TODO: Do the version stuff properly!
find_path(PHYSXSDK_PATH Include/PxActor.h
PATHS
+ $ENV{PM_PhysX_PATH}
+ $ENV{PM_PhysX_PATH}/PhysX_3.4
${GW_DEPS_ROOT}/$ENV{PM_PhysX_NAME}/${PhysXSDK_FIND_VERSION}
${GW_DEPS_ROOT}/PhysX/${PhysXSDK_FIND_VERSION}
)
@@ -32,26 +33,26 @@ if (TARGET_BUILD_PLATFORM STREQUAL "Windows")
# What compiler version do we want?
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 18.0.0.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0.0.0)
- SET(VS_STR "vc12")
+ SET(VS_STR "VS2013")
elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.0.0.0)
- SET(VS_STR "vc14")
+ SET(VS_STR "VS2015")
else()
MESSAGE(FATAL_ERROR "Failed to find compatible PxSharedSDK - Only supporting VS2013 and VS2015")
endif()
- SET(LIB_PATH ${PHYSXSDK_PATH}/lib/${VS_STR}${PHYSX_ARCH_FOLDER}-cmake${PHYSX_CRT_SUFFIX})
+ SET(LIB_PATH ${PHYSXSDK_PATH}/lib/${VS_STR}${PHYSX_ARCH_FOLDER}-cmake${PHYSX_CRT_SUFFIX} ${PHYSXSDK_PATH}/lib/${VS_STR}${PHYSX_ARCH_FOLDER} ${PHYSXSDK_PATH}/../Lib/${PHYSX_ARCH_FOLDER}/${VS_STR} ${PHYSXSDK_PATH}/../Lib)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".dll")
elseif(TARGET_BUILD_PLATFORM STREQUAL "PS4")
- SET(LIB_PATH ${PHYSXSDK_PATH}/lib/vc14ps4-cmake)
+ SET(LIB_PATH ${PHYSXSDK_PATH}/lib/vc14ps4-cmake ${PHYSXSDK_PATH}/../Lib)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
SET(CMAKE_FIND_LIBRARY_PREFIXES "lib")
elseif(TARGET_BUILD_PLATFORM STREQUAL "XboxOne")
- SET(LIB_PATH ${PHYSXSDK_PATH}/lib/vc14xboxone-cmake)
- SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
- SET(CMAKE_FIND_LIBRARY_PREFIXES "lib")
+ SET(LIB_PATH ${PHYSXSDK_PATH}/lib/vc14xboxone-cmake ${PHYSXSDK_PATH}/../lib)
+ SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".lib")
+ SET(CMAKE_FIND_LIBRARY_PREFIXES "lib" "")
elseif(TARGET_BUILD_PLATFORM STREQUAL "linux")
- SET(LIB_PATH ${PHYSXSDK_PATH}/lib/linux64-cmake)
+ SET(LIB_PATH ${PHYSXSDK_PATH}/lib/linux64-cmake ${PHYSXSDK_PATH}/../Lib)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
SET(CMAKE_FIND_LIBRARY_PREFIXES "lib")
SET(PHYSX_ARCH_FILE "_x64")
@@ -60,11 +61,11 @@ endif()
# Now find all of the PhysX libs in the lib directory
find_library(PHYSX3_LIB
- NAMES PhysX3${PHYSX_ARCH_FILE}
+ NAMES PhysX3 PhysX3${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PHYSX3EXTENSIONS_LIB
- NAMES PhysX3Extensions${PHYSX_ARCH_FILE}
+ NAMES PhysX3Extensions PhysX3Extensions${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PHYSX3CHARACTERKINEMATIC_LIB
@@ -80,130 +81,130 @@ find_library(PHYSX3COOKING_LIB
PATHS ${LIB_PATH}
)
find_library(LOWLEVEL_LIB
- NAMES LowLevel${PHYSX_ARCH_FILE}
+ NAMES LowLevel LowLevel${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVELAABB_LIB
- NAMES LowLevelAABB${PHYSX_ARCH_FILE}
+ NAMES LowLevelAABB LowLevelAABB${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVELCLOTH_LIB
- NAMES LowLevelCloth${PHYSX_ARCH_FILE}
+ NAMES LowLevelCloth LowLevelCloth${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVELDYNAMICS_LIB
- NAMES LowLevelDynamics${PHYSX_ARCH_FILE}
+ NAMES LowLevelDynamics LowLevelDynamics${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVELPARTICLES_LIB
- NAMES LowLevelParticles${PHYSX_ARCH_FILE}
+ NAMES LowLevelParticles LowLevelParticles${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(SCENEQUERY_LIB
- NAMES SceneQuery${PHYSX_ARCH_FILE}
+ NAMES SceneQuery SceneQuery${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(SIMULATIONCONTROLLER_LIB
- NAMES SimulationController${PHYSX_ARCH_FILE}
+ NAMES SimulationController SimulationController${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PHYSX3_LIB_DEBUG
- NAMES PhysX3DEBUG${PHYSX_ARCH_FILE}
+ NAMES PhysX3DEBUG PhysX3DEBUG${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PHYSX3EXTENSIONS_LIB_DEBUG
- NAMES PhysX3ExtensionsDEBUG${PHYSX_ARCH_FILE}
+ NAMES PhysX3ExtensionsDEBUG PhysX3ExtensionsDEBUG${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PHYSX3CHARACTERKINEMATIC_LIB_DEBUG
- NAMES PhysX3CharacterKinematicDEBUG${PHYSX_ARCH_FILE}
+ NAMES PhysX3CharacterKinematicDEBUG PhysX3CharacterKinematicDEBUG${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PHYSX3COMMON_LIB_DEBUG
- NAMES PhysX3CommonDEBUG${PHYSX_ARCH_FILE}
+ NAMES PhysX3CommonDEBUG PhysX3CommonDEBUG${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PHYSX3COOKING_LIB_DEBUG
- NAMES PhysX3CookingDEBUG${PHYSX_ARCH_FILE}
+ NAMES PhysX3CookingDEBUG PhysX3CookingDEBUG${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVEL_LIB_DEBUG
- NAMES LowLevelDEBUG${PHYSX_ARCH_FILE}
+ NAMES LowLevelDEBUG LowLevelDEBUG${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVELAABB_LIB_DEBUG
- NAMES LowLevelAABBDEBUG${PHYSX_ARCH_FILE}
+ NAMES LowLevelAABBDEBUG LowLevelAABBDEBUG${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVELCLOTH_LIB_DEBUG
- NAMES LowLevelClothDEBUG${PHYSX_ARCH_FILE}
+ NAMES LowLevelClothDEBUG LowLevelClothDEBUG${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVELDYNAMICS_LIB_DEBUG
- NAMES LowLevelDynamicsDEBUG${PHYSX_ARCH_FILE}
+ NAMES LowLevelDynamicsDEBUG LowLevelDynamicsDEBUG${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVELPARTICLES_LIB_DEBUG
- NAMES LowLevelParticlesDEBUG${PHYSX_ARCH_FILE}
+ NAMES LowLevelParticlesDEBUG LowLevelParticlesDEBUG${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(SCENEQUERY_LIB_DEBUG
- NAMES SceneQueryDEBUG${PHYSX_ARCH_FILE}
+ NAMES SceneQueryDEBUG SceneQueryDEBUG${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(SIMULATIONCONTROLLER_LIB_DEBUG
- NAMES SimulationControllerDEBUG${PHYSX_ARCH_FILE}
+ NAMES SimulationControllerDEBUG SimulationControllerDEBUG${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PHYSX3_LIB_CHECKED
- NAMES PhysX3CHECKED${PHYSX_ARCH_FILE}
+ NAMES PhysX3CHECKED PhysX3CHECKED${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PHYSX3EXTENSIONS_LIB_CHECKED
- NAMES PhysX3ExtensionsCHECKED${PHYSX_ARCH_FILE}
+ NAMES PhysX3ExtensionsCHECKED PhysX3ExtensionsCHECKED${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PHYSX3CHARACTERKINEMATIC_LIB_CHECKED
- NAMES PhysX3CharacterKinematicCHECKED${PHYSX_ARCH_FILE}
+ NAMES PhysX3CharacterKinematicCHECKED PhysX3CharacterKinematicCHECKED${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PHYSX3COMMON_LIB_CHECKED
- NAMES PhysX3CommonCHECKED${PHYSX_ARCH_FILE}
+ NAMES PhysX3CommonCHECKED PhysX3CommonCHECKED${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PHYSX3COOKING_LIB_CHECKED
- NAMES PhysX3CookingCHECKED${PHYSX_ARCH_FILE}
+ NAMES PhysX3CookingCHECKED PhysX3CookingCHECKED${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVEL_LIB_CHECKED
- NAMES LowLevelCHECKED${PHYSX_ARCH_FILE}
+ NAMES LowLevelCHECKED LowLevelCHECKED${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVELAABB_LIB_CHECKED
- NAMES LowLevelAABBCHECKED${PHYSX_ARCH_FILE}
+ NAMES LowLevelAABBCHECKED LowLevelAABBCHECKED${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVELCLOTH_LIB_CHECKED
- NAMES LowLevelClothCHECKED${PHYSX_ARCH_FILE}
+ NAMES LowLevelClothCHECKED LowLevelClothCHECKED${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVELDYNAMICS_LIB_CHECKED
- NAMES LowLevelDynamicsCHECKED${PHYSX_ARCH_FILE}
+ NAMES LowLevelDynamicsCHECKED LowLevelDynamicsCHECKED${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(LOWLEVELPARTICLES_LIB_CHECKED
- NAMES LowLevelParticlesCHECKED${PHYSX_ARCH_FILE}
+ NAMES LowLevelParticlesCHECKED LowLevelParticlesCHECKED${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(SCENEQUERY_LIB_CHECKED
- NAMES SceneQueryCHECKED${PHYSX_ARCH_FILE}
+ NAMES SceneQueryCHECKED SceneQueryCHECKED${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(SIMULATIONCONTROLLER_LIB_CHECKED
- NAMES SimulationControllerCHECKED${PHYSX_ARCH_FILE}
+ NAMES SimulationControllerCHECKED SimulationControllerCHECKED${PHYSX_ARCH_FILE}
PATHS ${LIB_PATH}
)
@@ -268,16 +269,12 @@ if (TARGET_BUILD_PLATFORM STREQUAL "Windows")
)
- SET(DLL_PATH ${PHYSXSDK_PATH}/bin/${VS_STR}${PHYSX_ARCH_FOLDER}-cmake${PHYSX_CRT_SUFFIX})
+ SET(DLL_PATH ${PHYSXSDK_PATH}/bin/${VS_STR}${PHYSX_ARCH_FOLDER}-cmake${PHYSX_CRT_SUFFIX} ${PHYSXSDK_PATH}/bin/${VS_STR}${PHYSX_ARCH_FOLDER} ${PHYSXSDK_PATH}/../Bin/${PHYSX_ARCH_FOLDER}/${VS_STR} ${PHYSXSDK_PATH}/../Bin)
find_library(PHYSX3_DLL
NAMES PhysX3${PHYSX_ARCH_FILE}
PATHS ${DLL_PATH}
)
- find_library(PHYSX3CHARACTERKINEMATIC_DLL
- NAMES PhysX3CharacterKinematic${PHYSX_ARCH_FILE}
- PATHS ${DLL_PATH}
- )
find_library(PHYSX3COMMON_DLL
NAMES PhysX3Common${PHYSX_ARCH_FILE}
PATHS ${DLL_PATH}
@@ -296,10 +293,6 @@ if (TARGET_BUILD_PLATFORM STREQUAL "Windows")
NAMES PhysX3DEBUG${PHYSX_ARCH_FILE}
PATHS ${DLL_PATH}
)
- find_library(PHYSX3CHARACTERKINEMATIC_DLL_DEBUG
- NAMES PhysX3CharacterKinematicDEBUG${PHYSX_ARCH_FILE}
- PATHS ${DLL_PATH}
- )
find_library(PHYSX3COMMON_DLL_DEBUG
NAMES PhysX3CommonDEBUG${PHYSX_ARCH_FILE}
PATHS ${DLL_PATH}
@@ -318,10 +311,6 @@ if (TARGET_BUILD_PLATFORM STREQUAL "Windows")
NAMES PhysX3PROFILE${PHYSX_ARCH_FILE}
PATHS ${DLL_PATH}
)
- find_library(PHYSX3CHARACTERKINEMATIC_DLL_PROFILE
- NAMES PhysX3CharacterKinematicPROFILE${PHYSX_ARCH_FILE}
- PATHS ${DLL_PATH}
- )
find_library(PHYSX3COMMON_DLL_PROFILE
NAMES PhysX3CommonPROFILE${PHYSX_ARCH_FILE}
PATHS ${DLL_PATH}
@@ -340,10 +329,6 @@ if (TARGET_BUILD_PLATFORM STREQUAL "Windows")
NAMES PhysX3CHECKED${PHYSX_ARCH_FILE}
PATHS ${DLL_PATH}
)
- find_library(PHYSX3CHARACTERKINEMATIC_DLL_CHECKED
- NAMES PhysX3CharacterKinematicCHECKED${PHYSX_ARCH_FILE}
- PATHS ${DLL_PATH}
- )
find_library(PHYSX3COMMON_DLL_CHECKED
NAMES PhysX3CommonCHECKED${PHYSX_ARCH_FILE}
PATHS ${DLL_PATH}
@@ -361,30 +346,26 @@ if (TARGET_BUILD_PLATFORM STREQUAL "Windows")
# Create this list to check for found dlls below
SET(DLL_VAR_LIST
PHYSX3_DLL
- PHYSX3CHARACTERKINEMATIC_DLL
- PHYSX3COMMON_DLL
+ PHYSX3COMMON_DLL
PHYSX3COOKING_DLL
PHYSX3_DLL_DEBUG
- PHYSX3CHARACTERKINEMATIC_DLL_DEBUG
- PHYSX3COMMON_DLL_DEBUG
+ PHYSX3COMMON_DLL_DEBUG
PHYSX3COOKING_DLL_DEBUG
PHYSX3_DLL_PROFILE
- PHYSX3CHARACTERKINEMATIC_DLL_PROFILE
- PHYSX3COMMON_DLL_PROFILE
+ PHYSX3COMMON_DLL_PROFILE
PHYSX3COOKING_DLL_PROFILE
PHYSX3_DLL_CHECKED
- PHYSX3CHARACTERKINEMATIC_DLL_CHECKED
- PHYSX3COMMON_DLL_CHECKED
+ PHYSX3COMMON_DLL_CHECKED
PHYSX3COOKING_DLL_CHECKED
)
endif()
if (TARGET_BUILD_PLATFORM STREQUAL "linux")
- SET(BIN_PATH ${PHYSXSDK_PATH}/bin/linux64-cmake)
+ SET(BIN_PATH ${PHYSXSDK_PATH}/bin/linux64-cmake ${PHYSXSDK_PATH}/../Bin)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".so")
find_library(PHYSX3_LIB
diff --git a/sdk/compiler/cmake/modules/FindPxSharedSDK.cmake b/sdk/compiler/cmake/modules/FindPxSharedSDK.cmake
index 0f67f5c..ff18af3 100644
--- a/sdk/compiler/cmake/modules/FindPxSharedSDK.cmake
+++ b/sdk/compiler/cmake/modules/FindPxSharedSDK.cmake
@@ -7,9 +7,11 @@ include(FindPackageHandleStandardArgs)
# Find the includes
-# TODO: Do the version stuff properly!
+# Always try explicit PATH variable first
find_path(PXSHAREDSDK_PATH include/foundation/Px.h
PATHS
+ $ENV{PM_PxShared_PATH}
+ $ENV{PM_PhysX_PATH}/PxShared # multipack
${GW_DEPS_ROOT}/$ENV{PM_PxShared_NAME}/${PxSharedSDK_FIND_VERSION}
${GW_DEPS_ROOT}/PxShared/${PxSharedSDK_FIND_VERSION}
)
@@ -33,37 +35,37 @@ if (TARGET_BUILD_PLATFORM STREQUAL "Windows")
# What compiler version do we want?
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 18.0.0.0 AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0.0.0)
- SET(VS_STR "vc12")
+ SET(VS_STR "VS2013")
elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.0.0.0)
- SET(VS_STR "vc14")
+ SET(VS_STR "VS2015")
else()
MESSAGE(FATAL_ERROR "Failed to find compatible PxSharedSDK - Only supporting VS2013 and VS2015")
endif()
- SET(LIB_PATH ${PXSHAREDSDK_PATH}/lib/${VS_STR}${PXSHARED_ARCH_FOLDER}-cmake${PXSHARED_CRT_SUFFIX})
+ SET(LIB_PATH ${PXSHAREDSDK_PATH}/lib/${VS_STR}${PXSHARED_ARCH_FOLDER}-cmake${PXSHARED_CRT_SUFFIX} ${PXSHAREDSDK_PATH}/lib/${VS_STR}${PXSHARED_ARCH_FOLDER} ${PXSHAREDSDK_PATH}/../lib/${PXSHARED_ARCH_FOLDER}/${VS_STR} ${PXSHAREDSDK_PATH}/../lib)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".dll")
elseif(TARGET_BUILD_PLATFORM STREQUAL "PS4")
- SET(LIB_PATH ${PXSHAREDSDK_PATH}/lib/vc14ps4-cmake)
+ SET(LIB_PATH ${PXSHAREDSDK_PATH}/lib/vc14ps4-cmake ${PXSHAREDSDK_PATH}/../lib)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
SET(CMAKE_FIND_LIBRARY_PREFIXES "lib")
elseif(TARGET_BUILD_PLATFORM STREQUAL "XboxOne")
- SET(LIB_PATH ${PXSHAREDSDK_PATH}/lib/vc14xboxone-cmake)
- SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
- SET(CMAKE_FIND_LIBRARY_PREFIXES "lib")
+ SET(LIB_PATH ${PXSHAREDSDK_PATH}/lib/vc14xboxone-cmake ${PXSHAREDSDK_PATH}/../Lib)
+ SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".lib")
+ SET(CMAKE_FIND_LIBRARY_PREFIXES "lib" "")
elseif(TARGET_BUILD_PLATFORM STREQUAL "linux")
- SET(LIB_PATH ${PXSHAREDSDK_PATH}/lib/linux64-cmake)
+ SET(LIB_PATH ${PXSHAREDSDK_PATH}/lib/linux64-cmake ${PXSHAREDSDK_PATH}/../Lib)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
SET(CMAKE_FIND_LIBRARY_PREFIXES "lib")
SET(PXSHARED_ARCH_FILE "_x64")
endif()
-# Now find all of the PhysX libs in the lib directory
+# Now find all of the PxShared libs in the lib directory
find_library(PSFASTXML_LIB
- NAMES PsFastXml${PXSHARED_ARCH_FILE}
+ NAMES PsFastXml PsFastXml${PXSHARED_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PXFOUNDATION_LIB
@@ -75,12 +77,12 @@ find_library(PXPVDSDK_LIB
PATHS ${LIB_PATH}
)
find_library(PXTASK_LIB
- NAMES PxTask${PXSHARED_ARCH_FILE}
+ NAMES PxTask PxTask${PXSHARED_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PSFASTXML_LIB_DEBUG
- NAMES PsFastXmlDEBUG${PXSHARED_ARCH_FILE}
+ NAMES PsFastXmlDEBUG PsFastXmlDEBUG${PXSHARED_ARCH_FILE}
PATHS ${LIB_PATH}
)
find_library(PXFOUNDATION_LIB_DEBUG
@@ -92,7 +94,7 @@ find_library(PXPVDSDK_LIB_DEBUG
PATHS ${LIB_PATH}
)
find_library(PXTASK_LIB_DEBUG
- NAMES PxTaskDEBUG${PXSHARED_ARCH_FILE}
+ NAMES PxTaskDEBUG PxTaskDEBUG${PXSHARED_ARCH_FILE}
PATHS ${LIB_PATH}
)
@@ -131,7 +133,7 @@ find_library(PXTASK_LIB_PROFILE
)
if (TARGET_BUILD_PLATFORM STREQUAL "Windows")
- SET(DLL_PATH ${PXSHAREDSDK_PATH}/bin/${VS_STR}${PXSHARED_ARCH_FOLDER}-cmake${PXSHARED_CRT_SUFFIX})
+ SET(DLL_PATH ${PXSHAREDSDK_PATH}/bin/${VS_STR}${PXSHARED_ARCH_FOLDER}-cmake${PXSHARED_CRT_SUFFIX} ${PXSHAREDSDK_PATH}/bin/${VS_STR}${PXSHARED_ARCH_FOLDER} ${PXSHAREDSDK_PATH}/../bin/${PXSHARED_ARCH_FOLDER}/${VS_STR} ${PXSHAREDSDK_PATH}/../bin)
find_library(PXFOUNDATION_DLL
NAMES PxFoundation${PXSHARED_ARCH_FILE}
@@ -187,7 +189,7 @@ if (TARGET_BUILD_PLATFORM STREQUAL "Windows")
endif()
if (TARGET_BUILD_PLATFORM STREQUAL "linux")
- SET(BIN_PATH ${PXSHAREDSDK_PATH}/bin/linux64-cmake)
+ SET(BIN_PATH ${PXSHAREDSDK_PATH}/bin/linux64-cmake ${PXSHAREDSDK_PATH}/../Bin)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".so")
find_library(PXFOUNDATION_LIB
diff --git a/sdk/extensions/authoring/include/NvBlastExtAuthoring.h b/sdk/extensions/authoring/include/NvBlastExtAuthoring.h
index dd49677..d3ca85c 100644
--- a/sdk/extensions/authoring/include/NvBlastExtAuthoring.h
+++ b/sdk/extensions/authoring/include/NvBlastExtAuthoring.h
@@ -43,6 +43,7 @@ namespace Nv
{
class Mesh;
class VoronoiSitesGenerator;
+ class CutoutSet;
class FractureTool;
class ConvexMeshBuilder;
class BlastBondGenerator;
@@ -71,6 +72,22 @@ NVBLAST_API Nv::Blast::Mesh* NvBlastExtAuthoringCreateMesh(const physx::PxVec3*
const physx::PxVec2* uv, uint32_t verticesCount, const uint32_t* indices, uint32_t indicesCount);
/**
+Constructs mesh object from array of vertices, edges and facets.
+User should call release() after usage.
+
+\param[in] vertices Array for Nv::Blast::Vertex
+\param[in] edges Array for Nv::Blast::Edge
+\param[in] facets Array for Nv::Blast::Facet
+\param[in] verticesCount Number of vertices in mesh
+\param[in] edgesCount Number of edges in mesh
+\param[in] facetsCount Number of facets in mesh
+
+\return pointer to Nv::Blast::Mesh if it was created succefully otherwise return nullptr
+*/
+NVBLAST_API Nv::Blast::Mesh* NvBlastExtAuthoringCreateMeshFromFacets(const void* vertices, const void* edges, const void* facets,
+ uint32_t verticesCount, uint32_t edgesCount, uint32_t facetsCount);
+
+/**
Voronoi sites should not be generated outside of the fractured mesh, so VoronoiSitesGenerator
should be supplied with fracture mesh.
\param[in] mesh Fracture mesh
@@ -80,6 +97,27 @@ should be supplied with fracture mesh.
NVBLAST_API Nv::Blast::VoronoiSitesGenerator* NvBlastExtAuthoringCreateVoronoiSitesGenerator(Nv::Blast::Mesh* mesh,
Nv::Blast::RandomGeneratorBase* rng);
+/** Instantiates a blank CutoutSet */
+NVBLAST_API Nv::Blast::CutoutSet* NvBlastExtAuthoringCreateCutoutSet();
+
+/**
+Builds a cutout set (which must have been initially created by createCutoutSet()).
+Uses a bitmap described by pixelBuffer, bufferWidth, and bufferHeight. Each pixel is represented
+by one byte in the buffer.
+
+\param cutoutSet the CutoutSet to build
+\param pixelBuffer pointer to be beginning of the pixel buffer
+\param bufferWidth the width of the buffer in pixels
+\param bufferHeight the height of the buffer in pixels
+\param segmentationErrorThreshold Reduce the number of vertices on curve untill segmentation error is smaller then specified. By default set it to 0.001.
+\param snapThreshold the pixel distance at which neighboring cutout vertices and segments may be fudged into alignment. By default set it to 1.
+\param periodic whether or not to use periodic boundary conditions when creating cutouts from the map
+\param expandGaps expand cutout regions to gaps or keep it as is
+
+*/
+NVBLAST_API void NvBlastExtAuthoringBuildCutoutSet(Nv::Blast::CutoutSet& cutoutSet, const uint8_t* pixelBuffer,
+ uint32_t bufferWidth, uint32_t bufferHeight, float segmentationErrorThreshold, float snapThreshold, bool periodic, bool expandGaps);
+
/**
Create FractureTool object.
\return Pointer to create FractureTool. User's code should release it after usage.
@@ -145,6 +183,16 @@ Performs pending fractures and generates fractured asset, render and collision g
NVBLAST_API Nv::Blast::AuthoringResult* NvBlastExtAuthoringProcessFracture(Nv::Blast::FractureTool& fTool,
Nv::Blast::BlastBondGenerator& bondGenerator, Nv::Blast::ConvexMeshBuilder& collisionBuilder, const Nv::Blast::CollisionParams& collisionParam, int32_t defaultSupportDepth = -1);
+/**
+Updates graphics mesh only
+
+\param[in] fTool Fracture tool created by NvBlastExtAuthoringCreateFractureTool
+\param[out] ares AuthoringResult object which contains chunks, for which rendermeshes will be updated (e.g. to tweak UVs).
+*/
+NVBLAST_API void NvBlastExtUpdateGraphicsMesh(Nv::Blast::FractureTool& fTool, Nv::Blast::AuthoringResult& ares);
+
+
+
/**
Creates MeshCleaner object
@@ -173,7 +221,7 @@ descriptor arrays returned. The user must free this memory after use with NVBLAS
\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.
-
+\param[in] maxSeparation Maximal distance between chunks which can be connected by bond.
\return the number of bonds in newBondDescs
*/
NVBLAST_API uint32_t NvBlastExtAuthoringFindAssetConnectingBonds
@@ -185,7 +233,8 @@ NVBLAST_API uint32_t NvBlastExtAuthoringFindAssetConnectingBonds
const uint32_t** convexHullOffsets,
const Nv::Blast::CollisionHull*** chunkHulls,
uint32_t componentCount,
- NvBlastExtAssetUtilsBondDesc*& newBondDescs
+ NvBlastExtAssetUtilsBondDesc*& newBondDescs,
+ float maxSeparation = 0.0f
);
#endif // ifndef NVBLASTAUTHORING_H
diff --git a/sdk/extensions/authoring/include/NvBlastExtAuthoringBondGenerator.h b/sdk/extensions/authoring/include/NvBlastExtAuthoringBondGenerator.h
index 0151ed9..bd25098 100644
--- a/sdk/extensions/authoring/include/NvBlastExtAuthoringBondGenerator.h
+++ b/sdk/extensions/authoring/include/NvBlastExtAuthoringBondGenerator.h
@@ -56,10 +56,12 @@ struct PlaneChunkIndexer;
Bond interface generation configuration
EXACT - common surface will be searched
AVERAGE - Inerface is approximated by projections or intersecitons with midplane
+ maxSeparation - for AVERAGE mode. Maximum distance between chunks and midplane used in decision whether create bond or chunks are too far from each other.
*/
struct BondGenerationConfig
{
enum BondGenMode { EXACT, AVERAGE };
+ float maxSeparation;
BondGenMode bondMode;
};
@@ -164,7 +166,7 @@ public:
\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;
+ const bool* chunkIsSupport, const uint32_t* meshGroups, NvBlastBondDesc*& resultBondDescs, float maxSeparation) = 0;
};
} // namespace Blast
diff --git a/sdk/extensions/authoring/include/NvBlastExtAuthoringCutout.h b/sdk/extensions/authoring/include/NvBlastExtAuthoringCutout.h
new file mode 100644
index 0000000..7a26393
--- /dev/null
+++ b/sdk/extensions/authoring/include/NvBlastExtAuthoringCutout.h
@@ -0,0 +1,106 @@
+// 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 NVBLASTAUTHORINGCUTOUT_H
+#define NVBLASTAUTHORINGCUTOUT_H
+
+#include "NvBlastExtAuthoringTypes.h"
+
+
+namespace Nv
+{
+namespace Blast
+{
+
+/**
+Interface to a "cutout set," used with chippable fracturing. A cutout set is created from a bitmap. The
+result is turned into cutouts which are applied to the mesh. For example, a bitmap which looks like a brick
+pattern will generate a cutout for each "brick," forming the cutout set.
+
+Each cutout is a 2D entity, meant to be projected onto various faces of a mesh. They are represented
+by a set of 2D vertices, which form closed loops. More than one loop may represent a single cutout, if
+the loops are forced to be convex. Otherwise, a cutout is represented by a single loop.
+*/
+class CutoutSet
+{
+public:
+ /** Returns the number of cutouts in the set. */
+ virtual uint32_t getCutoutCount() const = 0;
+
+ /**
+ Applies to the cutout indexed by cutoutIndex:
+ Returns the number of vertices in the cutout.
+ */
+ virtual uint32_t getCutoutVertexCount(uint32_t cutoutIndex) const = 0;
+
+ /**
+ Applies to the cutout indexed by cutoutIndex:
+ Returns the number of loops in this cutout.
+ */
+ virtual uint32_t getCutoutLoopCount(uint32_t cutoutIndex) const = 0;
+
+ /**
+ Applies to the cutout indexed by cutoutIndex:
+ Returns the vertex indexed by vertexIndex. (Only the X and Y coordinates are used.)
+ */
+ virtual const physx::PxVec3& getCutoutVertex(uint32_t cutoutIndex, uint32_t vertexIndex) const = 0;
+
+ /**
+ Applies to the cutout indexed by cutoutIndex:
+ Returns the number of vertices in the loop indexed by loopIndex.
+ */
+ virtual uint32_t getCutoutLoopSize(uint32_t coutoutIndex, uint32_t loopIndex) const = 0;
+
+ /**
+ Applies to the cutout indexed by cutoutIndex:
+ Returns the vertex index of the vertex indexed by vertexNum, in the loop
+ indexed by loopIndex.
+ */
+ virtual uint32_t getCutoutLoopVertexIndex(uint32_t cutoutIndex, uint32_t loopIndex, uint32_t vertexNum) const = 0;
+
+ /**
+ Applies to the cutout indexed by cutoutIndex:
+ Returns the flags of the vertex indexed by vertexNum, in the loop
+ indexed by loopIndex.
+ */
+ virtual uint32_t getCutoutLoopVertexFlags(uint32_t cutoutIndex, uint32_t loopIndex, uint32_t vertexNum) const = 0;
+
+ /**
+ Whether or not this cutout set is to be tiled.
+ */
+ virtual bool isPeriodic() const = 0;
+
+ /**
+ The dimensions of the fracture map used to create the cutout set.
+ */
+ virtual const physx::PxVec2& getDimensions() const = 0;
+
+ /** Serialization */
+ //virtual void serialize(physx::PxFileBuf& stream) const = 0;
+ //virtual void deserialize(physx::PxFileBuf& stream) = 0;
+
+ /** Releases all memory and deletes itself. */
+ virtual void release() = 0;
+
+protected:
+ /** Protected destructor. Use the release() method. */
+ virtual ~CutoutSet() {}
+};
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // idndef NVBLASTAUTHORINGCUTOUT_H
diff --git a/sdk/extensions/authoring/include/NvBlastExtAuthoringFractureTool.h b/sdk/extensions/authoring/include/NvBlastExtAuthoringFractureTool.h
index 2c8eef4..e926005 100644
--- a/sdk/extensions/authoring/include/NvBlastExtAuthoringFractureTool.h
+++ b/sdk/extensions/authoring/include/NvBlastExtAuthoringFractureTool.h
@@ -39,6 +39,7 @@ namespace Blast
class SpatialAccelerator;
class Triangulator;
class Mesh;
+class CutoutSet;
/*
Chunk data, chunk with chunkId == 0 is always source mesh.
@@ -49,53 +50,97 @@ struct ChunkInfo
int32_t parent;
int32_t chunkId;
bool isLeaf;
+ bool isChanged;
};
+/*
+ Noise fracturing configuration for chunks's faces
+*/
+struct NoiseConfiguration
+{
+ /**
+ Noisy slicing configutaion:
+
+ Amplitude of cutting surface noise. If it is 0 - noise is disabled.
+ */
+ float amplitude = 0.f;
+
+ /**
+ Frequencey of cutting surface noise.
+ */
+ float frequency = 1.f;
+
+ /**
+ Octave number in slicing surface noise.
+ */
+ uint32_t octaveNumber = 1;
+
+ /**
+ Cutting surface resolution.
+ */
+ uint32_t surfaceResolution = 1;
+};
/*
Slicing fracturing configuration
*/
struct SlicingConfiguration
{
- /**
+ /**
Number of slices in each direction
*/
int32_t x_slices = 1, y_slices = 1, z_slices = 1;
-
- /**
+
+ /**
Offset variation, value in [0, 1]
*/
float offset_variations = 0.f;
-
- /**
+
+ /**
Angle variation, value in [0, 1]
*/
float angle_variations = 0.f;
+ /*
+ Noise parameters for faces between sliced chunks
+ */
+ NoiseConfiguration noise;
+};
+
+/**
+ Cutout fracturing configuration
+*/
+struct CutoutConfiguration
+{
/**
- Noisy slicing configutaion:
+ Set of grouped convex loop patterns for cutout in normal direction.
+ Not required for PLANE_ONLY mode
+ */
+ CutoutSet* cutoutSet = nullptr;
- Amplitude of cutting surface noise. If it is 0 - noise is disabled.
+ /**
+ Transform for initial pattern position and orientation.
+ By default 2d pattern lies in XY plane (Y is up) the center of pattern is (0, 0)
*/
- float noiseAmplitude = 0.f;
-
+ physx::PxTransform transform = physx::PxTransform(physx::PxIdentity);
+
/**
- Frequencey of cutting surface noise.
+ Scale for pattern. Unscaled pattern has size (1, 1).
+ For negative scale pattern will be placed at the center of chunk and scaled with max distance between points of its AABB
*/
- float noiseFrequency = 1.f;
+ physx::PxVec2 scale = physx::PxVec2(-1, -1);
/**
- Octave number in slicing surface noise.
+ If relative transform is set - position will be displacement vector from chunk's center. Otherwise from global origin.
*/
- uint32_t noiseOctaveNumber = 1;
+ bool isRelativeTransform = true;
/**
- Cutting surface resolution.
+ Noise parameters for cutout surface, see NoiseConfiguration.
*/
- uint32_t surfaceResolution = 1;
+ NoiseConfiguration noise;
};
-
/**
Class for voronoi sites generation inside supplied mesh.
*/
@@ -159,11 +204,13 @@ public:
\param[in] center Center of sphere
*/
virtual void generateInSphere(const uint32_t count, const float radius, const physx::PxVec3& center) = 0;
+
/**
Set stencil mesh. With stencil mesh sites are generated only inside both of fracture and stencil meshes.
\param[in] stencil Stencil mesh.
*/
virtual void setStencil(const Mesh* stencil) = 0;
+
/**
Removes stencil mesh
*/
@@ -269,7 +316,33 @@ public:
\return If 0, fracturing is successful.
*/
- virtual int32_t slicing(uint32_t chunkId, SlicingConfiguration conf, bool replaceChunk, RandomGeneratorBase* rnd) = 0;
+ virtual int32_t slicing(uint32_t chunkId, const SlicingConfiguration& conf, bool replaceChunk, RandomGeneratorBase* rnd) = 0;
+
+ /**
+ Cut chunk with plane.
+ \param[in] chunkId Chunk to fracture
+ \param[in] normal Plane normal
+ \param[in] position Point on plane
+ \param[in] noise Noise configuration for plane-chunk intersection, see NoiseConfiguration.
+ \param[in] replaceChunk if 'true', newly generated chunks will replace source chunk, if 'false', newly generated chunks will be at next depth level, source chunk will be parent for them.
+ Case replaceChunk == true && chunkId == 0 considered as wrong input parameters
+ \param[in] rnd User supplied random number generator
+
+ \return If 0, fracturing is successful.
+ */
+ virtual int32_t cut(uint32_t chunkId, const physx::PxVec3& normal, const physx::PxVec3& position, const NoiseConfiguration& noise, bool replaceChunk, RandomGeneratorBase* rnd) = 0;
+
+ /**
+ Cutout fracture for specified chunk.
+ \param[in] chunkId Chunk to fracture
+ \param[in] conf Cutout parameters, see CutoutConfiguration.
+ \param[in] replaceChunk if 'true', newly generated chunks will replace source chunk, if 'false', newly generated chunks will be at next depth level, source chunk will be parent for them.
+ Case replaceChunk == true && chunkId == 0 considered as wrong input parameters
+ \param[in] rnd User supplied random number generator
+
+ \return If 0, fracturing is successful.
+ */
+ virtual int32_t cutout(uint32_t chunkId, CutoutConfiguration conf, bool replaceChunk, RandomGeneratorBase* rnd) = 0;
/**
@@ -277,6 +350,9 @@ public:
*/
virtual void finalizeFracturing() = 0;
+ /**
+ Returns overall number of chunks in fracture.
+ */
virtual uint32_t getChunkCount() const = 0;
/**
@@ -302,6 +378,15 @@ public:
virtual uint32_t getBaseMesh(int32_t chunkIndex, Triangle*& output) = 0;
/**
+ Update chunk base mesh
+ \note Doesn't allocates output array, Triangle* output should be preallocated by user
+ \param[in] chunkIndex Chunk index
+ \param[out] output Array of triangles to be filled
+ \return number of triangles in base mesh
+ */
+ virtual uint32_t updateBaseMesh(int32_t chunkIndex, Triangle* output) = 0;
+
+ /**
Return index of chunk with specified chunkId
\param[in] chunkId Chunk ID
\return Chunk index in internal buffer, if not exist -1 is returned.
@@ -330,7 +415,6 @@ public:
*/
virtual uint32_t getChunksIdAtDepth(uint32_t depth, int32_t*& chunkIds) = 0;
-
/**
Get result geometry without noise as vertex and index buffers, where index buffers contain series of triplets
which represent triangles.
@@ -368,7 +452,28 @@ public:
*/
virtual bool deleteAllChildrenOfChunk(int32_t chunkId) = 0;
+ /**
+ Optimize chunk hierarhy for better runtime performance.
+ It tries to unite chunks to groups of some size in order to transform flat hierarchy (all chunks are children of single root)
+ to tree like hieracrhy with limited number of children for each chunk.
+ \param[in] maxAtLevel If number of children of some chunk less then maxAtLevel then it would be considered as already optimized and skipped.
+ \param[in] maxGroupSize Max number of children for processed chunks.
+ */
virtual void uniteChunks(uint32_t maxAtLevel, uint32_t maxGroupSize) = 0;
+
+ /**
+ Rescale interior uv coordinates of given chunk to fit square of given size.
+ \param[in] side Size of square side
+ \param[in] chunkId Chunk ID for which UVs should be scaled.
+ */
+ virtual void fitUvToRect(float side, uint32_t chunkId) = 0;
+
+ /**
+ Rescale interior uv coordinates of all existing chunks to fit square of given size, relative sizes will be preserved.
+ \param[in] side Size of square side
+ */
+ virtual void fitAllUvToRect(float side) = 0;
+
};
} // namespace Blast
diff --git a/sdk/extensions/authoring/include/NvBlastExtAuthoringTypes.h b/sdk/extensions/authoring/include/NvBlastExtAuthoringTypes.h
index 512beca..b81ed25 100644
--- a/sdk/extensions/authoring/include/NvBlastExtAuthoringTypes.h
+++ b/sdk/extensions/authoring/include/NvBlastExtAuthoringTypes.h
@@ -90,6 +90,14 @@ struct Triangle
{
return ((b.p - a.p).cross(c.p - a.p));
}
+ inline Vertex& getVertex(uint32_t index)
+ {
+ return (&a)[index];
+ }
+ inline const Vertex& getVertex(uint32_t index) const
+ {
+ return (&a)[index];
+ }
};
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp
index b024bb7..13654e2 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoring.cpp
@@ -14,6 +14,7 @@
#include "NvBlastExtAuthoringFractureToolImpl.h"
#include "NvBlastExtAuthoringCollisionBuilderImpl.h"
#include "NvBlastExtAuthoringBondGeneratorImpl.h"
+#include "NvBlastExtAuthoringCutoutImpl.h"
#include "NvBlastTypes.h"
#include "NvBlastIndexFns.h"
#include "NvBlast.h"
@@ -35,6 +36,11 @@ Mesh* NvBlastExtAuthoringCreateMesh(const PxVec3* position, const PxVec3* normal
return new MeshImpl(position, normals, uv, verticesCount, indices, indicesCount);
}
+Mesh* NvBlastExtAuthoringCreateMeshFromFacets(const void* vertices, const void* edges, const void* facets, uint32_t verticesCount, uint32_t edgesCount, uint32_t facetsCount)
+{
+ return new MeshImpl((Vertex*)vertices, (Edge*)edges, (Facet*)facets, verticesCount, edgesCount, facetsCount);
+}
+
MeshCleaner* NvBlastExtAuthoringCreateMeshCleaner()
{
return new MeshCleanerImpl;
@@ -45,6 +51,17 @@ VoronoiSitesGenerator* NvBlastExtAuthoringCreateVoronoiSitesGenerator(Mesh* mesh
return new VoronoiSitesGeneratorImpl(mesh, rng);
}
+CutoutSet* NvBlastExtAuthoringCreateCutoutSet()
+{
+ return new CutoutSetImpl();
+}
+
+void NvBlastExtAuthoringBuildCutoutSet(CutoutSet& cutoutSet, const uint8_t* pixelBuffer, uint32_t bufferWidth, uint32_t bufferHeight,
+ float segmentationErrorThreshold, float snapThreshold, bool periodic, bool expandGaps)
+{
+ ::createCutoutSet(*(CutoutSetImpl*)&cutoutSet, pixelBuffer, bufferWidth, bufferHeight, segmentationErrorThreshold, snapThreshold, periodic, expandGaps);
+}
+
FractureTool* NvBlastExtAuthoringCreateFractureTool()
{
return new FractureToolImpl;
@@ -297,6 +314,8 @@ AuthoringResult* NvBlastExtAuthoringProcessFracture(FractureTool& fTool, BlastBo
{
uint32_t trianglesCount = aResult.geometryOffset[i + 1] - aResult.geometryOffset[i];
memcpy(aResult.geometry + aResult.geometryOffset[i], chunkGeometry[i], trianglesCount * sizeof(Nv::Blast::Triangle));
+ delete chunkGeometry[i];
+ chunkGeometry[i] = nullptr;
}
float maxX = INT32_MIN;
@@ -376,7 +395,8 @@ uint32_t NvBlastExtAuthoringFindAssetConnectingBonds
const uint32_t** convexHullOffsets,
const CollisionHull*** chunkHulls,
uint32_t componentCount,
- NvBlastExtAssetUtilsBondDesc*& newBondDescs
+ NvBlastExtAssetUtilsBondDesc*& newBondDescs,
+ float maxSeparation
)
{
//We don't need to use any of the cooking related parts of this
@@ -441,7 +461,7 @@ uint32_t NvBlastExtAuthoringFindAssetConnectingBonds
//Find the bonds
NvBlastBondDesc* newBonds = nullptr;
- const int32_t newBoundCount = bondGenerator.bondsFromPrefractured(totalChunkCount, combinedConvexHullOffsets.data(), combinedConvexHulls.data(), isSupportChunk.get(), originalComponentIndex.data(), newBonds);
+ const int32_t newBoundCount = bondGenerator.bondsFromPrefractured(totalChunkCount, combinedConvexHullOffsets.data(), combinedConvexHulls.data(), isSupportChunk.get(), originalComponentIndex.data(), newBonds, maxSeparation);
//Convert the bonds back to per-component chunks
newBondDescs = SAFE_ARRAY_NEW(NvBlastExtAssetUtilsBondDesc, newBoundCount);
@@ -466,3 +486,13 @@ uint32_t NvBlastExtAuthoringFindAssetConnectingBonds
return newBoundCount;
}
+
+
+void NvBlastExtUpdateGraphicsMesh(Nv::Blast::FractureTool& fTool, Nv::Blast::AuthoringResult& aResult)
+{
+ uint32_t chunkCount = fTool.getChunkCount();
+ for (uint32_t i = 0; i < chunkCount; ++i)
+ {
+ fTool.updateBaseMesh(i, aResult.geometry + aResult.geometryOffset[i]);
+ }
+} \ No newline at end of file
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.cpp
index 02e7a30..95d2f1a 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.cpp
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.cpp
@@ -149,7 +149,7 @@ namespace Nv
};
float BlastBondGeneratorImpl::processWithMidplanes(TriangleProcessor* trProcessor, const std::vector<PxVec3>& chunk1Points, const std::vector<PxVec3>& chunk2Points,
- const std::vector<PxVec3>& hull1p, const std::vector<PxVec3>& hull2p, PxVec3& normal, PxVec3& centroid)
+ const std::vector<PxVec3>& hull1p, const std::vector<PxVec3>& hull2p, PxVec3& normal, PxVec3& centroid, float maxSeparation)
{
PxBounds3 bounds;
PxBounds3 aBounds;
@@ -185,7 +185,7 @@ namespace Nv
chunk2Centroid *= (1.0f / chunk2Points.size());
Separation separation;
- if (!importerHullsInProximityApexFree(hull1p.size(), hull1p.data(), aBounds, PxTransform(PxIdentity), PxVec3(1, 1, 1), hull2p.size(), hull2p.data(), bBounds, PxTransform(PxIdentity), PxVec3(1, 1, 1), 0.000, &separation))
+ if (!importerHullsInProximityApexFree(hull1p.size(), hull1p.data(), aBounds, PxTransform(PxIdentity), PxVec3(1, 1, 1), hull2p.size(), hull2p.data(), bBounds, PxTransform(PxIdentity), PxVec3(1, 1, 1), 2.0f * maxSeparation, &separation))
{
return 0.0;
}
@@ -198,13 +198,13 @@ namespace Nv
}
std::vector<PxVec3> interfacePoints;
- float firstCentroidSide = midplane.distance(chunk1Centroid);
- float secondCentroidSide = midplane.distance(chunk2Centroid);
+ float firstCentroidSide = (midplane.distance(chunk1Centroid) > 0) ? 1 : -1;
+ float secondCentroidSide = (midplane.distance(chunk2Centroid) > 0) ? 1 : -1;
for (uint32_t i = 0; i < chunk1Points.size(); ++i)
{
float dst = midplane.distance(chunk1Points[i]);
- if (dst * firstCentroidSide < 0)
+ if (dst * firstCentroidSide < maxSeparation)
{
interfacePoints.push_back(chunk1Points[i]);
}
@@ -213,7 +213,7 @@ namespace Nv
for (uint32_t i = 0; i < chunk2Points.size(); ++i)
{
float dst = midplane.distance(chunk2Points[i]);
- if (dst * secondCentroidSide < 0)
+ if (dst * secondCentroidSide < maxSeparation)
{
interfacePoints.push_back(chunk2Points[i]);
}
@@ -272,8 +272,7 @@ namespace Nv
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);
if (!chunkHulls)
@@ -344,9 +343,14 @@ namespace Nv
{
tempHullPtr->release();
}
- bnd.scaleFast(1.1f);
- candidates.push_back(BondGenerationCandidate(bnd.minimum, false, chunk, meshGroups[chunk]));
- candidates.push_back(BondGenerationCandidate(bnd.maximum, true, chunk, meshGroups[chunk]));
+ float minSide = bnd.getDimensions().abs().minElement();
+ if (minSide > 0.f)
+ {
+ float scaling = std::max(1.1f, conf.maxSeparation / (minSide));
+ bnd.scaleFast(scaling);
+ }
+ candidates.push_back(BondGenerationCandidate(bnd.minimum, false, chunk, meshGroups != nullptr ? meshGroups[chunk] : 0));
+ candidates.push_back(BondGenerationCandidate(bnd.maximum, true, chunk, meshGroups != nullptr ? meshGroups[chunk] : 0));
}
std::sort(candidates.begin(), candidates.end());
@@ -360,7 +364,7 @@ namespace Nv
{
for (uint32_t activeChunk : listOfActiveChunks)
{
- if (candidates[activeChunk].parentComponent == candidates[idx].parentComponent) continue; // Don't connect components with itself.
+ if (meshGroups != nullptr && (meshGroups[activeChunk] == candidates[idx].parentComponent)) continue; // Don't connect components with itself.
possibleBondGraph[activeChunk].push_back(candidates[idx].parentChunk);
}
listOfActiveChunks.insert(candidates[idx].parentChunk);
@@ -388,7 +392,7 @@ namespace Nv
PxVec3 normal;
PxVec3 centroid;
- 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);
+ 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, conf.maxSeparation);
if (area > 0)
{
NvBlastBondDesc bDesc;
@@ -1118,9 +1122,10 @@ namespace Nv
}
- int32_t BlastBondGeneratorImpl::bondsFromPrefractured(uint32_t meshCount, const uint32_t* convexHullOffset, const CollisionHull** chunkHulls, const bool* chunkIsSupport, const uint32_t* meshGroups, NvBlastBondDesc*& resultBondDescs)
+ int32_t BlastBondGeneratorImpl::bondsFromPrefractured(uint32_t meshCount, const uint32_t* convexHullOffset, const CollisionHull** chunkHulls, const bool* chunkIsSupport, const uint32_t* meshGroups, NvBlastBondDesc*& resultBondDescs, float maxSeparation)
{
BondGenerationConfig conf;
+ conf.maxSeparation = maxSeparation;
conf.bondMode = BondGenerationConfig::AVERAGE;
return createFullBondListAveraged(meshCount, convexHullOffset, nullptr, chunkHulls, chunkIsSupport, meshGroups, resultBondDescs, conf);
}
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.h b/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.h
index a33468d..8337cf2 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.h
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringBondGeneratorImpl.h
@@ -69,13 +69,13 @@ public:
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;
+ const bool* chunkIsSupport, const uint32_t* meshGroups, NvBlastBondDesc*& resultBondDescs, float maxSeparation) override;
private:
float processWithMidplanes( TriangleProcessor* trProcessor,
const std::vector<physx::PxVec3>& chunk1Points, const std::vector<physx::PxVec3>& chunk2Points,
const std::vector<physx::PxVec3>& hull1p, const std::vector<physx::PxVec3>& hull2p,
- physx::PxVec3& normal, physx::PxVec3& centroid);
+ physx::PxVec3& normal, physx::PxVec3& centroid, float maxSeparation);
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);
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringCutoutImpl.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoringCutoutImpl.cpp
new file mode 100644
index 0000000..5c2986f
--- /dev/null
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringCutoutImpl.cpp
@@ -0,0 +1,2508 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, 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.
+*/
+
+
+#include "NvBlastExtAuthoringCutoutImpl.h"
+#include "NvBlastGlobals.h"
+#include <NvBlastAssert.h>
+#include <algorithm>
+#include <set>
+#include <map>
+#include "PxMath.h"
+
+#pragma warning(disable : 4267)
+#pragma warning(disable : 4244)
+
+#define CUTOUT_DISTANCE_THRESHOLD (0.7f)
+
+#define CUTOUT_DISTANCE_EPS (0.01f)
+
+using namespace Nv::Blast;
+
+// Unsigned modulus
+PX_INLINE uint32_t mod(int32_t n, uint32_t modulus)
+{
+ const int32_t d = n/(int32_t)modulus;
+ const int32_t m = n - d*(int32_t)modulus;
+ return m >= 0 ? (uint32_t)m : (uint32_t)m + modulus;
+}
+
+PX_INLINE float square(float x)
+{
+ return x * x;
+}
+
+// 2D cross product
+PX_INLINE float dotXY(const physx::PxVec3& v, const physx::PxVec3& w)
+{
+ return v.x * w.x + v.y * w.y;
+}
+
+// Z-component of cross product
+PX_INLINE float crossZ(const physx::PxVec3& v, const physx::PxVec3& w)
+{
+ return v.x * w.y - v.y * w.x;
+}
+
+// z coordinates may be used to store extra info - only deal with x and y
+PX_INLINE float perpendicularDistanceSquared(const physx::PxVec3& v0, const physx::PxVec3& v1, const physx::PxVec3& v2)
+{
+ const physx::PxVec3 base = v2 - v0;
+ const physx::PxVec3 leg = v1 - v0;
+
+ const float baseLen2 = dotXY(base, base);
+
+ return baseLen2 > PX_EPS_F32 * dotXY(leg, leg) ? square(crossZ(base, leg)) / baseLen2 : 0.0f;
+}
+
+// z coordinates may be used to store extra info - only deal with x and y
+PX_INLINE float perpendicularDistanceSquared(const std::vector< physx::PxVec3 >& cutout, uint32_t index)
+{
+ const uint32_t size = cutout.size();
+ return perpendicularDistanceSquared(cutout[(index + size - 1) % size], cutout[index], cutout[(index + 1) % size]);
+}
+
+////////////////////////////////////////////////
+// ApexShareUtils - Begin
+////////////////////////////////////////////////
+
+struct BoundsRep
+{
+ BoundsRep() : type(0)
+ {
+ aabb.setEmpty();
+ }
+
+ physx::PxBounds3 aabb;
+ uint32_t type; // By default only reports if subtypes are the same, configurable. Valid range {0...7}
+};
+
+struct IntPair
+{
+ void set(int32_t _i0, int32_t _i1)
+ {
+ i0 = _i0;
+ i1 = _i1;
+ }
+
+ int32_t i0, i1;
+
+ static int compare(const void* a, const void* b)
+ {
+ const int32_t diff0 = ((IntPair*)a)->i0 - ((IntPair*)b)->i0;
+ return diff0 ? diff0 : (((IntPair*)a)->i1 - ((IntPair*)b)->i1);
+ }
+};
+
+struct BoundsInteractions
+{
+ BoundsInteractions() : bits(0x8040201008040201ULL) {}
+ BoundsInteractions(bool setAll) : bits(setAll ? 0xFFFFFFFFFFFFFFFFULL : 0x0000000000000000ULL) {}
+
+ bool set(unsigned group1, unsigned group2, bool interacts)
+ {
+ if (group1 >= 8 || group2 >= 8)
+ {
+ return false;
+ }
+ const uint64_t mask = (uint64_t)1 << ((group1 << 3) + group2) | (uint64_t)1 << ((group2 << 3) + group1);
+ if (interacts)
+ {
+ bits |= mask;
+ }
+ else
+ {
+ bits &= ~mask;
+ }
+ return true;
+ }
+
+ uint64_t bits;
+};
+
+enum Bounds3Axes
+{
+ Bounds3X = 1,
+ Bounds3Y = 2,
+ Bounds3Z = 4,
+
+ Bounds3XY = Bounds3X | Bounds3Y,
+ Bounds3YZ = Bounds3Y | Bounds3Z,
+ Bounds3ZX = Bounds3Z | Bounds3X,
+
+ Bounds3XYZ = Bounds3X | Bounds3Y | Bounds3Z
+};
+
+void boundsCalculateOverlaps(std::vector<IntPair>& overlaps, Bounds3Axes axesToUse, const BoundsRep* bounds, uint32_t boundsCount, uint32_t boundsByteStride,
+ const BoundsInteractions& interactions = BoundsInteractions(), bool append = false);
+
+void createIndexStartLookup(std::vector<uint32_t>& lookup, int32_t indexBase, uint32_t indexRange, int32_t* indexSource, uint32_t indexCount, uint32_t indexByteStride);
+
+/*
+Index bank - double-sided free list for O(1) borrow/return of unique IDs
+
+Type IndexType should be an unsigned integer type or something that can be cast to and from
+an integer
+*/
+template <class IndexType>
+class IndexBank
+{
+public:
+ IndexBank<IndexType>(uint32_t capacity = 0) : indexCount(0), capacityLocked(false)
+ {
+ maxCapacity = calculateMaxCapacity();
+ reserve_internal(capacity);
+ }
+
+ // Copy constructor
+ IndexBank<IndexType>(const IndexBank<IndexType>& other)
+ {
+ *this = other;
+ }
+
+ virtual ~IndexBank<IndexType>() {}
+
+ // Assignment operator
+ IndexBank<IndexType>& operator = (const IndexBank<IndexType>& other)
+ {
+ indices = other.indices;
+ ranks = other.ranks;
+ maxCapacity = other.maxCapacity;
+ indexCount = other.indexCount;
+ capacityLocked = other.capacityLocked;
+ return *this;
+ }
+
+ void setIndicesAndRanks(uint16_t* indicesIn, uint16_t* ranksIn, uint32_t capacityIn, uint32_t usedCountIn)
+ {
+ indexCount = usedCountIn;
+ reserve_internal(capacityIn);
+ for (uint32_t i = 0; i < capacityIn; ++i)
+ {
+ indices[i] = indicesIn[i];
+ ranks[i] = ranksIn[i];
+ }
+ }
+
+ void clear(uint32_t capacity = 0, bool used = false)
+ {
+ capacityLocked = false;
+ indices.reset();
+ ranks.reset();
+ reserve_internal(capacity);
+ if (used)
+ {
+ indexCount = capacity;
+ indices.resize(capacity);
+ for (IndexType i = (IndexType)0; i < (IndexType)capacity; ++i)
+ {
+ indices[i] = i;
+ }
+ }
+ else
+ {
+ indexCount = 0;
+ }
+ }
+
+ // Equivalent to calling freeLastUsed() until the used list is empty.
+ void clearFast()
+ {
+ indexCount = 0;
+ }
+
+ // This is the reserve size. The bank can only grow, due to shuffling of indices
+ virtual void reserve(uint32_t capacity)
+ {
+ reserve_internal(capacity);
+ }
+
+ // If lock = true, keeps bank from automatically resizing
+ void lockCapacity(bool lock)
+ {
+ capacityLocked = lock;
+ }
+
+ bool isCapacityLocked() const
+ {
+ return capacityLocked;
+ }
+
+ void setMaxCapacity(uint32_t inMaxCapacity)
+ {
+ // Cannot drop below current capacity, nor above max set by data types
+ maxCapacity = PxClamp(inMaxCapacity, capacity(), calculateMaxCapacity());
+ }
+
+ uint32_t capacity() const
+ {
+ return indices.size();
+ }
+ uint32_t usedCount() const
+ {
+ return indexCount;
+ }
+ uint32_t freeCount() const
+ {
+ return capacity() - usedCount();
+ }
+
+ // valid from [0] to [size()-1]
+ const IndexType* usedIndices() const
+ {
+ return indices.data();
+ }
+
+ // valid from [0] to [free()-1]
+ const IndexType* freeIndices() const
+ {
+ return indices.begin() + usedCount();
+ }
+
+ bool isValid(IndexType index) const
+ {
+ return index < (IndexType)capacity();
+ }
+ bool isUsed(IndexType index) const
+ {
+ return isValid(index) && (ranks[index] < (IndexType)usedCount());
+ }
+ bool isFree(IndexType index) const
+ {
+ return isValid(index) && !isUsed();
+ }
+
+ IndexType getRank(IndexType index) const
+ {
+ return ranks[index];
+ }
+
+ // Gets the next available index, if any
+ bool useNextFree(IndexType& index)
+ {
+ if (freeCount() == 0)
+ {
+ if (capacityLocked)
+ {
+ return false;
+ }
+ if (capacity() >= maxCapacity)
+ {
+ return false;
+ }
+ reserve(PxClamp(capacity() * 2, (uint32_t)1, maxCapacity));
+ PX_ASSERT(freeCount() > 0);
+ }
+ index = indices[indexCount++];
+ return true;
+ }
+
+ // Frees the last used index, if any
+ bool freeLastUsed(IndexType& index)
+ {
+ if (usedCount() == 0)
+ {
+ return false;
+ }
+ index = indices[--indexCount];
+ return true;
+ }
+
+ // Requests a particular index. If that index is available, it is borrowed and the function
+ // returns true. Otherwise nothing happens and the function returns false.
+ bool use(IndexType index)
+ {
+ if (!indexIsValidForUse(index))
+ {
+ return false;
+ }
+ IndexType oldRank;
+ placeIndexAtRank(index, (IndexType)indexCount++, oldRank);
+ return true;
+ }
+
+ bool free(IndexType index)
+ {
+ if (!indexIsValidForFreeing(index))
+ {
+ return false;
+ }
+ IndexType oldRank;
+ placeIndexAtRank(index, (IndexType)--indexCount, oldRank);
+ return true;
+ }
+
+ bool useAndReturnRanks(IndexType index, IndexType& newRank, IndexType& oldRank)
+ {
+ if (!indexIsValidForUse(index))
+ {
+ return false;
+ }
+ newRank = (IndexType)indexCount++;
+ placeIndexAtRank(index, newRank, oldRank);
+ return true;
+ }
+
+ bool freeAndReturnRanks(IndexType index, IndexType& newRank, IndexType& oldRank)
+ {
+ if (!indexIsValidForFreeing(index))
+ {
+ return false;
+ }
+ newRank = (IndexType)--indexCount;
+ placeIndexAtRank(index, newRank, oldRank);
+ return true;
+ }
+
+protected:
+
+ bool indexIsValidForUse(IndexType index)
+ {
+ if (!isValid(index))
+ {
+ if (capacityLocked)
+ {
+ return false;
+ }
+ if (capacity() >= maxCapacity)
+ {
+ return false;
+ }
+ reserve(physx::PxClamp(2 * (uint32_t)index, (uint32_t)1, maxCapacity));
+ PX_ASSERT(isValid(index));
+ }
+ return !isUsed(index);
+ }
+
+ bool indexIsValidForFreeing(IndexType index)
+ {
+ if (!isValid(index))
+ {
+ // Invalid index
+ return false;
+ }
+ return isUsed(index);
+ }
+
+ // This is the reserve size. The bank can only grow, due to shuffling of indices
+ void reserve_internal(uint32_t capacity)
+ {
+ capacity = std::min(capacity, maxCapacity);
+ const uint32_t oldCapacity = indices.size();
+ if (capacity > oldCapacity)
+ {
+ indices.resize(capacity);
+ ranks.resize(capacity);
+ for (IndexType i = (IndexType)oldCapacity; i < (IndexType)capacity; ++i)
+ {
+ indices[i] = i;
+ ranks[i] = i;
+ }
+ }
+ }
+
+private:
+
+ void placeIndexAtRank(IndexType index, IndexType newRank, IndexType& oldRank) // returns old rank
+ {
+ const IndexType replacementIndex = indices[newRank];
+ oldRank = ranks[index];
+ indices[oldRank] = replacementIndex;
+ indices[newRank] = index;
+ ranks[replacementIndex] = oldRank;
+ ranks[index] = newRank;
+ }
+
+ uint32_t calculateMaxCapacity()
+ {
+#pragma warning(push)
+#pragma warning(disable: 4127) // conditional expression is constant
+ if (sizeof(IndexType) >= sizeof(uint32_t))
+ {
+ return 0xFFFFFFFF; // Limited by data type we use to report capacity
+ }
+ else
+ {
+ return (1u << (8 * std::min((uint32_t)sizeof(IndexType), 3u))) - 1; // Limited by data type we use for indices
+ }
+#pragma warning(pop)
+ }
+
+protected:
+
+ std::vector<IndexType> indices;
+ std::vector<IndexType> ranks;
+ uint32_t maxCapacity;
+ uint32_t indexCount;
+ bool capacityLocked;
+};
+
+struct Marker
+{
+ float pos;
+ uint32_t id; // lsb = type (0 = max, 1 = min), other bits used for object index
+
+ void set(float _pos, int32_t _id)
+ {
+ pos = _pos;
+ id = (uint32_t)_id;
+ }
+};
+
+static int compareMarkers(const void* A, const void* B)
+{
+ // Sorts by value. If values equal, sorts min types greater than max types, to reduce the # of overlaps
+ const float delta = ((Marker*)A)->pos - ((Marker*)B)->pos;
+ return delta != 0 ? (delta < 0 ? -1 : 1) : ((int)(((Marker*)A)->id & 1) - (int)(((Marker*)B)->id & 1));
+}
+
+void boundsCalculateOverlaps(std::vector<IntPair>& overlaps, Bounds3Axes axesToUse, const BoundsRep* bounds, uint32_t boundsCount, uint32_t boundsByteStride,
+ const BoundsInteractions& interactions, bool append)
+{
+ if (!append)
+ {
+ overlaps.clear();
+ }
+
+ uint32_t D = 0;
+ uint32_t axisNums[3];
+ for (unsigned i = 0; i < 3; ++i)
+ {
+ if ((axesToUse >> i) & 1)
+ {
+ axisNums[D++] = i;
+ }
+ }
+
+ if (D == 0 || D > 3)
+ {
+ return;
+ }
+
+ std::vector< std::vector<Marker> > axes;
+ axes.resize(D);
+ uint32_t overlapCount[3];
+
+ for (uint32_t n = 0; n < D; ++n)
+ {
+ const uint32_t axisNum = axisNums[n];
+ std::vector<Marker>& axis = axes[n];
+ overlapCount[n] = 0;
+ axis.resize(2 * boundsCount);
+ uint8_t* boundsPtr = (uint8_t*)bounds;
+ for (uint32_t i = 0; i < boundsCount; ++i, boundsPtr += boundsByteStride)
+ {
+ const BoundsRep& boundsRep = *(const BoundsRep*)boundsPtr;
+ const physx::PxBounds3& box = boundsRep.aabb;
+ float min = box.minimum[axisNum];
+ float max = box.maximum[axisNum];
+ if (min >= max)
+ {
+ const float mid = 0.5f * (min + max);
+ float pad = 0.000001f * fabsf(mid);
+ min = mid - pad;
+ max = mid + pad;
+ }
+ axis[i << 1].set(min, (int32_t)i << 1 | 1);
+ axis[i << 1 | 1].set(max, (int32_t)i << 1);
+ }
+ qsort(axis.data(), axis.size(), sizeof(Marker), compareMarkers);
+ uint32_t localOverlapCount = 0;
+ for (uint32_t i = 0; i < axis.size(); ++i)
+ {
+ Marker& marker = axis[i];
+ if (marker.id & 1)
+ {
+ overlapCount[n] += localOverlapCount;
+ ++localOverlapCount;
+ }
+ else
+ {
+ --localOverlapCount;
+ }
+ }
+ }
+
+ unsigned int axis0;
+ unsigned int axis1;
+ unsigned int axis2;
+ unsigned int maxBin;
+ if (D == 1)
+ {
+ maxBin = 0;
+ axis0 = axisNums[0];
+ axis1 = axis0;
+ axis2 = axis0;
+ }
+ else if (D == 2)
+ {
+ if (overlapCount[0] < overlapCount[1])
+ {
+ maxBin = 0;
+ axis0 = axisNums[0];
+ axis1 = axisNums[1];
+ axis2 = axis0;
+ }
+ else
+ {
+ maxBin = 1;
+ axis0 = axisNums[1];
+ axis1 = axisNums[0];
+ axis2 = axis0;
+ }
+ }
+ else
+ {
+ maxBin = overlapCount[0] < overlapCount[1] ? (overlapCount[0] < overlapCount[2] ? 0U : 2U) : (overlapCount[1] < overlapCount[2] ? 1U : 2U);
+ axis0 = axisNums[maxBin];
+ axis1 = (axis0 + 1) % 3;
+ axis2 = (axis0 + 2) % 3;
+ }
+
+ const uint64_t interactionBits = interactions.bits;
+
+ IndexBank<uint32_t> localOverlaps(boundsCount);
+ std::vector<Marker>& axis = axes[maxBin];
+ float boxMin1 = 0.0f;
+ float boxMax1 = 0.0f;
+ float boxMin2 = 0.0f;
+ float boxMax2 = 0.0f;
+
+ for (uint32_t i = 0; i < axis.size(); ++i)
+ {
+ Marker& marker = axis[i];
+ const uint32_t index = marker.id >> 1;
+ if (marker.id & 1)
+ {
+ const BoundsRep& boundsRep = *(const BoundsRep*)((uint8_t*)bounds + index*boundsByteStride);
+ const uint8_t interaction = (uint8_t)((interactionBits >> (boundsRep.type << 3)) & 0xFF);
+ const physx::PxBounds3& box = boundsRep.aabb;
+ // These conditionals compile out with optimization:
+ if (D > 1)
+ {
+ boxMin1 = box.minimum[axis1];
+ boxMax1 = box.maximum[axis1];
+ if (D == 3)
+ {
+ boxMin2 = box.minimum[axis2];
+ boxMax2 = box.maximum[axis2];
+ }
+ }
+ const uint32_t localOverlapCount = localOverlaps.usedCount();
+ const uint32_t* localOverlapIndices = localOverlaps.usedIndices();
+ for (uint32_t j = 0; j < localOverlapCount; ++j)
+ {
+ const uint32_t overlapIndex = localOverlapIndices[j];
+ const BoundsRep& overlapBoundsRep = *(const BoundsRep*)((uint8_t*)bounds + overlapIndex*boundsByteStride);
+ if ((interaction >> overlapBoundsRep.type) & 1)
+ {
+ const physx::PxBounds3& overlapBox = overlapBoundsRep.aabb;
+ // These conditionals compile out with optimization:
+ if (D > 1)
+ {
+ if (boxMin1 >= overlapBox.maximum[axis1] || boxMax1 <= overlapBox.minimum[axis1])
+ {
+ continue;
+ }
+ if (D == 3)
+ {
+ if (boxMin2 >= overlapBox.maximum[axis2] || boxMax2 <= overlapBox.minimum[axis2])
+ {
+ continue;
+ }
+ }
+ }
+ // Add overlap
+ IntPair pair;
+ pair.i0 = (int32_t)index;
+ pair.i1 = (int32_t)overlapIndex;
+ overlaps.push_back(pair);
+ }
+ }
+ PX_ASSERT(localOverlaps.isValid(index));
+ PX_ASSERT(!localOverlaps.isUsed(index));
+ localOverlaps.use(index);
+ }
+ else
+ {
+ // Remove local overlap
+ PX_ASSERT(localOverlaps.isValid(index));
+ localOverlaps.free(index);
+ }
+ }
+}
+
+void createIndexStartLookup(std::vector<uint32_t>& lookup, int32_t indexBase, uint32_t indexRange, int32_t* indexSource, uint32_t indexCount, uint32_t indexByteStride)
+{
+ if (indexRange == 0)
+ {
+ lookup.resize(std::max(indexRange + 1, 2u));
+ lookup[0] = 0;
+ lookup[1] = indexCount;
+ }
+ else
+ {
+ lookup.resize(indexRange + 1);
+ uint32_t indexPos = 0;
+ for (uint32_t i = 0; i < indexRange; ++i)
+ {
+ for (; indexPos < indexCount; ++indexPos, indexSource = (int32_t*)((uintptr_t)indexSource + indexByteStride))
+ {
+ if (*indexSource >= (int32_t)i + indexBase)
+ {
+ lookup[i] = indexPos;
+ break;
+ }
+ }
+ if (indexPos == indexCount)
+ {
+ lookup[i] = indexPos;
+ }
+ }
+ lookup[indexRange] = indexCount;
+ }
+}
+
+////////////////////////////////////////////////
+// ApexShareUtils - End
+////////////////////////////////////////////////
+
+struct CutoutVert
+{
+ int32_t cutoutIndex;
+ int32_t vertIndex;
+
+ void set(int32_t _cutoutIndex, int32_t _vertIndex)
+ {
+ cutoutIndex = _cutoutIndex;
+ vertIndex = _vertIndex;
+ }
+};
+
+struct NewVertex
+{
+ CutoutVert vertex;
+ float edgeProj;
+};
+
+static int compareNewVertices(const void* a, const void* b)
+{
+ const int32_t cutoutDiff = ((NewVertex*)a)->vertex.cutoutIndex - ((NewVertex*)b)->vertex.cutoutIndex;
+ if (cutoutDiff)
+ {
+ return cutoutDiff;
+ }
+ const int32_t vertDiff = ((NewVertex*)a)->vertex.vertIndex - ((NewVertex*)b)->vertex.vertIndex;
+ if (vertDiff)
+ {
+ return vertDiff;
+ }
+ const float projDiff = ((NewVertex*)a)->edgeProj - ((NewVertex*)b)->edgeProj;
+ return projDiff ? (projDiff < 0.0f ? -1 : 1) : 0;
+}
+
+template<typename T>
+class Map2d
+{
+public:
+ Map2d(uint32_t width, uint32_t height)
+ {
+ create_internal(width, height, NULL);
+ }
+ Map2d(uint32_t width, uint32_t height, T fillValue)
+ {
+ create_internal(width, height, &fillValue);
+ }
+ Map2d(const Map2d& map)
+ {
+ *this = map;
+ }
+
+ Map2d& operator = (const Map2d& map)
+ {
+ mMem.clear();
+ create_internal(map.mWidth, map.mHeight, NULL);
+ return *this;
+ }
+
+ void create(uint32_t width, uint32_t height)
+ {
+ return create_internal(width, height, NULL);
+ }
+ void create(uint32_t width, uint32_t height, T fillValue)
+ {
+ create_internal(width, height, &fillValue);
+ }
+
+ //void clear(const T value)
+ //{
+ // for (auto it = mMem.begin(); it != mMem.end(); it++)
+ // {
+ // for (auto it2 = it->begin(); it2 != it->end(); it2++)
+ // {
+ // *it2 = value;
+ // }
+ // }
+ //}
+
+ void setOrigin(uint32_t x, uint32_t y)
+ {
+ mOriginX = x;
+ mOriginY = y;
+ }
+
+ const T& operator()(int32_t x, int32_t y) const
+ {
+ x = (int32_t)mod(x+(int32_t)mOriginX, mWidth);
+ y = (int32_t)mod(y+(int32_t)mOriginY, mHeight);
+ return mMem[y][x];
+ }
+ T& operator()(int32_t x, int32_t y)
+ {
+ x = (int32_t)mod(x+(int32_t)mOriginX, mWidth);
+ y = (int32_t)mod(y+(int32_t)mOriginY, mHeight);
+ return mMem[y][x];
+ }
+
+private:
+
+ void create_internal(uint32_t width, uint32_t height, T* val)
+ {
+ mMem.clear();
+ mWidth = width;
+ mHeight = height;
+ mMem.resize(mHeight);
+ for (auto it = mMem.begin(); it != mMem.end(); it++)
+ {
+ it->resize(mWidth, val ? *val : 0);
+ }
+ mOriginX = 0;
+ mOriginY = 0;
+ }
+
+ std::vector<std::vector<T>> mMem;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mOriginX;
+ uint32_t mOriginY;
+};
+
+class BitMap
+{
+public:
+ BitMap() : mMem(NULL) {}
+ BitMap(uint32_t width, uint32_t height) : mMem(NULL)
+ {
+ create_internal(width, height, NULL);
+ }
+ BitMap(uint32_t width, uint32_t height, bool fillValue) : mMem(NULL)
+ {
+ create_internal(width, height, &fillValue);
+ }
+ BitMap(const BitMap& map)
+ {
+ *this = map;
+ }
+ ~BitMap()
+ {
+ delete [] mMem;
+ }
+
+ BitMap& operator = (const BitMap& map)
+ {
+ delete [] mMem;
+ mMem = NULL;
+ if (map.mMem)
+ {
+ create_internal(map.mWidth, map.mHeight, NULL);
+ memcpy(mMem, map.mMem, mHeight * mRowBytes);
+ }
+ return *this;
+ }
+
+ void create(uint32_t width, uint32_t height)
+ {
+ return create_internal(width, height, NULL);
+ }
+ void create(uint32_t width, uint32_t height, bool fillValue)
+ {
+ create_internal(width, height, &fillValue);
+ }
+
+ void clear(bool value)
+ {
+ memset(mMem, value ? 0xFF : 0x00, mRowBytes * mHeight);
+ }
+
+ void setOrigin(uint32_t x, uint32_t y)
+ {
+ mOriginX = x;
+ mOriginY = y;
+ }
+
+ bool read(int32_t x, int32_t y) const
+ {
+ x = (int32_t)mod(x+(int32_t)mOriginX, mWidth);
+ y = (int32_t)mod(y+(int32_t)mOriginY, mHeight);
+ return ((mMem[(x >> 3) + y * mRowBytes] >> (x & 7)) & 1) != 0;
+ }
+ void set(int32_t x, int32_t y)
+ {
+ x = (int32_t)mod(x+(int32_t)mOriginX, mWidth);
+ y = (int32_t)mod(y+(int32_t)mOriginY, mHeight);
+ mMem[(x >> 3) + y * mRowBytes] |= 1 << (x & 7);
+ }
+ void reset(int32_t x, int32_t y)
+ {
+ x = (int32_t)mod(x+(int32_t)mOriginX, mWidth);
+ y = (int32_t)mod(y+(int32_t)mOriginY, mHeight);
+ mMem[(x >> 3) + y * mRowBytes] &= ~(1 << (x & 7));
+ }
+
+private:
+
+ void create_internal(uint32_t width, uint32_t height, bool* val)
+ {
+ delete [] mMem;
+ mRowBytes = (width + 7) >> 3;
+ const uint32_t bytes = mRowBytes * height;
+ if (bytes == 0)
+ {
+ mWidth = mHeight = 0;
+ mMem = NULL;
+ return;
+ }
+ mWidth = width;
+ mHeight = height;
+ mMem = new uint8_t[bytes];
+ mOriginX = 0;
+ mOriginY = 0;
+ if (val)
+ {
+ clear(*val);
+ }
+ }
+
+ uint8_t* mMem;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mRowBytes;
+ uint32_t mOriginX;
+ uint32_t mOriginY;
+};
+
+
+PX_INLINE int32_t taxicabSine(int32_t i)
+{
+ // 0 1 1 1 0 -1 -1 -1
+ return (int32_t)((0x01A9 >> ((i & 7) << 1)) & 3) - 1;
+}
+
+// Only looks at x and y components
+PX_INLINE bool directionsXYOrderedCCW(const physx::PxVec3& d0, const physx::PxVec3& d1, const physx::PxVec3& d2)
+{
+ const bool ccw02 = crossZ(d0, d2) > 0.0f;
+ const bool ccw01 = crossZ(d0, d1) > 0.0f;
+ const bool ccw21 = crossZ(d2, d1) > 0.0f;
+ return ccw02 ? ccw01 && ccw21 : ccw01 || ccw21;
+}
+
+PX_INLINE float compareTraceSegmentToLineSegment(const std::vector<POINT2D>& trace, int _start, int delta, float distThreshold, uint32_t width, uint32_t height, bool hasBorder)
+{
+ if (delta < 2)
+ {
+ return 0.0f;
+ }
+
+ const uint32_t size = trace.size();
+
+ uint32_t start = (uint32_t)_start, end = (uint32_t)(_start + delta) % size;
+
+ const bool startIsOnBorder = hasBorder && (trace[start].x == -1 || trace[start].x == (int)width || trace[start].y == -1 || trace[start].y == (int)height);
+ const bool endIsOnBorder = hasBorder && (trace[end].x == -1 || trace[end].x == (int)width || trace[end].y == -1 || trace[end].y == (int)height);
+
+ if (startIsOnBorder || endIsOnBorder)
+ {
+ if ((trace[start].x == -1 && trace[end].x == -1) ||
+ (trace[start].y == -1 && trace[end].y == -1) ||
+ (trace[start].x == (int)width && trace[end].x == (int)width) ||
+ (trace[start].y == (int)height && trace[end].y == (int)height))
+ {
+ return 0.0f;
+ }
+ return PX_MAX_F32;
+ }
+
+ physx::PxVec3 orig((float)trace[start].x, (float)trace[start].y, 0);
+ physx::PxVec3 dest((float)trace[end].x, (float)trace[end].y, 0);
+ physx::PxVec3 dir = dest - orig;
+
+ dir.normalize();
+
+ float aveError = 0.0f;
+
+ for (;;)
+ {
+ if (++start >= size)
+ {
+ start = 0;
+ }
+ if (start == end)
+ {
+ break;
+ }
+ physx::PxVec3 testDisp((float)trace[start].x, (float)trace[start].y, 0);
+ testDisp -= orig;
+ aveError += (float)(physx::PxAbs(testDisp.x * dir.y - testDisp.y * dir.x) >= distThreshold);
+ }
+
+ aveError /= delta - 1;
+
+ return aveError;
+}
+
+// Segment i starts at vi and ends at vi+ei
+// Tests for overlap in segments' projection onto xy plane
+// Returns distance between line segments. (Negative value indicates overlap.)
+PX_INLINE float segmentsIntersectXY(const physx::PxVec3& v0, const physx::PxVec3& e0, const physx::PxVec3& v1, const physx::PxVec3& e1)
+{
+ const physx::PxVec3 dv = v1 - v0;
+
+ physx::PxVec3 d0 = e0;
+ d0.normalize();
+ physx::PxVec3 d1 = e1;
+ d1.normalize();
+
+ const float c10 = crossZ(dv, d0);
+ const float d10 = crossZ(e1, d0);
+
+ float a1 = physx::PxAbs(c10);
+ float b1 = physx::PxAbs(c10 + d10);
+
+ if (c10 * (c10 + d10) < 0.0f)
+ {
+ if (a1 < b1)
+ {
+ a1 = -a1;
+ }
+ else
+ {
+ b1 = -b1;
+ }
+ }
+
+ const float c01 = crossZ(d1, dv);
+ const float d01 = crossZ(e0, d1);
+
+ float a2 = physx::PxAbs(c01);
+ float b2 = physx::PxAbs(c01 + d01);
+
+ if (c01 * (c01 + d01) < 0.0f)
+ {
+ if (a2 < b2)
+ {
+ a2 = -a2;
+ }
+ else
+ {
+ b2 = -b2;
+ }
+ }
+
+ return physx::PxMax(physx::PxMin(a1, b1), physx::PxMin(a2, b2));
+}
+
+// If point projects onto segment, returns true and proj is set to a
+// value in the range [0,1], indicating where along the segment (from v0 to v1)
+// the projection lies, and dist2 is set to the distance squared from point to
+// the line segment. Otherwise, returns false.
+// Note, if v1 = v0, then the function returns true with proj = 0.
+PX_INLINE bool projectOntoSegmentXY(float& proj, float& dist2, const physx::PxVec3& point, const physx::PxVec3& v0, const physx::PxVec3& v1, float margin)
+{
+ const physx::PxVec3 seg = v1 - v0;
+ const physx::PxVec3 x = point - v0;
+ const float seg2 = dotXY(seg, seg);
+ const float d = dotXY(x, seg);
+
+ if (d < 0.0f || d > seg2)
+ {
+ return false;
+ }
+
+ const float margin2 = margin * margin;
+
+ const float p = seg2 > 0.0f ? d / seg2 : 0.0f;
+ const float lineDist2 = d * p;
+
+ if (lineDist2 < margin2)
+ {
+ return false;
+ }
+
+ const float pPrime = 1.0f - p;
+ const float dPrime = seg2 - d;
+ const float lineDistPrime2 = dPrime * pPrime;
+
+ if (lineDistPrime2 < margin2)
+ {
+ return false;
+ }
+
+ proj = p;
+ dist2 = dotXY(x, x) - lineDist2;
+ return true;
+}
+
+PX_INLINE bool isOnBorder(const physx::PxVec3& v, uint32_t width, uint32_t height)
+{
+ return v.x < -0.5f || v.x >= width - 0.5f || v.y < -0.5f || v.y >= height - 0.5f;
+}
+
+static void createCutout(Nv::Blast::Cutout& cutout, const std::vector<POINT2D>& trace, float segmentationErrorThreshold, float snapThreshold, uint32_t width, uint32_t height, bool hasBorder)
+{
+ cutout.vertices.clear();
+
+ const uint32_t traceSize = trace.size();
+
+ if (traceSize == 0)
+ {
+ return; // Nothing to do
+ }
+
+ uint32_t size = traceSize;
+
+ std::vector<int> vertexIndices;
+
+ const float pixelCenterOffset = hasBorder ? 0.5f : 0.0f;
+
+ // Find best segment
+ uint32_t start = 0;
+ uint32_t delta = 0;
+ for (uint32_t iStart = 0; iStart < size; ++iStart)
+ {
+ uint32_t iDelta = (size >> 1) + (size & 1);
+ for (; iDelta > 1; --iDelta)
+ {
+ float fit = compareTraceSegmentToLineSegment(trace, (int32_t)iStart, (int32_t)iDelta, CUTOUT_DISTANCE_THRESHOLD, width, height, hasBorder);
+ if (fit < segmentationErrorThreshold)
+ {
+ break;
+ }
+ }
+ if (iDelta > delta)
+ {
+ start = iStart;
+ delta = iDelta;
+ }
+ }
+ cutout.vertices.push_back(physx::PxVec3((float)trace[start].x + pixelCenterOffset, (float)trace[start].y + pixelCenterOffset, 0));
+
+ // Now complete the loop
+ while ((size -= delta) > 0)
+ {
+ start = (start + delta) % traceSize;
+ cutout.vertices.push_back(physx::PxVec3((float)trace[start].x + pixelCenterOffset, (float)trace[start].y + pixelCenterOffset, 0));
+ if (size == 1)
+ {
+ delta = 1;
+ break;
+ }
+ for (delta = size - 1; delta > 1; --delta)
+ {
+ float fit = compareTraceSegmentToLineSegment(trace, (int32_t)start, (int32_t)delta, CUTOUT_DISTANCE_THRESHOLD, width, height, hasBorder);
+ if (fit < segmentationErrorThreshold)
+ {
+ break;
+ }
+ }
+ }
+
+ const float snapThresh2 = square(snapThreshold);
+
+ // Use the snapThreshold to clean up
+ while ((size = cutout.vertices.size()) >= 4)
+ {
+ bool reduced = false;
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ const uint32_t i1 = (i + 1) % size;
+ const uint32_t i2 = (i + 2) % size;
+ const uint32_t i3 = (i + 3) % size;
+ physx::PxVec3& v0 = cutout.vertices[i];
+ physx::PxVec3& v1 = cutout.vertices[i1];
+ physx::PxVec3& v2 = cutout.vertices[i2];
+ physx::PxVec3& v3 = cutout.vertices[i3];
+ const physx::PxVec3 d0 = v1 - v0;
+ const physx::PxVec3 d1 = v2 - v1;
+ const physx::PxVec3 d2 = v3 - v2;
+ const float den = crossZ(d0, d2);
+ if (den != 0)
+ {
+ const float recipDen = 1.0f / den;
+ const float s0 = crossZ(d1, d2) * recipDen;
+ const float s2 = crossZ(d0, d1) * recipDen;
+ if (s0 >= 0 || s2 >= 0)
+ {
+ if (d0.magnitudeSquared()*s0* s0 <= snapThresh2 && d2.magnitudeSquared()*s2* s2 <= snapThresh2)
+ {
+ v1 += d0 * s0;
+
+ //uint32_t index = (uint32_t)(&v2 - cutout.vertices.begin());
+ cutout.vertices.erase(cutout.vertices.begin() + std::distance(cutout.vertices.data(), &v2));
+
+ reduced = true;
+ break;
+ }
+ }
+ }
+ }
+ if (!reduced)
+ {
+ break;
+ }
+ }
+}
+
+static void splitTJunctions(Nv::Blast::CutoutSetImpl& cutoutSet, float threshold)
+{
+ // Set bounds reps
+ std::vector<BoundsRep> bounds;
+ std::vector<CutoutVert> cutoutMap; // maps bounds # -> ( cutout #, vertex # ).
+ std::vector<IntPair> overlaps;
+
+ const float distThreshold2 = threshold * threshold;
+
+ // Split T-junctions
+ uint32_t edgeCount = 0;
+ for (uint32_t i = 0; i < cutoutSet.cutouts.size(); ++i)
+ {
+ edgeCount += cutoutSet.cutouts[i].vertices.size();
+ }
+
+ bounds.resize(edgeCount);
+ cutoutMap.resize(edgeCount);
+
+ edgeCount = 0;
+ for (uint32_t i = 0; i < cutoutSet.cutouts.size(); ++i)
+ {
+ Nv::Blast::Cutout& cutout = cutoutSet.cutouts[i];
+ const uint32_t cutoutSize = cutout.vertices.size();
+ for (uint32_t j = 0; j < cutoutSize; ++j)
+ {
+ bounds[edgeCount].aabb.include(cutout.vertices[j]);
+ bounds[edgeCount].aabb.include(cutout.vertices[(j + 1) % cutoutSize]);
+ PX_ASSERT(!bounds[edgeCount].aabb.isEmpty());
+ bounds[edgeCount].aabb.fattenFast(threshold);
+ cutoutMap[edgeCount].set((int32_t)i, (int32_t)j);
+ ++edgeCount;
+ }
+ }
+
+ // Find bounds overlaps
+ if (bounds.size() > 0)
+ {
+ boundsCalculateOverlaps(overlaps, Bounds3XY, &bounds[0], bounds.size(), sizeof(bounds[0]));
+ }
+
+ std::vector<NewVertex> newVertices;
+ for (uint32_t overlapIndex = 0; overlapIndex < overlaps.size(); ++overlapIndex)
+ {
+ const IntPair& mapPair = overlaps[overlapIndex];
+ const CutoutVert& seg0Map = cutoutMap[(uint32_t)mapPair.i0];
+ const CutoutVert& seg1Map = cutoutMap[(uint32_t)mapPair.i1];
+
+ if (seg0Map.cutoutIndex == seg1Map.cutoutIndex)
+ {
+ // Only split based on vertex/segment junctions from different cutouts
+ continue;
+ }
+
+ NewVertex newVertex;
+ float dist2 = 0;
+
+ const Nv::Blast::Cutout& cutout0 = cutoutSet.cutouts[(uint32_t)seg0Map.cutoutIndex];
+ const uint32_t cutoutSize0 = cutout0.vertices.size();
+ const Nv::Blast::Cutout& cutout1 = cutoutSet.cutouts[(uint32_t)seg1Map.cutoutIndex];
+ const uint32_t cutoutSize1 = cutout1.vertices.size();
+
+ if (projectOntoSegmentXY(newVertex.edgeProj, dist2, cutout0.vertices[(uint32_t)seg0Map.vertIndex], cutout1.vertices[(uint32_t)seg1Map.vertIndex],
+ cutout1.vertices[(uint32_t)(seg1Map.vertIndex + 1) % cutoutSize1], 0.25f))
+ {
+ if (dist2 <= distThreshold2)
+ {
+ newVertex.vertex = seg1Map;
+ newVertices.push_back(newVertex);
+ }
+ }
+
+ if (projectOntoSegmentXY(newVertex.edgeProj, dist2, cutout1.vertices[(uint32_t)seg1Map.vertIndex], cutout0.vertices[(uint32_t)seg0Map.vertIndex],
+ cutout0.vertices[(uint32_t)(seg0Map.vertIndex + 1) % cutoutSize0], 0.25f))
+ {
+ if (dist2 <= distThreshold2)
+ {
+ newVertex.vertex = seg0Map;
+ newVertices.push_back(newVertex);
+ }
+ }
+ }
+
+ if (newVertices.size())
+ {
+ // Sort new vertices
+ qsort(newVertices.data(), newVertices.size(), sizeof(NewVertex), compareNewVertices);
+
+ // Insert new vertices
+ uint32_t lastCutoutIndex = 0xFFFFFFFF;
+ uint32_t lastVertexIndex = 0xFFFFFFFF;
+ float lastProj = 1.0f;
+ for (uint32_t newVertexIndex = newVertices.size(); newVertexIndex--;)
+ {
+ const NewVertex& newVertex = newVertices[newVertexIndex];
+ if (newVertex.vertex.cutoutIndex != (int32_t)lastCutoutIndex)
+ {
+ lastCutoutIndex = (uint32_t)newVertex.vertex.cutoutIndex;
+ lastVertexIndex = 0xFFFFFFFF;
+ }
+ if (newVertex.vertex.vertIndex != (int32_t)lastVertexIndex)
+ {
+ lastVertexIndex = (uint32_t)newVertex.vertex.vertIndex;
+ lastProj = 1.0f;
+ }
+ Nv::Blast::Cutout& cutout = cutoutSet.cutouts[(uint32_t)newVertex.vertex.cutoutIndex];
+ const float proj = lastProj > 0.0f ? newVertex.edgeProj / lastProj : 0.0f;
+ const physx::PxVec3 pos = (1.0f - proj) * cutout.vertices[(uint32_t)newVertex.vertex.vertIndex]
+ + proj * cutout.vertices[(uint32_t)(newVertex.vertex.vertIndex + 1) % cutout.vertices.size()];
+ cutout.vertices.push_back(physx::PxVec3());
+ for (uint32_t n = cutout.vertices.size(); --n > (uint32_t)newVertex.vertex.vertIndex + 1;)
+ {
+ cutout.vertices[n] = cutout.vertices[n - 1];
+ }
+ cutout.vertices[(uint32_t)newVertex.vertex.vertIndex + 1] = pos;
+ lastProj = newVertex.edgeProj;
+ }
+ }
+}
+
+
+static void mergeVertices(Nv::Blast::CutoutSetImpl& cutoutSet, float threshold, uint32_t width, uint32_t height)
+{
+ // Set bounds reps
+ uint32_t vertexCount = 0;
+ for (uint32_t i = 0; i < cutoutSet.cutouts.size(); ++i)
+ {
+ vertexCount += cutoutSet.cutouts[i].vertices.size();
+ }
+
+ std::vector<BoundsRep> bounds;
+ std::vector<CutoutVert> cutoutMap; // maps bounds # -> ( cutout #, vertex # ).
+ bounds.resize(vertexCount);
+ cutoutMap.resize(vertexCount);
+
+ vertexCount = 0;
+ for (uint32_t i = 0; i < cutoutSet.cutouts.size(); ++i)
+ {
+ Nv::Blast::Cutout& cutout = cutoutSet.cutouts[i];
+ for (uint32_t j = 0; j < cutout.vertices.size(); ++j)
+ {
+ physx::PxVec3& vertex = cutout.vertices[j];
+ physx::PxVec3 min(vertex.x - threshold, vertex.y - threshold, 0.0f);
+ physx::PxVec3 max(vertex.x + threshold, vertex.y + threshold, 0.0f);
+ bounds[vertexCount].aabb = physx::PxBounds3(min, max);
+ cutoutMap[vertexCount].set((int32_t)i, (int32_t)j);
+ ++vertexCount;
+ }
+ }
+
+ // Find bounds overlaps
+ std::vector<IntPair> overlaps;
+ if (bounds.size() > 0)
+ {
+ boundsCalculateOverlaps(overlaps, Bounds3XY, &bounds[0], bounds.size(), sizeof(bounds[0]));
+ }
+ uint32_t overlapCount = overlaps.size();
+
+ if (overlapCount == 0)
+ {
+ return;
+ }
+
+ // Sort by first index
+ qsort(overlaps.data(), overlapCount, sizeof(IntPair), IntPair::compare);
+
+ const float threshold2 = threshold * threshold;
+
+ std::vector<IntPair> pairs;
+
+ // Group by first index
+ std::vector<uint32_t> lookup;
+ createIndexStartLookup(lookup, 0, vertexCount, &overlaps.begin()->i0, overlapCount, sizeof(IntPair));
+ for (uint32_t i = 0; i < vertexCount; ++i)
+ {
+ const uint32_t start = lookup[i];
+ const uint32_t stop = lookup[i + 1];
+ if (start == stop)
+ {
+ continue;
+ }
+ const CutoutVert& cutoutVert0 = cutoutMap[(uint32_t)overlaps[start].i0];
+ const physx::PxVec3& vert0 = cutoutSet.cutouts[(uint32_t)cutoutVert0.cutoutIndex].vertices[(uint32_t)cutoutVert0.vertIndex];
+ const bool isOnBorder0 = !cutoutSet.periodic && isOnBorder(vert0, width, height);
+ for (uint32_t j = start; j < stop; ++j)
+ {
+ const CutoutVert& cutoutVert1 = cutoutMap[(uint32_t)overlaps[j].i1];
+ if (cutoutVert0.cutoutIndex == cutoutVert1.cutoutIndex)
+ {
+ // No pairs from the same cutout
+ continue;
+ }
+ const physx::PxVec3& vert1 = cutoutSet.cutouts[(uint32_t)cutoutVert1.cutoutIndex].vertices[(uint32_t)cutoutVert1.vertIndex];
+ const bool isOnBorder1 = !cutoutSet.periodic && isOnBorder(vert1, width, height);
+ if (isOnBorder0 != isOnBorder1)
+ {
+ // No border/non-border pairs
+ continue;
+ }
+ if ((vert0 - vert1).magnitudeSquared() > threshold2)
+ {
+ // Distance outside threshold
+ continue;
+ }
+ // A keeper. Keep a symmetric list
+ IntPair overlap = overlaps[j];
+ pairs.push_back(overlap);
+ const int32_t i0 = overlap.i0;
+ overlap.i0 = overlap.i1;
+ overlap.i1 = i0;
+ pairs.push_back(overlap);
+ }
+ }
+
+ // Sort by first index
+ qsort(pairs.data(), pairs.size(), sizeof(IntPair), IntPair::compare);
+
+ // For every vertex, only keep closest neighbor from each cutout
+ createIndexStartLookup(lookup, 0, vertexCount, &pairs.begin()->i0, pairs.size(), sizeof(IntPair));
+ for (uint32_t i = 0; i < vertexCount; ++i)
+ {
+ const uint32_t start = lookup[i];
+ const uint32_t stop = lookup[i + 1];
+ if (start == stop)
+ {
+ continue;
+ }
+ const CutoutVert& cutoutVert0 = cutoutMap[(uint32_t)pairs[start].i0];
+ const physx::PxVec3& vert0 = cutoutSet.cutouts[(uint32_t)cutoutVert0.cutoutIndex].vertices[(uint32_t)cutoutVert0.vertIndex];
+ uint32_t groupStart = start;
+ while (groupStart < stop)
+ {
+ uint32_t next = groupStart;
+ const CutoutVert& cutoutVert1 = cutoutMap[(uint32_t)pairs[next].i1];
+ int32_t currentOtherCutoutIndex = cutoutVert1.cutoutIndex;
+ const physx::PxVec3& vert1 = cutoutSet.cutouts[(uint32_t)currentOtherCutoutIndex].vertices[(uint32_t)cutoutVert1.vertIndex];
+ uint32_t keep = groupStart;
+ float minDist2 = (vert0 - vert1).magnitudeSquared();
+ while (++next < stop)
+ {
+ const CutoutVert& cutoutVertNext = cutoutMap[(uint32_t)pairs[next].i1];
+ if (currentOtherCutoutIndex != cutoutVertNext.cutoutIndex)
+ {
+ break;
+ }
+ const physx::PxVec3& vertNext = cutoutSet.cutouts[(uint32_t)cutoutVertNext.cutoutIndex].vertices[(uint32_t)cutoutVertNext.vertIndex];
+ const float dist2 = (vert0 - vertNext).magnitudeSquared();
+ if (dist2 < minDist2)
+ {
+ pairs[keep].set(-1, -1); // Invalidate
+ keep = next;
+ minDist2 = dist2;
+ }
+ else
+ {
+ pairs[next].set(-1, -1); // Invalidate
+ }
+ }
+ groupStart = next;
+ }
+ }
+
+ // Eliminate invalid pairs (compactify)
+ uint32_t pairCount = 0;
+ for (uint32_t i = 0; i < pairs.size(); ++i)
+ {
+ if (pairs[i].i0 >= 0 && pairs[i].i1 >= 0)
+ {
+ pairs[pairCount++] = pairs[i];
+ }
+ }
+ pairs.resize(pairCount);
+
+ // Snap points together
+ std::vector<bool> pinned(vertexCount, false);
+
+ for (uint32_t i = 0; i < pairCount; ++i)
+ {
+ const uint32_t i0 = (uint32_t)pairs[i].i0;
+ if (pinned[i0])
+ {
+ continue;
+ }
+ const CutoutVert& cutoutVert0 = cutoutMap[i0];
+ physx::PxVec3& vert0 = cutoutSet.cutouts[(uint32_t)cutoutVert0.cutoutIndex].vertices[(uint32_t)cutoutVert0.vertIndex];
+ const uint32_t i1 = (uint32_t)pairs[i].i1;
+ const CutoutVert& cutoutVert1 = cutoutMap[i1];
+ physx::PxVec3& vert1 = cutoutSet.cutouts[(uint32_t)cutoutVert1.cutoutIndex].vertices[(uint32_t)cutoutVert1.vertIndex];
+ const physx::PxVec3 disp = vert1 - vert0;
+ // Move and pin
+ pinned[i0] = true;
+ if (pinned[i1])
+ {
+ vert0 = vert1;
+ }
+ else
+ {
+ vert0 += 0.5f * disp;
+ vert1 = vert0;
+ pinned[i1] = true;
+ }
+ }
+}
+
+static void eliminateStraightAngles(Nv::Blast::CutoutSetImpl& cutoutSet)
+{
+ // Eliminate straight angles
+ for (uint32_t i = 0; i < cutoutSet.cutouts.size(); ++i)
+ {
+ Nv::Blast::Cutout& cutout = cutoutSet.cutouts[i];
+ uint32_t oldSize;
+ do
+ {
+ oldSize = cutout.vertices.size();
+ for (uint32_t j = 0; j < cutout.vertices.size();)
+ {
+// if( isOnBorder( cutout.vertices[j], width, height ) )
+// { // Don't eliminate border vertices
+// ++j;
+// continue;
+// }
+ if (perpendicularDistanceSquared(cutout.vertices, j) < CUTOUT_DISTANCE_EPS * CUTOUT_DISTANCE_EPS)
+ {
+ cutout.vertices.erase(cutout.vertices.begin() + j);
+ }
+ else
+ {
+ ++j;
+ }
+ }
+ }
+ while (cutout.vertices.size() != oldSize);
+ }
+}
+
+static void simplifyCutoutSetImpl(Nv::Blast::CutoutSetImpl& cutoutSet, float threshold, uint32_t width, uint32_t height)
+{
+ splitTJunctions(cutoutSet, 1.0f);
+ mergeVertices(cutoutSet, threshold, width, height);
+ eliminateStraightAngles(cutoutSet);
+}
+
+//static void cleanCutout(Nv::Blast::Cutout& cutout, uint32_t loopIndex, float tolerance)
+//{
+// Nv::Blast::ConvexLoop& loop = cutout.convexLoops[loopIndex];
+// const float tolerance2 = tolerance * tolerance;
+// uint32_t oldSize;
+// do
+// {
+// oldSize = loop.polyVerts.size();
+// uint32_t size = oldSize;
+// for (uint32_t i = 0; i < size; ++i)
+// {
+// Nv::Blast::PolyVert& v0 = loop.polyVerts[(i + size - 1) % size];
+// Nv::Blast::PolyVert& v1 = loop.polyVerts[i];
+// Nv::Blast::PolyVert& v2 = loop.polyVerts[(i + 1) % size];
+// if (perpendicularDistanceSquared(cutout.vertices[v0.index], cutout.vertices[v1.index], cutout.vertices[v2.index]) <= tolerance2)
+// {
+// loop.polyVerts.erase(loop.polyVerts.begin() + i);
+// --size;
+// --i;
+// }
+// }
+// }
+// while (loop.polyVerts.size() != oldSize);
+//}
+
+//static bool decomposeCutoutIntoConvexLoops(Nv::Blast::Cutout& cutout, float cleanupTolerance = 0.0f)
+//{
+// const uint32_t size = cutout.vertices.size();
+//
+// if (size < 3)
+// {
+// return false;
+// }
+//
+// // Initialize to one loop, which may not be convex
+// cutout.convexLoops.resize(1);
+// cutout.convexLoops[0].polyVerts.resize(size);
+//
+// // See if the winding is ccw:
+//
+// // Scale to normalized size to avoid overflows
+// physx::PxBounds3 bounds;
+// bounds.setEmpty();
+// for (uint32_t i = 0; i < size; ++i)
+// {
+// bounds.include(cutout.vertices[i]);
+// }
+// physx::PxVec3 center = bounds.getCenter();
+// physx::PxVec3 extent = bounds.getExtents();
+// if (extent[0] < PX_EPS_F32 || extent[1] < PX_EPS_F32)
+// {
+// return false;
+// }
+// const physx::PxVec3 scale(1.0f / extent[0], 1.0f / extent[1], 0.0f);
+//
+// // Find "area" (it will only be correct in sign!)
+// physx::PxVec3 prevV = (cutout.vertices[size - 1] - center).multiply(scale);
+// float area = 0.0f;
+// for (uint32_t i = 0; i < size; ++i)
+// {
+// const physx::PxVec3 v = (cutout.vertices[i] - center).multiply(scale);
+// area += crossZ(prevV, v);
+// prevV = v;
+// }
+//
+// if (physx::PxAbs(area) < PX_EPS_F32 * PX_EPS_F32)
+// {
+// return false;
+// }
+//
+// const bool ccw = area > 0.0f;
+//
+// for (uint32_t i = 0; i < size; ++i)
+// {
+// Nv::Blast::PolyVert& vert = cutout.convexLoops[0].polyVerts[i];
+// vert.index = (uint16_t)(ccw ? i : size - i - 1);
+// vert.flags = 0;
+// }
+//
+// const float cleanupTolerance2 = square(cleanupTolerance);
+//
+// // Find reflex vertices
+// for (uint32_t i = 0; i < cutout.convexLoops.size();)
+// {
+// Nv::Blast::ConvexLoop& loop = cutout.convexLoops[i];
+// const uint32_t loopSize = loop.polyVerts.size();
+// if (loopSize <= 3)
+// {
+// ++i;
+// continue;
+// }
+// uint32_t j = 0;
+// for (; j < loopSize; ++j)
+// {
+// const physx::PxVec3& v0 = cutout.vertices[loop.polyVerts[(j + loopSize - 1) % loopSize].index];
+// const physx::PxVec3& v1 = cutout.vertices[loop.polyVerts[j].index];
+// const physx::PxVec3& v2 = cutout.vertices[loop.polyVerts[(j + 1) % loopSize].index];
+// const physx::PxVec3 e0 = v1 - v0;
+// if (crossZ(e0, v2 - v1) < 0.0f)
+// {
+// // reflex
+// break;
+// }
+// }
+// if (j < loopSize)
+// {
+// // Find a vertex
+// float minLen2 = PX_MAX_F32;
+// float maxMinDist = -PX_MAX_F32;
+// uint32_t kToUse = 0;
+// uint32_t mToUse = 2;
+// bool cleanSliceFound = false; // A transversal is parallel with an edge
+// for (uint32_t k = 0; k < loopSize; ++k)
+// {
+// const physx::PxVec3& vkPrev = cutout.vertices[loop.polyVerts[(k + loopSize - 1) % loopSize].index];
+// const physx::PxVec3& vk = cutout.vertices[loop.polyVerts[k].index];
+// const physx::PxVec3& vkNext = cutout.vertices[loop.polyVerts[(k + 1) % loopSize].index];
+// const uint32_t mStop = k ? loopSize : loopSize - 1;
+// for (uint32_t m = k + 2; m < mStop; ++m)
+// {
+// const physx::PxVec3& vmPrev = cutout.vertices[loop.polyVerts[(m + loopSize - 1) % loopSize].index];
+// const physx::PxVec3& vm = cutout.vertices[loop.polyVerts[m].index];
+// const physx::PxVec3& vmNext = cutout.vertices[loop.polyVerts[(m + 1) % loopSize].index];
+// const physx::PxVec3 newEdge = vm - vk;
+// if (!directionsXYOrderedCCW(vk - vkPrev, newEdge, vkNext - vk) ||
+// !directionsXYOrderedCCW(vm - vmPrev, -newEdge, vmNext - vm))
+// {
+// continue;
+// }
+// const float len2 = newEdge.magnitudeSquared();
+// float minDist = PX_MAX_F32;
+// for (uint32_t l = 0; l < loopSize; ++l)
+// {
+// const uint32_t l1 = (l + 1) % loopSize;
+// if (l == k || l1 == k || l == m || l1 == m)
+// {
+// continue;
+// }
+// const physx::PxVec3& vl = cutout.vertices[loop.polyVerts[l].index];
+// const physx::PxVec3& vl1 = cutout.vertices[loop.polyVerts[l1].index];
+// const float dist = segmentsIntersectXY(vl, vl1 - vl, vk, newEdge);
+// if (dist < minDist)
+// {
+// minDist = dist;
+// }
+// }
+// if (minDist <= 0.0f)
+// {
+// if (minDist > maxMinDist)
+// {
+// maxMinDist = minDist;
+// kToUse = k;
+// mToUse = m;
+// }
+// }
+// else
+// {
+// if (perpendicularDistanceSquared(vkPrev, vk, vm) <= cleanupTolerance2 ||
+// perpendicularDistanceSquared(vk, vm, vmNext) <= cleanupTolerance2)
+// {
+// if (!cleanSliceFound)
+// {
+// minLen2 = len2;
+// kToUse = k;
+// mToUse = m;
+// }
+// else
+// {
+// if (len2 < minLen2)
+// {
+// minLen2 = len2;
+// kToUse = k;
+// mToUse = m;
+// }
+// }
+// cleanSliceFound = true;
+// }
+// else if (!cleanSliceFound && len2 < minLen2)
+// {
+// minLen2 = len2;
+// kToUse = k;
+// mToUse = m;
+// }
+// }
+// }
+// }
+// cutout.convexLoops.push_back(Nv::Blast::ConvexLoop());
+// Nv::Blast::ConvexLoop& newLoop = cutout.convexLoops.back();
+// Nv::Blast::ConvexLoop& oldLoop = cutout.convexLoops[i];
+// newLoop.polyVerts.resize(mToUse - kToUse + 1);
+// for (uint32_t n = 0; n <= mToUse - kToUse; ++n)
+// {
+// newLoop.polyVerts[n] = oldLoop.polyVerts[kToUse + n];
+// }
+// newLoop.polyVerts[mToUse - kToUse].flags = 1; // Mark this vertex (and edge that follows) as a split edge
+// oldLoop.polyVerts[kToUse].flags = 1; // Mark this vertex (and edge that follows) as a split edge
+// oldLoop.polyVerts.erase(oldLoop.polyVerts.begin() + kToUse + 1, oldLoop.polyVerts.begin() + (mToUse - (kToUse + 1)));
+// if (cleanupTolerance > 0.0f)
+// {
+// cleanCutout(cutout, i, cleanupTolerance);
+// cleanCutout(cutout, cutout.convexLoops.size() - 1, cleanupTolerance);
+// }
+// }
+// else
+// {
+// if (cleanupTolerance > 0.0f)
+// {
+// cleanCutout(cutout, i, cleanupTolerance);
+// }
+// ++i;
+// }
+// }
+//
+// return true;
+//}
+
+static void traceRegion(std::vector<POINT2D>& trace, Map2d<uint32_t>& regions, Map2d<uint8_t>& pathCounts, uint32_t regionIndex, const POINT2D& startPoint)
+{
+ POINT2D t = startPoint;
+ trace.clear();
+ trace.push_back(t);
+ ++pathCounts(t.x, t.y); // Increment path count
+ // Find initial path direction
+ int32_t dirN;
+ for (dirN = 1; dirN < 8; ++dirN) //TODO Should we start from dirN = 0?
+ {
+ const POINT2D t1 = POINT2D(t.x + taxicabSine(dirN + 2), t.y + taxicabSine(dirN));
+ if (regions(t1.x, t1.y) != regionIndex)
+ {
+ break;
+ }
+ }
+ bool done = false;
+ do
+ {
+ for (int32_t i = 1; i < 8; ++i) // Skip direction we just came from
+ {
+ --dirN;
+ const POINT2D t1 = POINT2D(t.x + taxicabSine(dirN + 2), t.y + taxicabSine(dirN));
+ if (regions(t1.x, t1.y) != regionIndex)
+ {
+ if (t1.x == trace[0].x && t1.y == trace[0].y)
+ {
+ done = true;
+ break;
+ }
+ trace.push_back(t1);
+ t = t1;
+ ++pathCounts(t.x, t.y); // Increment path count
+ dirN += 4;
+ break;
+ }
+ }
+ } while (!done && dirN >= 0);
+
+ //NvBlast GWD-399: Try to fix bad corners
+ int32_t sz = (int32_t)trace.size();
+ if (sz > 4)
+ {
+ struct CornerPixel
+ {
+ int32_t id;
+ POINT2D p;
+ CornerPixel(int32_t id, int32_t x, int32_t y) : id(id), p(x, y) { }
+ };
+ std::vector <CornerPixel> cp;
+ int32_t xb = 0, yb = 0; //bit buffer stores 1 if value do not changed from preview point and 0 otherwise (5 bits is used)
+ for (int32_t i = -4; i < sz; i++) //fill buffer with 4 elements from the end of trace
+ {
+ //idx, idx - 1, idx - 2, idx - 3 values with correct indexing to trace
+ int32_t idx = (sz + i) % sz, idx_ = (sz + i - 1) % sz, idx__ = (sz + i - 2) % sz, idx___ = (sz + i - 3) % sz;
+ //update buffer
+ xb <<= 1;
+ yb <<= 1;
+ xb += (trace[idx].x - trace[idx_].x) == 0;
+ yb += (trace[idx].y - trace[idx_].y) == 0;
+ //filter buffer for 11100-00111 or 00111-11100 corner patterns
+ if (i >= 0 && ((xb & 0x1F) ^ (yb & 0x1F)) == 0x1B)
+ {
+ if ((xb & 3) == 3)
+ {
+ if (((yb >> 3) & 3) == 3)
+ {
+ cp.push_back(CornerPixel(idx__, trace[idx].x, trace[idx___].y));
+ }
+ }
+ else if ((yb & 3) == 3)
+ {
+ if (((xb >> 3) & 3) == 3)
+ {
+ cp.push_back(CornerPixel(idx__, trace[idx___].x, trace[idx].y));
+ }
+ }
+ }
+ }
+ std::sort(cp.begin(), cp.end(), [](const CornerPixel& cp1, const CornerPixel& cp2) -> bool
+ {
+ return cp1.id > cp2.id;
+ });
+ for (auto it = cp.begin(); it != cp.end(); it++)
+ {
+ trace.insert(trace.begin() + it->id, it->p);
+ ++pathCounts(it->p.x, it->p.y);
+ }
+ }
+}
+
+void Nv::Blast::convertTracesToIncremental(std::vector< std::vector<POINT2D>* >& traces)
+{
+ uint32_t cutoutCount = traces.size();
+
+ std::map<POINT2D, std::map<uint32_t, uint32_t>> pointToTrace;
+ for (uint32_t i = 0; i < cutoutCount; i++)
+ {
+ auto& trace = *traces[i];
+ std::map<uint32_t, std::map<uint32_t, uint32_t>> segment;
+ std::map<int32_t, int32_t> newSegmentIndex;
+
+ for (uint32_t p = 0; p < trace.size(); p++)
+ {
+ if (pointToTrace.find(trace[p]) == pointToTrace.end())
+ {
+ pointToTrace[trace[p]] = std::map<uint32_t, uint32_t>();
+ }
+ pointToTrace[trace[p]][i] = p;
+ newSegmentIndex[p] = p;
+
+ for (auto it : pointToTrace[trace[p]])
+ {
+ if (it.first == i) continue;
+
+ if (segment.find(it.first) == segment.end())
+ {
+ segment[it.first] = std::map<uint32_t, uint32_t>();
+ }
+ segment[it.first][p] = it.second;
+ }
+ }
+
+ for (auto& s : segment)
+ {
+ if (s.second.size() < 2)
+ {
+ continue;
+ }
+ int32_t oldTraceSize = trace.size();
+ std::map<int32_t, int32_t> newTraceIndex;
+ for (int32_t p = 0; p < oldTraceSize; p++)
+ {
+ newTraceIndex[p] = p;
+ }
+
+ int32_t start = newSegmentIndex[s.second.begin()->first];
+ int32_t end = -1, prev = -1;
+ int32_t deleted = 0;
+ int32_t insertPoint = start;
+ //int32_t attachPoint = end;
+ int32_t otherStart = s.second.begin()->second, otherEnd = s.second.rbegin()->second, otherIncr = 0, otherPrev = -1;
+ for (auto ss : s.second)
+ {
+ if (physx::PxAbs(newTraceIndex[newSegmentIndex[ss.first]] - prev) > 1)
+ {
+ if (end >= start)
+ {
+ deleted += end - start + 1;
+ for (int32_t tp = start; tp < end; tp++)
+ {
+ newTraceIndex[tp] = -1;
+ }
+ for (int32_t tp = end; tp < oldTraceSize; tp++)
+ {
+ newTraceIndex[tp] -= end + 1 - start;
+ //pointToTrace[trace[tp]][i] -= end + 1 - start;
+ }
+ trace.erase(trace.begin() + start, trace.begin() + end + 1);
+
+ }
+ start = newTraceIndex[newSegmentIndex[ss.first]];
+ insertPoint = start;
+ //attachPoint = end;
+ otherStart = ss.second;
+ if (otherPrev >= 0)
+ {
+ otherEnd = otherPrev;
+ }
+ }
+ else
+ {
+ end = newTraceIndex[newSegmentIndex[ss.first]];
+ }
+ if (otherIncr == 0 && otherPrev >= 0 && physx::PxAbs((int32_t)ss.second - otherPrev) == 1)
+ {
+ otherIncr = otherPrev - (int32_t)ss.second;
+ }
+ prev = newTraceIndex[newSegmentIndex[ss.first]];
+ otherPrev = ss.second;
+ }
+ if (otherIncr == 0 && physx::PxAbs(otherPrev - (int32_t)s.second.begin()->second) == 1)
+ {
+ otherIncr = otherPrev - (int32_t)s.second.begin()->second;
+ }
+ NVBLAST_ASSERT(otherIncr != 0);
+ if (otherIncr == 0)
+ {
+ continue;
+ }
+ end = (end < start ? trace.size() : end + 1);
+ trace.erase(trace.begin() + start, trace.begin() + end);
+ for (int32_t tp = start; tp < end; tp++)
+ {
+ newTraceIndex[tp + deleted] = -1;
+ }
+
+ auto& otherTrace = *traces[s.first];
+ std::vector<POINT2D> insertSegment; insertSegment.reserve(otherTrace.size());
+ int shouldFinish = 2, pIndex = oldTraceSize;
+ while (shouldFinish != 0)
+ {
+ if (shouldFinish == 1)
+ {
+ shouldFinish--;
+ }
+ insertSegment.push_back(otherTrace[otherStart]);
+ auto itToOldPoint = pointToTrace[insertSegment.back()].find(i);
+ if (itToOldPoint != pointToTrace[insertSegment.back()].end())
+ {
+ newTraceIndex[itToOldPoint->second] = insertPoint + insertSegment.size() - 1;
+ }
+ else
+ {
+ newTraceIndex[pIndex++] = insertPoint + insertSegment.size() - 1;
+ }
+ pointToTrace[insertSegment.back()].erase(s.first);
+
+ otherStart = mod(otherStart + otherIncr, otherTrace.size());
+ if (otherStart == otherEnd)
+ {
+ shouldFinish--;
+ }
+ }
+
+ for (int32_t tp = end; tp < oldTraceSize; tp++)
+ {
+ if (newTraceIndex[tp] >= 0)
+ {
+ newTraceIndex[tp] = newTraceIndex[tp - 1] + 1;
+ }
+ }
+
+ trace.insert(trace.begin() + insertPoint, insertSegment.begin(), insertSegment.end());
+
+ for (auto nti : newTraceIndex)
+ {
+ if (nti.second >= 0)
+ {
+ pointToTrace[trace[nti.second]][i] = nti.second;
+ }
+ }
+ for (auto& nsi : newSegmentIndex)
+ {
+ if (nsi.second >= 0)
+ {
+ nsi.second = newTraceIndex[nsi.second];
+ }
+ }
+ }
+ }
+ //TODO: Investigate possible problem - merged trace splits to 2 traces (int, ext)
+}
+
+void Nv::Blast::createCutoutSet(Nv::Blast::CutoutSetImpl& cutoutSet, const uint8_t* pixelBuffer, uint32_t bufferWidth, uint32_t bufferHeight,
+ float segmentationErrorThreshold, float snapThreshold, bool periodic, bool expandGaps)
+{
+ cutoutSet.cutouts.clear();
+ cutoutSet.periodic = periodic;
+ cutoutSet.dimensions = physx::PxVec2((float)bufferWidth, (float)bufferHeight);
+
+ if (!periodic)
+ {
+ cutoutSet.dimensions[0] += 1.0f;
+ cutoutSet.dimensions[1] += 1.0f;
+ }
+
+ if (pixelBuffer == NULL || bufferWidth == 0 || bufferHeight == 0)
+ {
+ return;
+ }
+
+ const int borderPad = periodic ? 0 : 2; // Padded for borders if not periodic
+ const int originCoord = periodic ? 0 : 1;
+
+ BitMap map(bufferWidth + borderPad, bufferHeight + borderPad, 0);
+ map.setOrigin((uint32_t)originCoord, (uint32_t)originCoord);
+
+ bool hasBorder = false;
+ for (uint32_t y = 0; y < bufferHeight; ++y)
+ {
+ for (uint32_t x = 0; x < bufferWidth; ++x)
+ {
+ const uint32_t pix = 5033165 * (uint32_t)pixelBuffer[0] + 9898557 * (uint32_t)pixelBuffer[1] + 1845494 * (uint32_t)pixelBuffer[2];
+ pixelBuffer += 3;
+ if ((pix >> 28) != 0)
+ {
+ map.set((int32_t)x, (int32_t)y);
+ hasBorder = true;
+ }
+ }
+ }
+
+ // Add borders if not tiling
+ if (!periodic)
+ {
+ for (int32_t x = -1; x <= (int32_t)bufferWidth; ++x)
+ {
+ map.set(x, -1);
+ map.set(x, (int32_t)bufferHeight);
+ }
+ for (int32_t y = -1; y <= (int32_t)bufferHeight; ++y)
+ {
+ map.set(-1, y);
+ map.set((int32_t)bufferWidth, y);
+ }
+ }
+
+ // Now search for regions
+
+ // Create a region map
+ Map2d<uint32_t> regions(bufferWidth + borderPad, bufferHeight + borderPad, 0xFFFFFFFF); // Initially an invalid value
+ regions.setOrigin((uint32_t)originCoord, (uint32_t)originCoord);
+
+ // Create a path counting map
+ Map2d<uint8_t> pathCounts(bufferWidth + borderPad, bufferHeight + borderPad, 0);
+ pathCounts.setOrigin((uint32_t)originCoord, (uint32_t)originCoord);
+
+ // Bump path counts on borders
+ if (!periodic)
+ {
+ for (int32_t x = -1; x <= (int32_t)bufferWidth; ++x)
+ {
+ pathCounts(x, -1) = 1;
+ pathCounts(x, (int32_t)bufferHeight) = 1;
+ }
+ for (int32_t y = -1; y <= (int32_t)bufferHeight; ++y)
+ {
+ pathCounts(-1, y) = 1;
+ pathCounts((int32_t)bufferWidth, y) = 1;
+ }
+ }
+
+ std::vector<POINT2D> stack;
+ std::vector<POINT2D> traceStarts;
+ std::vector< std::vector<POINT2D>* > traces;
+
+ // Initial fill of region maps and path maps
+ for (int32_t y = 0; y < (int32_t)bufferHeight; ++y)
+ {
+ for (int32_t x = 0; x < (int32_t)bufferWidth; ++x)
+ {
+ if (map.read(x - 1, y) && !map.read(x, y))
+ {
+ // Found an empty spot next to a filled spot
+ POINT2D t(x - 1, y);
+ const uint32_t regionIndex = traceStarts.size();
+ traceStarts.push_back(t); // Save off initial point
+ traces.push_back(new std::vector<POINT2D>());
+ NVBLAST_ASSERT(traces.size() == traceStarts.size()); // This must be the same size as traceStarts
+ //traces.back() = (std::vector<POINT2D>*)PX_ALLOC(sizeof(std::vector<POINT2D>), PX_DEBUG_EXP("CutoutPoint2DSet"));
+ //new(traces.back()) std::vector<POINT2D>;
+ // Flood fill region map
+ std::set<uint64_t> visited;
+ stack.push_back(POINT2D(x, y));
+#define COMPRESS(x, y) (((uint64_t)(x) << 32) + (y))
+ visited.insert(COMPRESS(x, y));
+ do
+ {
+ const POINT2D s = stack.back();
+ stack.pop_back();
+ map.set(s.x, s.y);
+ regions(s.x, s.y) = regionIndex;
+ POINT2D n;
+ for (int32_t i = 0; i < 4; ++i)
+ {
+ const int32_t i0 = i & 1;
+ const int32_t i1 = (i >> 1) & 1;
+ n.x = s.x + i0 - i1;
+ n.y = s.y + i0 + i1 - 1;
+ if (!map.read(n.x, n.y) && visited.find(COMPRESS(n.x, n.y)) == visited.end())
+ {
+ stack.push_back(n);
+ visited.insert(COMPRESS(n.x, n.y));
+ }
+ }
+ } while (stack.size());
+#undef COMPRESS
+ // Trace region
+ PX_ASSERT(map.read(t.x, t.y));
+ std::vector<POINT2D>& trace = *traces[regionIndex];
+ traceRegion(trace, regions, pathCounts, regionIndex, t);
+ }
+ }
+ }
+
+ uint32_t cutoutCount = traces.size();
+
+ //find internal traces
+
+ // Now expand regions until the paths completely overlap
+ if (expandGaps)
+ {
+ bool somePathChanged;
+ int sanityCounter = 1000;
+ bool abort = false;
+ do
+ {
+ somePathChanged = false;
+ for (uint32_t i = 0; i < cutoutCount; ++i)
+ {
+ bool pathChanged = false;
+ std::vector<POINT2D>& trace = *traces[i];
+ for (uint32_t j = 0; j < trace.size(); ++j)
+ {
+ const POINT2D& t = trace[j];
+ if (pathCounts(t.x, t.y) == 1)
+ {
+ regions(t.x, t.y) = i;
+ pathChanged = true;
+ }
+ }
+ if (pathChanged)
+ {
+ // Recalculate cutout
+ // Decrement pathCounts
+ for (uint32_t j = 0; j < trace.size(); ++j)
+ {
+ const POINT2D& t = trace[j];
+ --pathCounts(t.x, t.y);
+ }
+ // Erase trace
+ // Calculate new start point
+ POINT2D& t = traceStarts[i];
+ int stop = (int)cutoutSet.dimensions.x;
+ while (regions(t.x, t.y) == i)
+ {
+ --t.x;
+ if (--stop < 0)
+ {
+ // There is an error; abort
+ break;
+ }
+ }
+ if (stop < 0)
+ {
+ // Release traces and abort
+ abort = true;
+ somePathChanged = false;
+ break;
+ }
+ traceRegion(trace, regions, pathCounts, i, t);
+ somePathChanged = true;
+ }
+ }
+ if (--sanityCounter <= 0)
+ {
+ abort = true;
+ break;
+ }
+ } while (somePathChanged);
+
+ if (abort)
+ {
+ for (uint32_t i = 0; i < cutoutCount; ++i)
+ {
+ traces[i]->~vector<POINT2D>();
+ delete traces[i];
+ }
+ cutoutCount = 0;
+ }
+
+ convertTracesToIncremental(traces);
+ }
+
+ // Create cutouts
+ cutoutSet.cutouts.resize(cutoutCount);
+ for (uint32_t i = 0; i < cutoutCount; ++i)
+ {
+ createCutout(cutoutSet.cutouts[i], *traces[i], segmentationErrorThreshold, snapThreshold, bufferWidth, bufferHeight, !cutoutSet.periodic);
+ }
+
+ simplifyCutoutSetImpl(cutoutSet, snapThreshold, bufferWidth, bufferHeight);
+
+ // Release traces
+ for (uint32_t i = 0; i < cutoutCount; ++i)
+ {
+ traces[i]->~vector<POINT2D>();
+ delete traces[i];
+ }
+
+ // Decompose each cutout in the set into convex loops
+ //uint32_t cutoutSetSize = 0;
+ //for (uint32_t i = 0; i < cutoutSet.cutouts.size(); ++i)
+ //{
+ // bool success = decomposeCutoutIntoConvexLoops(cutoutSet.cutouts[i]);
+ // if (success)
+ // {
+ // if (cutoutSetSize != i)
+ // {
+ // cutoutSet.cutouts[cutoutSetSize] = cutoutSet.cutouts[i];
+ // }
+ // ++cutoutSetSize;
+ // }
+ //}
+ //cutoutSet.cutouts.resize(cutoutSetSize);
+
+ //Check if single cutout spread to the whole area (no need to cutout then)
+ if (cutoutSet.cutouts.size() == 1 && (expandGaps || !hasBorder))
+ {
+ cutoutSet.cutouts.clear();
+ }
+}
+
+class Matrix22
+{
+public:
+ //! Default constructor
+ Matrix22()
+ {}
+
+ //! Construct from two base vectors
+ Matrix22(const physx::PxVec2& col0, const physx::PxVec2& col1)
+ : column0(col0), column1(col1)
+ {}
+
+ //! Construct from float[4]
+ explicit Matrix22(float values[]):
+ column0(values[0],values[1]),
+ column1(values[2],values[3])
+ {
+ }
+
+ //! Copy constructor
+ Matrix22(const Matrix22& other)
+ : column0(other.column0), column1(other.column1)
+ {}
+
+ //! Assignment operator
+ Matrix22& operator=(const Matrix22& other)
+ {
+ column0 = other.column0;
+ column1 = other.column1;
+ return *this;
+ }
+
+ //! Set to identity matrix
+ static Matrix22 createIdentity()
+ {
+ return Matrix22(physx::PxVec2(1,0), physx::PxVec2(0,1));
+ }
+
+ //! Set to zero matrix
+ static Matrix22 createZero()
+ {
+ return Matrix22(physx::PxVec2(0.0f), physx::PxVec2(0.0f));
+ }
+
+ //! Construct from diagonal, off-diagonals are zero.
+ static Matrix22 createDiagonal(const physx::PxVec2& d)
+ {
+ return Matrix22(physx::PxVec2(d.x,0.0f), physx::PxVec2(0.0f,d.y));
+ }
+
+
+ //! Get transposed matrix
+ Matrix22 getTranspose() const
+ {
+ const physx::PxVec2 v0(column0.x, column1.x);
+ const physx::PxVec2 v1(column0.y, column1.y);
+
+ return Matrix22(v0,v1);
+ }
+
+ //! Get the real inverse
+ Matrix22 getInverse() const
+ {
+ const float det = getDeterminant();
+ Matrix22 inverse;
+
+ if(det != 0)
+ {
+ const float invDet = 1.0f/det;
+
+ inverse.column0[0] = invDet * column1[1];
+ inverse.column0[1] = invDet * (-column0[1]);
+
+ inverse.column1[0] = invDet * (-column1[0]);
+ inverse.column1[1] = invDet * column0[0];
+
+ return inverse;
+ }
+ else
+ {
+ return createIdentity();
+ }
+ }
+
+ //! Get determinant
+ float getDeterminant() const
+ {
+ return column0[0] * column1[1] - column0[1] * column1[0];
+ }
+
+ //! Unary minus
+ Matrix22 operator-() const
+ {
+ return Matrix22(-column0, -column1);
+ }
+
+ //! Add
+ Matrix22 operator+(const Matrix22& other) const
+ {
+ return Matrix22( column0+other.column0,
+ column1+other.column1);
+ }
+
+ //! Subtract
+ Matrix22 operator-(const Matrix22& other) const
+ {
+ return Matrix22( column0-other.column0,
+ column1-other.column1);
+ }
+
+ //! Scalar multiplication
+ Matrix22 operator*(float scalar) const
+ {
+ return Matrix22(column0*scalar, column1*scalar);
+ }
+
+ //! Matrix vector multiplication (returns 'this->transform(vec)')
+ physx::PxVec2 operator*(const physx::PxVec2& vec) const
+ {
+ return transform(vec);
+ }
+
+ //! Matrix multiplication
+ Matrix22 operator*(const Matrix22& other) const
+ {
+ //Rows from this <dot> columns from other
+ //column0 = transform(other.column0) etc
+ return Matrix22(transform(other.column0), transform(other.column1));
+ }
+
+ // a <op>= b operators
+
+ //! Equals-add
+ Matrix22& operator+=(const Matrix22& other)
+ {
+ column0 += other.column0;
+ column1 += other.column1;
+ return *this;
+ }
+
+ //! Equals-sub
+ Matrix22& operator-=(const Matrix22& other)
+ {
+ column0 -= other.column0;
+ column1 -= other.column1;
+ return *this;
+ }
+
+ //! Equals scalar multiplication
+ Matrix22& operator*=(float scalar)
+ {
+ column0 *= scalar;
+ column1 *= scalar;
+ return *this;
+ }
+
+ //! Element access, mathematical way!
+ float operator()(unsigned int row, unsigned int col) const
+ {
+ return (*this)[col][(int)row];
+ }
+
+ //! Element access, mathematical way!
+ float& operator()(unsigned int row, unsigned int col)
+ {
+ return (*this)[col][(int)row];
+ }
+
+ // Transform etc
+
+ //! Transform vector by matrix, equal to v' = M*v
+ physx::PxVec2 transform(const physx::PxVec2& other) const
+ {
+ return column0*other.x + column1*other.y;
+ }
+
+ physx::PxVec2& operator[](unsigned int num) {return (&column0)[num];}
+ const physx::PxVec2& operator[](unsigned int num) const {return (&column0)[num];}
+
+ //Data, see above for format!
+
+ physx::PxVec2 column0, column1; //the two base vectors
+};
+
+PX_INLINE bool calculateUVMapping(const Nv::Blast::Triangle& triangle, physx::PxMat33& theResultMapping)
+{
+ physx::PxMat33 rMat;
+ physx::PxMat33 uvMat;
+ for (unsigned col = 0; col < 3; ++col)
+ {
+ auto v = triangle.getVertex(col);
+ rMat[col] = v.p;
+ uvMat[col] = physx::PxVec3(v.uv[0][0], v.uv[0][1], 1.0f);
+ }
+
+ if (uvMat.getDeterminant() == 0.0f)
+ {
+ return false;
+ }
+
+ theResultMapping = rMat*uvMat.getInverse();
+
+ return true;
+}
+
+//static bool calculateUVMapping(ExplicitHierarchicalMesh& theHMesh, const physx::PxVec3& theDir, physx::PxMat33& theResultMapping)
+//{
+// physx::PxVec3 cutoutDir( theDir );
+// cutoutDir.normalize( );
+//
+// const float cosineThreshold = physx::PxCos(3.141593f / 180); // 1 degree
+//
+// ExplicitRenderTriangle* triangleToUse = NULL;
+// float greatestCosine = -PX_MAX_F32;
+// float greatestArea = 0.0f; // for normals within the threshold
+// for ( uint32_t partIndex = 0; partIndex < theHMesh.partCount(); ++partIndex )
+// {
+// ExplicitRenderTriangle* theTriangles = theHMesh.meshTriangles( partIndex );
+// uint32_t triangleCount = theHMesh.meshTriangleCount( partIndex );
+// for ( uint32_t tIndex = 0; tIndex < triangleCount; ++tIndex )
+// {
+// ExplicitRenderTriangle& theTriangle = theTriangles[tIndex];
+// physx::PxVec3 theEdge1 = theTriangle.vertices[1].position - theTriangle.vertices[0].position;
+// physx::PxVec3 theEdge2 = theTriangle.vertices[2].position - theTriangle.vertices[0].position;
+// physx::PxVec3 theNormal = theEdge1.cross( theEdge2 );
+// float theArea = theNormal.normalize(); // twice the area, but that's ok
+//
+// if (theArea == 0.0f)
+// {
+// continue;
+// }
+//
+// const float cosine = cutoutDir.dot(theNormal);
+//
+// if (cosine < cosineThreshold)
+// {
+// if (cosine > greatestCosine && greatestArea == 0.0f)
+// {
+// greatestCosine = cosine;
+// triangleToUse = &theTriangle;
+// }
+// }
+// else
+// {
+// if (theArea > greatestArea)
+// {
+// greatestArea = theArea;
+// triangleToUse = &theTriangle;
+// }
+// }
+// }
+// }
+//
+// if (triangleToUse == NULL)
+// {
+// return false;
+// }
+//
+// return calculateUVMapping(*triangleToUse, theResultMapping);
+//}
+
+
+
+//bool calculateCutoutUVMapping(ExplicitHierarchicalMesh& hMesh, const physx::PxVec3& targetDirection, physx::PxMat33& theMapping)
+//{
+// return ::calculateUVMapping(hMesh, targetDirection, theMapping);
+//}
+
+//bool calculateCutoutUVMapping(const Nv::Blast::Triangle& targetDirection, physx::PxMat33& theMapping)
+//{
+// return ::calculateUVMapping(targetDirection, theMapping);
+//}
+
+
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringCutoutImpl.h b/sdk/extensions/authoring/source/NvBlastExtAuthoringCutoutImpl.h
new file mode 100644
index 0000000..6824291
--- /dev/null
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringCutoutImpl.h
@@ -0,0 +1,129 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, 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.
+*/
+
+
+#ifndef NVBLASTAUTHORINGFCUTOUTIMPL_H
+#define NVBLASTAUTHORINGFCUTOUTIMPL_H
+
+#include "NvBlastExtAuthoringCutout.h"
+#include <vector>
+#include "PxMat44.h" // TODO Should replace?
+
+namespace Nv
+{
+namespace Blast
+{
+
+struct PolyVert
+{
+ uint16_t index;
+ uint16_t flags;
+};
+
+struct ConvexLoop
+{
+ std::vector<PolyVert> polyVerts;
+};
+
+struct Cutout
+{
+ std::vector<physx::PxVec3> vertices;
+ std::vector<ConvexLoop> convexLoops;
+};
+
+struct POINT2D
+{
+ POINT2D() {}
+ POINT2D(int32_t _x, int32_t _y) : x(_x), y(_y) {}
+
+ int32_t x;
+ int32_t y;
+
+ bool operator==(const POINT2D& other) const
+ {
+ return x == other.x && y == other.y;
+ }
+ bool operator<(const POINT2D& other) const
+ {
+ if (x == other.x) return y < other.y;
+ return x < other.x;
+ }
+};
+
+void convertTracesToIncremental(std::vector< std::vector<POINT2D>* >& traces);
+
+struct CutoutSetImpl : public CutoutSet
+{
+ CutoutSetImpl() : periodic(false), dimensions(0.0f)
+ {
+ }
+
+ uint32_t getCutoutCount() const
+ {
+ return (uint32_t)cutouts.size();
+ }
+
+ uint32_t getCutoutVertexCount(uint32_t cutoutIndex) const
+ {
+ return (uint32_t)cutouts[cutoutIndex].vertices.size();
+ }
+ uint32_t getCutoutLoopCount(uint32_t cutoutIndex) const
+ {
+ return (uint32_t)cutouts[cutoutIndex].convexLoops.size();
+ }
+
+ const physx::PxVec3& getCutoutVertex(uint32_t cutoutIndex, uint32_t vertexIndex) const
+ {
+ return cutouts[cutoutIndex].vertices[vertexIndex];
+ }
+
+ uint32_t getCutoutLoopSize(uint32_t cutoutIndex, uint32_t loopIndex) const
+ {
+ return (uint32_t)cutouts[cutoutIndex].convexLoops[loopIndex].polyVerts.size();
+ }
+
+ uint32_t getCutoutLoopVertexIndex(uint32_t cutoutIndex, uint32_t loopIndex, uint32_t vertexNum) const
+ {
+ return cutouts[cutoutIndex].convexLoops[loopIndex].polyVerts[vertexNum].index;
+ }
+ uint32_t getCutoutLoopVertexFlags(uint32_t cutoutIndex, uint32_t loopIndex, uint32_t vertexNum) const
+ {
+ return cutouts[cutoutIndex].convexLoops[loopIndex].polyVerts[vertexNum].flags;
+ }
+ bool isPeriodic() const
+ {
+ return periodic;
+ }
+ const physx::PxVec2& getDimensions() const
+ {
+ return dimensions;
+ }
+
+ //void serialize(physx::PxFileBuf& stream) const;
+ //void deserialize(physx::PxFileBuf& stream);
+
+ void release()
+ {
+ delete this;
+ }
+
+ std::vector<Cutout> cutouts;
+ bool periodic;
+ physx::PxVec2 dimensions;
+};
+
+void createCutoutSet(Nv::Blast::CutoutSetImpl& cutoutSet, const uint8_t* pixelBuffer, uint32_t bufferWidth, uint32_t bufferHeight,
+ float segmentationErrorThreshold, float snapThreshold, bool periodic, bool expandGaps);
+
+
+} // namespace Blast
+} // namespace Nv
+
+#endif // ifndef NVBLASTAUTHORINGFCUTOUTIMPL_H
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.cpp
index 2b1cb65..9074b8c 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.cpp
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.cpp
@@ -23,12 +23,24 @@
#include "NvBlastExtAuthoringTriangulator.h"
#include "NvBlastExtAuthoringBooleanTool.h"
#include "NvBlastExtAuthoringAccelerator.h"
+#include "NvBlastExtAuthoringCutout.h"
#include "NvBlast.h"
#include "NvBlastGlobals.h"
#include "NvBlastExtAuthoringPerlinNoise.h"
#include <NvBlastAssert.h>
using namespace physx;
+#ifndef SAFE_DELETE
+#define SAFE_DELETE(p) \
+ { \
+ if(p) \
+ { \
+ delete (p); \
+ (p) = NULL; \
+ } \
+ }
+#endif
+
#define DEFAULT_BB_ACCELARATOR_RES 10
#define SLICING_INDEXER_OFFSET (1ll << 32)
@@ -421,6 +433,7 @@ int32_t FractureToolImpl::voronoiFracturing(uint32_t chunkId, uint32_t cellCount
if (resultMesh)
{
mChunkData.push_back(ChunkInfo());
+ mChunkData.back().isChanged = true;
mChunkData.back().isLeaf = true;
mChunkData.back().meshData = resultMesh;
mChunkData.back().parent = parentChunk;
@@ -601,6 +614,7 @@ int32_t FractureToolImpl::voronoiFracturing(uint32_t chunkId, uint32_t cellCount
{
mChunkData.push_back(ChunkInfo());
mChunkData.back().isLeaf = true;
+ mChunkData.back().isChanged = true;
mChunkData.back().meshData = resultMesh;
mChunkData.back().parent = parentChunk;
mChunkData.back().chunkId = mChunkIdCounter++;
@@ -627,9 +641,9 @@ 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)
+int32_t FractureToolImpl::slicing(uint32_t chunkId, const SlicingConfiguration& conf, bool replaceChunk, RandomGeneratorBase* rnd)
{
- if (conf.noiseAmplitude != 0)
+ if (conf.noise.amplitude != 0)
{
return slicingNoisy(chunkId, conf, replaceChunk, rnd);
}
@@ -676,6 +690,7 @@ int32_t FractureToolImpl::slicing(uint32_t chunkId, SlicingConfiguration conf, b
ChunkInfo ch;
ch.isLeaf = true;
+ ch.isChanged = true;
ch.parent = replaceChunk ? mChunkData[chunkIndex].parent : chunkId;
std::vector<ChunkInfo> xSlicedChunks;
std::vector<ChunkInfo> ySlicedChunks;
@@ -816,7 +831,7 @@ int32_t FractureToolImpl::slicing(uint32_t chunkId, SlicingConfiguration conf, b
return 0;
}
-int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration conf, bool replaceChunk, RandomGeneratorBase* rnd)
+int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, const SlicingConfiguration& conf, bool replaceChunk, RandomGeneratorBase* rnd)
{
if (replaceChunk && chunkId == 0)
{
@@ -860,6 +875,7 @@ int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration co
ChunkInfo ch;
ch.isLeaf = true;
+ ch.isChanged = true;
ch.parent = replaceChunk ? mChunkData[chunkIndex].parent : chunkId;
std::vector<ChunkInfo> xSlicedChunks;
std::vector<ChunkInfo> ySlicedChunks;
@@ -873,7 +889,7 @@ int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration co
{
PxVec3 randVect = PxVec3(2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1);
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);
+ slBox = getNoisyCuttingBoxPair(center, lDir, 40, noisyPartSize, conf.noise.surfaceResolution, mPlaneIndexerOffset + SLICING_INDEXER_OFFSET, conf.noise.amplitude, conf.noise.frequency, conf.noise.octaveNumber, rnd->getRandomValue(), mInteriorMaterialId);
// DummyAccelerator accel(mesh->getFacetCount());
SweepingAccelerator accel(mesh);
SweepingAccelerator dummy(slBox);
@@ -883,7 +899,7 @@ int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration co
{
xSlicedChunks.push_back(ch);
}
- inverseNormalAndSetIndices(slBox, -mPlaneIndexerOffset);
+ inverseNormalAndSetIndices(slBox, -(mPlaneIndexerOffset + SLICING_INDEXER_OFFSET));
++mPlaneIndexerOffset;
bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_INTERSECION());
Mesh* result = bTool.createNewMesh();
@@ -915,7 +931,7 @@ int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration co
PxVec3 randVect = PxVec3(2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1);
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);
+ slBox = getNoisyCuttingBoxPair(center, lDir, 40, noisyPartSize, conf.noise.surfaceResolution, mPlaneIndexerOffset + SLICING_INDEXER_OFFSET, conf.noise.amplitude, conf.noise.frequency, conf.noise.octaveNumber, rnd->getRandomValue(), mInteriorMaterialId);
// DummyAccelerator accel(mesh->getFacetCount());
SweepingAccelerator accel(mesh);
SweepingAccelerator dummy(slBox);
@@ -925,7 +941,7 @@ int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration co
{
ySlicedChunks.push_back(ch);
}
- inverseNormalAndSetIndices(slBox, -mPlaneIndexerOffset);
+ inverseNormalAndSetIndices(slBox, -(mPlaneIndexerOffset + SLICING_INDEXER_OFFSET));
++mPlaneIndexerOffset;
bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_INTERSECION());
Mesh* result = bTool.createNewMesh();
@@ -956,7 +972,7 @@ int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration co
{
PxVec3 randVect = PxVec3(2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1, 2 * rnd->getRandomValue() - 1);
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);
+ slBox = getNoisyCuttingBoxPair(center, lDir, 40, noisyPartSize, conf.noise.surfaceResolution, mPlaneIndexerOffset + SLICING_INDEXER_OFFSET, conf.noise.amplitude, conf.noise.frequency, conf.noise.octaveNumber, rnd->getRandomValue(), mInteriorMaterialId);
// DummyAccelerator accel(mesh->getFacetCount());
SweepingAccelerator accel(mesh);
SweepingAccelerator dummy(slBox);
@@ -968,7 +984,7 @@ int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration co
mChunkData.push_back(ch);
newlyCreatedChunksIds.push_back(ch.chunkId);
}
- inverseNormalAndSetIndices(slBox, -mPlaneIndexerOffset);
+ inverseNormalAndSetIndices(slBox, -(mPlaneIndexerOffset + SLICING_INDEXER_OFFSET));
++mPlaneIndexerOffset;
bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_INTERSECION());
Mesh* result = bTool.createNewMesh();
@@ -1008,8 +1024,184 @@ int32_t FractureToolImpl::slicingNoisy(uint32_t chunkId, SlicingConfiguration co
return 0;
}
+int32_t FractureToolImpl::cut(uint32_t chunkId, const physx::PxVec3& normal, const physx::PxVec3& point, const NoiseConfiguration& noise, bool replaceChunk, RandomGeneratorBase* rnd)
+{
+ if (replaceChunk && chunkId == 0)
+ {
+ return 1;
+ }
+
+ int32_t chunkIndex = getChunkIndex(chunkId);
+ if (chunkIndex == -1)
+ {
+ return 1;
+ }
+ if (!mChunkData[chunkIndex].isLeaf)
+ {
+ deleteAllChildrenOfChunk(chunkId);
+ }
+ chunkIndex = getChunkIndex(chunkId);
+
+ Mesh* mesh = new MeshImpl(*reinterpret_cast <MeshImpl*>(mChunkData[chunkIndex].meshData));
+ BooleanEvaluator bTool;
+
+ ChunkInfo ch;
+ ch.chunkId = -1;
+ ch.isLeaf = true;
+ ch.isChanged = true;
+ ch.parent = replaceChunk ? mChunkData[chunkIndex].parent : chunkId;
+ float noisyPartSize = 1.2f;
+
+ // Perform cut
+ Mesh* slBox = getNoisyCuttingBoxPair((point - mOffset) / mScaleFactor, normal, 40, noisyPartSize, noise.surfaceResolution, mPlaneIndexerOffset + SLICING_INDEXER_OFFSET, noise.amplitude, noise.frequency, noise.octaveNumber, rnd->getRandomValue(), mInteriorMaterialId);
+ SweepingAccelerator accel(mesh);
+ SweepingAccelerator dummy(slBox);
+ bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_DIFFERENCE());
+ ch.meshData = bTool.createNewMesh();
+ inverseNormalAndSetIndices(slBox, -(mPlaneIndexerOffset + SLICING_INDEXER_OFFSET));
+ ++mPlaneIndexerOffset;
+ bTool.performBoolean(mesh, slBox, &accel, &dummy, BooleanConfigurations::BOOLEAN_INTERSECION());
+ Mesh* result = bTool.createNewMesh();
+ delete slBox;
+ delete mesh;
+ mesh = result;
+
+ if (mesh == 0) //Return if it doesn't cut specified chunk
+ {
+ return 1;
+ }
+
+ if (!mChunkData[chunkIndex].isLeaf)
+ {
+ deleteAllChildrenOfChunk(chunkId);
+ }
+ chunkIndex = getChunkIndex(chunkId);
+
+ int32_t firstChunkId = -1;
+ if (ch.meshData != 0)
+ {
+ ch.chunkId = mChunkIdCounter++;
+ mChunkData.push_back(ch);
+ firstChunkId = ch.chunkId;
+ }
+ if (mesh != 0)
+ {
+ ch.chunkId = mChunkIdCounter++;
+ ch.meshData = mesh;
+ mChunkData.push_back(ch);
+ }
+
+ mChunkData[chunkIndex].isLeaf = false;
+ if (replaceChunk)
+ {
+ eraseChunk(chunkId);
+ }
+
+ if (mRemoveIslands && firstChunkId >= 0)
+ {
+ islandDetectionAndRemoving(firstChunkId);
+ if (mesh != 0)
+ {
+ islandDetectionAndRemoving(ch.chunkId);
+ }
+ }
+
+ return 0;
+}
+
+int32_t FractureToolImpl::cutout(uint32_t chunkId, CutoutConfiguration conf, bool replaceChunk, RandomGeneratorBase* /*rnd*/)
+{
+ if (replaceChunk && chunkId == 0)
+ {
+ return 1;
+ }
+
+ int32_t chunkIndex = getChunkIndex(chunkId);
+ if (chunkIndex == -1)
+ {
+ return 1;
+ }
+ if (!mChunkData[chunkIndex].isLeaf)
+ {
+ deleteAllChildrenOfChunk(chunkId);
+ }
+ chunkIndex = getChunkIndex(chunkId);
+ Nv::Blast::CutoutSet& cutoutSet = *conf.cutoutSet;
+
+ Mesh* mesh = new MeshImpl(*reinterpret_cast <MeshImpl*>(mChunkData[chunkIndex].meshData));
+ float extrusionLength = mesh->getBoundingBox().getDimensions().magnitude();
+ auto scale = conf.scale / mScaleFactor;
+ conf.transform.p = (conf.transform.p - mOffset) / mScaleFactor;
+ if (scale.x < 0.f || scale.y < 0.f)
+ {
+ scale = physx::PxVec2(extrusionLength);
+ }
+ if (conf.isRelativeTransform)
+ {
+ conf.transform.p += mesh->getBoundingBox().getCenter() / mScaleFactor;
+ }
+ float xDim = cutoutSet.getDimensions().x;
+ float yDim = cutoutSet.getDimensions().y;
+
+ BooleanEvaluator bTool;
+ ChunkInfo ch;
+ ch.isLeaf = true;
+ ch.isChanged = true;
+ ch.parent = replaceChunk ? mChunkData[chunkIndex].parent : chunkId;
+ std::vector<uint32_t> newlyCreatedChunksIds;
+
+ for (uint32_t c = 0; c < cutoutSet.getCutoutCount(); c++)
+ {
+ uint32_t vertCount = cutoutSet.getCutoutVertexCount(c);
+ std::vector<physx::PxVec3> verts(vertCount);
+ for (uint32_t v = 0; v < vertCount; v++)
+ {
+ auto vert = cutoutSet.getCutoutVertex(c, v);
+ vert.x = (vert.x / xDim - 0.5f) * scale.x;
+ vert.y = (vert.y / yDim - 0.5f) * scale.y;
+ verts[v] = vert;
+ }
+ Mesh* cutoutMesh = getCuttingCylinder(verts.size(), verts.data(), conf.transform, 2.f * extrusionLength, mPlaneIndexerOffset + SLICING_INDEXER_OFFSET, mInteriorMaterialId);
+ SweepingAccelerator accel(mesh);
+ SweepingAccelerator dummy(cutoutMesh);
+ bTool.performBoolean(mesh, cutoutMesh, &accel, &dummy, BooleanConfigurations::BOOLEAN_INTERSECION());
+ ch.meshData = bTool.createNewMesh();
+ if (ch.meshData != 0)
+ {
+ ch.chunkId = mChunkIdCounter++;
+ newlyCreatedChunksIds.push_back(ch.chunkId);
+ mChunkData.push_back(ch);
+ }
+ inverseNormalAndSetIndices(cutoutMesh, -(mPlaneIndexerOffset++ + SLICING_INDEXER_OFFSET));
+ bTool.performBoolean(mesh, cutoutMesh, &accel, &dummy, BooleanConfigurations::BOOLEAN_DIFFERENCE());
+ Mesh* result = bTool.createNewMesh();
+ delete mesh;
+ mesh = result;
+ if (mesh == nullptr)
+ {
+ break;
+ }
+ SAFE_DELETE(cutoutMesh)
+ }
+ SAFE_DELETE(mesh);
+ mChunkData[chunkIndex].isLeaf = false;
+ if (replaceChunk)
+ {
+ eraseChunk(chunkId);
+ }
+
+ if (mRemoveIslands)
+ {
+ for (auto chunkToCheck : newlyCreatedChunksIds)
+ {
+ islandDetectionAndRemoving(chunkToCheck);
+ }
+ }
+
+ return 0;
+}
int32_t FractureToolImpl::getChunkIndex(int32_t chunkId)
{
@@ -1133,6 +1325,7 @@ int32_t FractureToolImpl::setChunkMesh(const Mesh* meshInput, int32_t parentId)
chunk.meshData = new MeshImpl(*reinterpret_cast <const MeshImpl*>(meshInput));
chunk.parent = parentId;
chunk.isLeaf = true;
+ chunk.isChanged = true;
if ((size_t)parentId < mChunkData.size())
{
mChunkData[parentId].isLeaf = false;
@@ -1153,10 +1346,12 @@ int32_t FractureToolImpl::setChunkMesh(const Mesh* meshInput, int32_t parentId)
mesh->getBoundingBoxWritable().minimum = (mesh->getBoundingBox().minimum - mOffset) * (1.0f / mScaleFactor);
mesh->getBoundingBoxWritable().maximum = (mesh->getBoundingBox().maximum - mOffset) * (1.0f / mScaleFactor);
-
- for (uint32_t i = 0; i < mesh->getFacetCount(); ++i)
+ if (parentId == -1) // We are setting root mesh. Set all facets as boundary.
{
- mesh->getFacetWritable(i)->userData = 0; // Mark facet as initial boundary facet
+ for (uint32_t i = 0; i < mesh->getFacetCount(); ++i)
+ {
+ mesh->getFacetWritable(i)->userData = 0; // Mark facet as initial boundary facet
+ }
}
return chunk.chunkId;
@@ -1244,20 +1439,39 @@ bool FractureToolImpl::deleteAllChildrenOfChunk(int32_t chunkId)
void FractureToolImpl::finalizeFracturing()
{
- for (uint32_t i = 0; i < mChunkPostprocessors.size(); ++i)
+ std::vector<Triangulator* > oldTriangulators = mChunkPostprocessors;
+ std::map<int32_t, int32_t> chunkIdToTriangulator;
+ std::set<uint32_t> newChunkMask;
+ for (uint32_t i = 0; i < oldTriangulators.size(); ++i)
{
- delete mChunkPostprocessors[i];
+ chunkIdToTriangulator[oldTriangulators[i]->getParentChunkId()] = i;
}
+ mChunkPostprocessors.clear();
mChunkPostprocessors.resize(mChunkData.size());
+ newChunkMask.insert(0xffffffff); // To trigger masking mode, if newChunkMask will happen to be empty, all UVs will be updated.
for (uint32_t i = 0; i < mChunkPostprocessors.size(); ++i)
{
- mChunkPostprocessors[i] = new Triangulator();
+
+ auto it = chunkIdToTriangulator.find(mChunkData[i].chunkId);
+ if (mChunkData[i].isChanged || it == chunkIdToTriangulator.end())
+ {
+ if (it != chunkIdToTriangulator.end())
+ {
+ delete oldTriangulators[it->second];
+ oldTriangulators[it->second] = nullptr;
+ }
+ mChunkPostprocessors[i] = new Triangulator();
+ mChunkPostprocessors[i]->triangulate(mChunkData[i].meshData);
+ mChunkPostprocessors[i]->getParentChunkId() = mChunkData[i].chunkId;
+ newChunkMask.insert(mChunkData[i].chunkId);
+ mChunkData[i].isChanged = false;
+ }
+ else
+ {
+ mChunkPostprocessors[i] = oldTriangulators[it->second];
+ }
}
- for (uint32_t i = 0; i < mChunkPostprocessors.size(); ++i)
- {
- mChunkPostprocessors[i]->triangulate(mChunkData[i].meshData);
- }
std::vector<int32_t> badOnes;
for (uint32_t i = 0; i < mChunkPostprocessors.size(); ++i)
{
@@ -1279,7 +1493,7 @@ void FractureToolImpl::finalizeFracturing()
std::swap(mChunkData[badOnes[i]], mChunkData.back());
mChunkData.pop_back();
}
-
+ fitAllUvToRect(1.0f, newChunkMask);
}
uint32_t FractureToolImpl::getChunkCount() const
@@ -1287,9 +1501,9 @@ uint32_t FractureToolImpl::getChunkCount() const
return (uint32_t)mChunkData.size();
}
-const ChunkInfo& FractureToolImpl::getChunkInfo(int32_t chunkId)
+const ChunkInfo& FractureToolImpl::getChunkInfo(int32_t chunkIndex)
{
- return mChunkData[chunkId];
+ return mChunkData[chunkIndex];
}
uint32_t FractureToolImpl::getBaseMesh(int32_t chunkIndex, Triangle*& output)
@@ -1299,7 +1513,7 @@ uint32_t FractureToolImpl::getBaseMesh(int32_t chunkIndex, Triangle*& output)
{
return 0; // finalizeFracturing() should be called before getting mesh!
}
- auto baseMesh = mChunkPostprocessors[chunkIndex]->getBaseMesh();
+ auto& baseMesh = mChunkPostprocessors[chunkIndex]->getBaseMesh();
output = new Triangle[baseMesh.size()];
memcpy(output, baseMesh.data(), baseMesh.size() * sizeof(Triangle));
@@ -1316,6 +1530,29 @@ uint32_t FractureToolImpl::getBaseMesh(int32_t chunkIndex, Triangle*& output)
return baseMesh.size();
}
+uint32_t FractureToolImpl::updateBaseMesh(int32_t chunkIndex, Triangle* output)
+{
+ NVBLAST_ASSERT(mChunkPostprocessors.size() > 0);
+ if (mChunkPostprocessors.size() == 0)
+ {
+ return 0; // finalizeFracturing() should be called before getting mesh!
+ }
+ auto& baseMesh = mChunkPostprocessors[chunkIndex]->getBaseMesh();
+ memcpy(output, baseMesh.data(), baseMesh.size() * sizeof(Triangle));
+
+ /* Scale mesh back */
+
+ for (uint32_t i = 0; i < baseMesh.size(); ++i)
+ {
+ Triangle& triangle = output[i];
+ triangle.a.p = triangle.a.p * mScaleFactor + mOffset;
+ triangle.b.p = triangle.b.p * mScaleFactor + mOffset;
+ triangle.c.p = triangle.c.p * mScaleFactor + mOffset;
+ }
+ return baseMesh.size();
+}
+
+
float getVolume(std::vector<Triangle>& triangles)
{
float volume = 0.0f;
@@ -1682,9 +1919,116 @@ uint32_t FractureToolImpl::createNewChunk(uint32_t parent)
mChunkData.back().chunkId = mChunkIdCounter++;
mChunkData.back().meshData = nullptr;
mChunkData.back().isLeaf = false;
+ mChunkData.back().isChanged = true;
return mChunkData.size() - 1;
}
+
+void FractureToolImpl::fitUvToRect(float side, uint32_t chunk)
+{
+ int32_t index = getChunkIndex(chunk);
+ if (mChunkPostprocessors.empty()) // It seems finalize have not been called, call it here.
+ {
+ finalizeFracturing();
+ }
+ if (index == -1 || (int32_t)mChunkPostprocessors.size() <= index)
+ {
+ return; // We dont have such chunk tringulated;
+ }
+ PxBounds3 bnd;
+ bnd.setEmpty();
+
+ std::vector<Triangle>& ctrs = mChunkPostprocessors[index]->getBaseMesh();
+ std::vector<Triangle>& output = mChunkPostprocessors[index]->getBaseMesh();
+
+ for (uint32_t trn = 0; trn < ctrs.size(); ++trn)
+ {
+ if (ctrs[trn].userData == 0) continue;
+ bnd.include(PxVec3(ctrs[trn].a.uv[0].x, ctrs[trn].a.uv[0].y, 0.0f));
+ bnd.include(PxVec3(ctrs[trn].b.uv[0].x, ctrs[trn].b.uv[0].y, 0.0f));
+ bnd.include(PxVec3(ctrs[trn].c.uv[0].x, ctrs[trn].c.uv[0].y, 0.0f));
+ }
+
+ float xscale = side / (bnd.maximum.x - bnd.minimum.x);
+ float yscale = side / (bnd.maximum.y - bnd.minimum.y);
+ xscale = std::min(xscale, yscale); // To have uniform scaling
+
+ for (uint32_t trn = 0; trn < ctrs.size(); ++trn)
+ {
+ if (ctrs[trn].userData == 0) continue;
+ output[trn].a.uv[0].x = (ctrs[trn].a.uv[0].x - bnd.minimum.x) * xscale;
+ output[trn].b.uv[0].x = (ctrs[trn].b.uv[0].x - bnd.minimum.x) * xscale;
+ output[trn].c.uv[0].x = (ctrs[trn].c.uv[0].x - bnd.minimum.x) * xscale;
+
+ output[trn].a.uv[0].y = (ctrs[trn].a.uv[0].y - bnd.minimum.y) * xscale;
+ output[trn].b.uv[0].y = (ctrs[trn].b.uv[0].y - bnd.minimum.y) * xscale;
+ output[trn].c.uv[0].y = (ctrs[trn].c.uv[0].y - bnd.minimum.y) * xscale;
+ }
+}
+
+void FractureToolImpl::fitAllUvToRect(float side)
+{
+ std::set<uint32_t> mask;
+ fitAllUvToRect(side, mask);
+}
+
+void FractureToolImpl::fitAllUvToRect(float side, std::set<uint32_t>& mask)
+{
+ if (mChunkPostprocessors.empty()) // It seems finalize have not been called, call it here.
+ {
+ finalizeFracturing();
+ }
+ if (mChunkPostprocessors.empty())
+ {
+ return; // We dont have triangulated chunks.
+ }
+ PxBounds3 bnd;
+ bnd.setEmpty();
+
+ for (uint32_t chunk = 0; chunk < mChunkData.size(); ++chunk)
+ {
+ Mesh* m = mChunkData[chunk].meshData;
+ const Edge* edges = m->getEdges();
+ const Vertex* vertices = m->getVertices();
+
+ for (uint32_t trn = 0; trn < m->getFacetCount(); ++trn)
+ {
+ if (m->getFacet(trn)->userData == 0) continue;
+ for (uint32_t ei = 0; ei < m->getFacet(trn)->edgesCount; ++ei)
+ {
+ int32_t v1 = edges[m->getFacet(trn)->firstEdgeNumber + ei].s;
+ int32_t v2 = edges[m->getFacet(trn)->firstEdgeNumber + ei].e;
+ bnd.include(PxVec3(vertices[v1].uv[0].x, vertices[v1].uv[0].y, 0.0f));
+ bnd.include(PxVec3(vertices[v2].uv[0].x, vertices[v2].uv[0].y, 0.0f));
+ }
+ }
+ }
+ float xscale = side / (bnd.maximum.x - bnd.minimum.x);
+ float yscale = side / (bnd.maximum.y - bnd.minimum.y);
+ xscale = std::min(xscale, yscale); // To have uniform scaling
+
+ for (uint32_t chunk = 0; chunk < mChunkPostprocessors.size(); ++chunk)
+ {
+ if (!mask.empty() && mask.find(mChunkPostprocessors[chunk]->getParentChunkId()) == mask.end()) continue;
+ std::vector<Triangle>& ctrs = mChunkPostprocessors[chunk]->getBaseMeshNotFitted();
+ std::vector<Triangle>& output = mChunkPostprocessors[chunk]->getBaseMesh();
+
+ for (uint32_t trn = 0; trn < ctrs.size(); ++trn)
+ {
+ if (ctrs[trn].userData == 0) continue;
+ output[trn].a.uv[0].x = (ctrs[trn].a.uv[0].x - bnd.minimum.x) * xscale;
+ output[trn].b.uv[0].x = (ctrs[trn].b.uv[0].x - bnd.minimum.x) * xscale;
+ output[trn].c.uv[0].x = (ctrs[trn].c.uv[0].x - bnd.minimum.x) * xscale;
+
+ output[trn].a.uv[0].y = (ctrs[trn].a.uv[0].y - bnd.minimum.y) * xscale;
+ output[trn].b.uv[0].y = (ctrs[trn].b.uv[0].y - bnd.minimum.y) * xscale;
+ output[trn].c.uv[0].y = (ctrs[trn].c.uv[0].y - bnd.minimum.y) * xscale;
+ }
+ }
+}
+
+
+
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;
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.h b/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.h
index 0f6cdf0..72b655f 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.h
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringFractureToolImpl.h
@@ -14,6 +14,7 @@
#include "NvBlastExtAuthoringFractureTool.h"
#include "NvBlastExtAuthoringMesh.h"
#include <vector>
+#include <set>
namespace Nv
{
@@ -114,8 +115,8 @@ public:
private:
std::vector <physx::PxVec3> mGeneratedSites;
- const Mesh* mMesh;
- const Mesh* mStencil;
+ const Mesh* mMesh;
+ const Mesh* mStencil;
RandomGeneratorBase* mRnd;
SpatialAccelerator* mAccelerator;
};
@@ -224,7 +225,34 @@ public:
\return If 0, fracturing is successful.
*/
- int32_t slicing(uint32_t chunkId, SlicingConfiguration conf, bool replaceChunk, RandomGeneratorBase* rnd) override;
+ int32_t slicing(uint32_t chunkId, const SlicingConfiguration& conf, bool replaceChunk, RandomGeneratorBase* rnd) override;
+
+
+ /**
+ Cut chunk with plane.
+ \param[in] chunkId Chunk to fracture
+ \param[in] normal Plane normal
+ \param[in] position Point on plane
+ \param[in] noise Noise configuration for plane-chunk intersection, see NoiseConfiguration.
+ \param[in] replaceChunk if 'true', newly generated chunks will replace source chunk, if 'false', newly generated chunks will be at next depth level, source chunk will be parent for them.
+ Case replaceChunk == true && chunkId == 0 considered as wrong input parameters
+ \param[in] rnd User supplied random number generator
+
+ \return If 0, fracturing is successful.
+ */
+ int32_t cut(uint32_t chunkId, const physx::PxVec3& normal, const physx::PxVec3& position, const NoiseConfiguration& noise, bool replaceChunk, RandomGeneratorBase* rnd) override;
+
+ /**
+ Cutout fracture for specified chunk.
+ \param[in] chunkId Chunk to fracture
+ \param[in] conf Cutout parameters, see CutoutConfiguration.
+ \param[in] replaceChunk if 'true', newly generated chunks will replace source chunk, if 'false', newly generated chunks will be at next depth level, source chunk will be parent for them.
+ Case replaceChunk == true && chunkId == 0 considered as wrong input parameters
+ \param[in] rnd User supplied random number generator
+
+ \return If 0, fracturing is successful.
+ */
+ int32_t cutout(uint32_t chunkId, CutoutConfiguration conf, bool replaceChunk, RandomGeneratorBase* rnd) override;
/**
@@ -258,6 +286,15 @@ public:
uint32_t getBaseMesh(int32_t chunkIndex, Triangle*& output) override;
/**
+ Update chunk base mesh
+ \note Doesn't allocates output array, Triangle* output should be preallocated by user
+ \param[in] chunkIndex Chunk index
+ \param[out] output Array of triangles to be filled
+ \return number of triangles in base mesh
+ */
+ uint32_t updateBaseMesh(int32_t chunkIndex, Triangle* output) override;
+
+ /**
Return index of chunk with specified chunkId
\param[in] chunkId Chunk ID
\return Chunk index in internal buffer, if not exist -1 is returned.
@@ -324,13 +361,29 @@ public:
void uniteChunks(uint32_t maxAtLevel, uint32_t maxGroupSize) override;
+ /**
+ Rescale interior uv coordinates of given chunk to fit square of given size.
+ \param[in] side Size of square side
+ \param[in] chunkId Chunk ID for which UVs should be scaled.
+ */
+ void fitUvToRect(float side, uint32_t chunkId) override;
+
+ /**
+ Rescale interior uv coordinates of all existing chunks to fit square of given size, relative sizes will be preserved.
+ \param[in] side Size of square side
+ */
+ void fitAllUvToRect(float side) override;
+
+
+
private:
void eraseChunk(int32_t chunkId);
bool isAncestorForChunk(int32_t ancestorId, int32_t chunkId);
- int32_t slicingNoisy(uint32_t chunkId, SlicingConfiguration conf, bool replaceChunk, RandomGeneratorBase* rnd);
+ int32_t slicingNoisy(uint32_t chunkId, const 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);
-
+ void fitAllUvToRect(float side, std::set<uint32_t>& mask);
+
/**
Returns newly created chunk index in mChunkData.
*/
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.cpp
index 423a2ed..d5169b4 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.cpp
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.cpp
@@ -12,14 +12,18 @@
#include "NvBlastExtAuthoringMeshImpl.h"
#include "NvBlastExtAuthoringTypes.h"
-#include <string.h>
#include "NvBlastExtAuthoringPerlinNoise.h"
+#include <NvBlastAssert.h>
+#include "PxMath.h"
#include <cmath>
+#include <string.h>
using physx::PxVec2;
using physx::PxVec3;
using physx::PxBounds3;
+#define UV_SCALE 1.f
+
namespace Nv
{
namespace Blast
@@ -212,7 +216,7 @@ void MeshImpl::recalculateBoundingBox()
-void getTangents(PxVec3& normal, PxVec3& t1, PxVec3& t2)
+void getTangents(const PxVec3& normal, PxVec3& t1, PxVec3& t2)
{
if (std::abs(normal.z) < 0.9)
@@ -260,17 +264,17 @@ Mesh* getCuttingBox(const PxVec3& point, const PxVec3& normal, float size, int64
positions[7].n = -lNormal;
positions[0].uv[0] = PxVec2(0, 0);
- positions[1].uv[0] = PxVec2(10, 0);
+ positions[1].uv[0] = PxVec2(UV_SCALE, 0);
- positions[2].uv[0] = PxVec2(10, 10);
- positions[3].uv[0] = PxVec2(0, 10);
+ positions[2].uv[0] = PxVec2(UV_SCALE, UV_SCALE);
+ positions[3].uv[0] = PxVec2(0, UV_SCALE);
positions[4].uv[0] = PxVec2(0, 0);
- positions[5].uv[0] = PxVec2(10, 0);
+ positions[5].uv[0] = PxVec2(UV_SCALE, 0);
- positions[6].uv[0] = PxVec2(10, 10);
- positions[7].uv[0] = PxVec2(0, 10);
+ positions[6].uv[0] = PxVec2(UV_SCALE, UV_SCALE);
+ positions[7].uv[0] = PxVec2(0, UV_SCALE);
std::vector<Edge> edges;
@@ -422,12 +426,15 @@ Mesh* getNoisyCuttingBoxPair(const physx::PxVec3& point, const physx::PxVec3& no
PxVec3 t2d = -t2 * 2.0f * jaggedPlaneSize / resolution;
int32_t vrtId = 0;
+ float invRes = 1.f / resolution;
for (uint32_t i = 0; i < resolution + 1; ++i)
{
PxVec3 lcPosit = cPosit;
for (uint32_t j = 0; j < resolution + 1; ++j)
{
vertices[vrtId].p = lcPosit;
+ vertices[vrtId].uv[0].x = invRes * i * UV_SCALE;
+ vertices[vrtId].uv[0].y = invRes * j * UV_SCALE;
lcPosit += t1d;
vrtId++;
}
@@ -570,17 +577,17 @@ Mesh* getBigBox(const PxVec3& point, float size, int32_t interiorMaterialId)
positions[7].p = point + (t1 - t2 + normal) * size;
positions[0].uv[0] = PxVec2(0, 0);
- positions[1].uv[0] = PxVec2(10, 0);
+ positions[1].uv[0] = PxVec2(UV_SCALE, 0);
- positions[2].uv[0] = PxVec2(10, 10);
- positions[3].uv[0] = PxVec2(0, 10);
+ positions[2].uv[0] = PxVec2(UV_SCALE, UV_SCALE);
+ positions[3].uv[0] = PxVec2(0, UV_SCALE);
positions[4].uv[0] = PxVec2(0, 0);
- positions[5].uv[0] = PxVec2(10, 0);
+ positions[5].uv[0] = PxVec2(UV_SCALE, 0);
- positions[6].uv[0] = PxVec2(10, 10);
- positions[7].uv[0] = PxVec2(0, 10);
+ positions[6].uv[0] = PxVec2(UV_SCALE, UV_SCALE);
+ positions[7].uv[0] = PxVec2(0, UV_SCALE);
std::vector<Edge> edges;
@@ -627,5 +634,53 @@ Mesh* getBigBox(const PxVec3& point, float size, int32_t interiorMaterialId)
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()));
}
+Mesh* getCuttingCylinder(uint32_t pointCount, const physx::PxVec3* points, const physx::PxTransform& transform, float height, int64_t id, int32_t interiorMaterialId)
+{
+ std::vector<Vertex> positions(pointCount * 2);
+ std::vector<Edge> edges(pointCount * 6);
+ std::vector<Facet> facets(pointCount + 2);
+
+ for (uint32_t i = 0; i < pointCount; i++)
+ {
+ uint32_t i1 = i + pointCount;
+ uint32_t i2 = i1 + 1;
+ uint32_t i3 = i + 1;
+
+ auto& p0 = positions[i];
+ auto& p1 = positions[i1];
+ p0.n = p1.n = PxVec3(0, 0, 0);
+ p0.p = p1.p = points[i];
+ p0.p.z = -height;
+ p1.p.z = height;
+ p0.p = transform.transform(p0.p);
+ p1.p = transform.transform(p1.p);
+ p0.uv[0] = PxVec2(0.f, UV_SCALE * i / pointCount);
+ p1.uv[0] = PxVec2(UV_SCALE, UV_SCALE * i / pointCount);
+
+ int32_t edgeIdx = 4 * i;
+ edges[edgeIdx + 0] = Edge(i, i3);
+ edges[edgeIdx + 1] = Edge(i3, i2);
+ edges[edgeIdx + 2] = Edge(i2, i1);
+ edges[edgeIdx + 3] = Edge(i1, i);
+ facets[i] = Facet(edgeIdx, 4, interiorMaterialId, id, -1);
+
+ edges[5 * pointCount + i ] = Edge(i1, i2);
+ edges[5 * pointCount - i - 1] = Edge(i3, i);
+ }
+
+ int32_t edgeIdx = 4 * (pointCount - 1);
+ edges[edgeIdx + 0].e = 0;
+ edges[edgeIdx + 1].s = 0;
+ edges[edgeIdx + 1].e = pointCount;
+ edges[edgeIdx + 2].s = pointCount;
+
+ //top and bottom faces
+ edges[4 * pointCount].s = 0;
+ edges[6 * pointCount - 1].e = pointCount;
+ facets[pointCount + 0] = Facet(4 * pointCount, pointCount, interiorMaterialId, 0, -1);
+ facets[pointCount + 1] = Facet(5 * pointCount, pointCount, interiorMaterialId, 0, -1);
+ 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()));
+}
+
} // namespace Blast
} // namespace Nv
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.h b/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.h
index 512c3c8..1863f64 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.h
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringMeshImpl.h
@@ -206,10 +206,20 @@ Mesh* getNoisyCuttingBoxPair(const physx::PxVec3& point, const physx::PxVec3& no
/**
Inverses normals of cutting box and sets indices.
\param[in] mesh Cutting box mesh
- \param[in] id Cutting box ID
+ \param[in] id Cutting box ID
*/
void inverseNormalAndSetIndices(Mesh* mesh, int64_t id);
+/**
+ Create cutting cylinder (extrusion of specified loop) at some particular position.
+ \param[in] pointCount Number of points in loop
+ \param[in] points Array of points for loop
+ \param[in] transform Cutting cylinder transform
+ \param[in] height Cutting cylinder height
+ \param[in] id Cutting cylinder ID
+*/
+Mesh* getCuttingCylinder(uint32_t pointCount, const physx::PxVec3* points, const physx::PxTransform& transform, float height, int64_t id, int32_t interiorMaterialId);
+
} // namespace Blast
} // namespace Nv
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.cpp b/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.cpp
index cc2442c..e7cbb06 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.cpp
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.cpp
@@ -47,20 +47,21 @@
using physx::PxVec3;
using physx::PxVec2;
-#define TWO_VERTICES_THRESHOLD 1e-7
-
namespace Nv
{
namespace Blast
{
-
+NV_FORCE_INLINE bool compareTwoFloats(float a, float b)
+{
+ return std::abs(b - a) <= FLT_EPSILON * std::abs(b + a);
+}
NV_FORCE_INLINE bool compareTwoVertices(const PxVec3& a, const PxVec3& b)
{
- return std::abs(b.x - a.x) < TWO_VERTICES_THRESHOLD && std::abs(b.y - a.y) < TWO_VERTICES_THRESHOLD && std::abs(b.z - a.z) < TWO_VERTICES_THRESHOLD;
+ return compareTwoFloats(a.x, b.x) && compareTwoFloats(a.y, b.y) && compareTwoFloats(a.z, b.z);
}
NV_FORCE_INLINE bool compareTwoVertices(const PxVec2& a, const PxVec2& b)
{
- return std::abs(b.x - a.x) < TWO_VERTICES_THRESHOLD && std::abs(b.y - a.y) < TWO_VERTICES_THRESHOLD;
+ return compareTwoFloats(a.x, b.x) && compareTwoFloats(a.y, b.y);
}
NV_FORCE_INLINE float getRotation(const PxVec2& a, const PxVec2& b)
@@ -68,7 +69,7 @@ NV_FORCE_INLINE float getRotation(const PxVec2& a, const PxVec2& b)
return a.x * b.y - a.y * b.x;
}
-inline bool pointInside(PxVec2 a, PxVec2 b, PxVec2 c, PxVec2 pnt)
+NV_FORCE_INLINE bool pointInside(PxVec2 a, PxVec2 b, PxVec2 c, PxVec2 pnt)
{
if (compareTwoVertices(a, pnt) || compareTwoVertices(b, pnt) || compareTwoVertices(c, pnt))
{
@@ -471,7 +472,10 @@ NV_FORCE_INLINE void Triangulator::addEdgeIfValid(EdgeWithParent& ed)
mBaseMeshEdges[it->second].s = ed.s;
mBaseMeshEdges[it->second].e = ed.e;
}
- mBaseMeshEdges[it->second].s = NOT_VALID_VERTEX;
+ else
+ {
+ mBaseMeshEdges[it->second].s = NOT_VALID_VERTEX;
+ }
}
}
@@ -504,7 +508,7 @@ void Triangulator::prepare(const Mesh* mesh)
temp.push_back(mBaseMeshEdges[i]);
}
}
-
+ mBaseMeshEdges = temp;
}
void Triangulator::reset()
@@ -560,6 +564,7 @@ void Triangulator::triangulate(const Mesh* mesh)
mBaseMeshResultTriangles.back().smoothingGroup = mBaseMeshTriangles[i].smoothingGroup;
}
+ mBaseMeshUVFittedTriangles = mBaseMeshResultTriangles; // Uvs will be fitted later, in FractureTool.
computePositionedMapping();
}
diff --git a/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.h b/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.h
index c5cb5b1..70fc9f5 100644
--- a/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.h
+++ b/sdk/extensions/authoring/source/NvBlastExtAuthoringTriangulator.h
@@ -59,9 +59,15 @@ public:
*/
std::vector<Triangle>& getBaseMesh()
{
+ return mBaseMeshUVFittedTriangles;
+ }
+
+ std::vector<Triangle>& getBaseMeshNotFitted()
+ {
return mBaseMeshResultTriangles;
}
+
/**
\return Return array of TriangleIndexed of base mesh. Each TriangleIndexed contains index of corresponding vertex in internal vertex buffer.
*/
@@ -96,8 +102,12 @@ public:
*/
void reset();
+ int32_t& getParentChunkId() { return parentChunkId; };
+
private:
+ int32_t parentChunkId;
+
int32_t addVerticeIfNotExist(const Vertex& p);
void addEdgeIfValid(EdgeWithParent& ed);
@@ -128,6 +138,7 @@ private:
Final triangles
*/
std::vector<Triangle> mBaseMeshResultTriangles;
+ std::vector<Triangle> mBaseMeshUVFittedTriangles;
};
} // namespace Blast
diff --git a/sdk/extensions/exporter/include/NvBlastExtExporter.h b/sdk/extensions/exporter/include/NvBlastExtExporter.h
index a8bbb50..2b852af 100644
--- a/sdk/extensions/exporter/include/NvBlastExtExporter.h
+++ b/sdk/extensions/exporter/include/NvBlastExtExporter.h
@@ -46,6 +46,12 @@ namespace Blast
struct AuthoringResult;
struct CollisionHull;
+struct Materials
+{
+ const char* name;
+ const char* diffuse_tex;
+};
+
struct ExporterMeshData
{
NvBlastAsset* asset; //Blast asset
@@ -66,7 +72,7 @@ struct ExporterMeshData
uint32_t submeshCount; //Number of submeshes
- const char** submeshNames; //Equal to material names
+ Materials* submeshMats;
/**
@@ -227,6 +233,11 @@ public:
Save scene to file.
*/
virtual bool saveToFile(const char* assetName, const char* outputPath) = 0;
+
+ /**
+ Set material index for interior surface. By default new material will be created;
+ */
+ virtual void setInteriorIndex(int32_t index) = 0;
};
}
diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp
index 497c846..d465d65 100644
--- a/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp
+++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.cpp
@@ -69,6 +69,8 @@ FbxFileWriter::FbxFileWriter():
mRenderLayer = FbxDisplayLayer::Create(mScene, FbxUtils::getRenderGeometryLayerName().c_str());
mRenderLayer->Show.Set(true);
mRenderLayer->Color.Set(FbxDouble3(0.0f, 1.0f, 0.0f));
+
+ mInteriorIndex = -1;
}
void FbxFileWriter::release()
@@ -89,13 +91,19 @@ void FbxFileWriter::createMaterials(const ExporterMeshData& aResult)
for (uint32_t i = 0; i < aResult.submeshCount; ++i)
{
- FbxSurfacePhong* material = FbxSurfacePhong::Create(sdkManager.get(), aResult.submeshNames[i]);
+ FbxSurfacePhong* material = FbxSurfacePhong::Create(sdkManager.get(), aResult.submeshMats[i].name);
material->Diffuse.Set(FbxDouble3(float(rand()) / RAND_MAX , float(rand()) / RAND_MAX, float(rand()) / RAND_MAX));
material->DiffuseFactor.Set(1.0);
mMaterials.push_back(material);
}
}
+void FbxFileWriter::setInteriorIndex(int32_t index)
+{
+ mInteriorIndex = index;
+}
+
+
void FbxFileWriter::createMaterials(const AuthoringResult& aResult)
{
mMaterials.clear();
@@ -113,10 +121,19 @@ void FbxFileWriter::createMaterials(const AuthoringResult& aResult)
material->DiffuseFactor.Set(1.0);
mMaterials.push_back(material);
}
- FbxSurfacePhong* interiorMat = FbxSurfacePhong::Create(sdkManager.get(), "Interior_Material");
- interiorMat->Diffuse.Set(FbxDouble3(1.0, 0.0, 0.5));
- interiorMat->DiffuseFactor.Set(1.0);
- mMaterials.push_back(interiorMat);
+ if (mInteriorIndex == -1) // No material setted. Create new one.
+ {
+ FbxSurfacePhong* interiorMat = FbxSurfacePhong::Create(sdkManager.get(), "Interior_Material");
+ interiorMat->Diffuse.Set(FbxDouble3(1.0, 0.0, 0.5));
+ interiorMat->DiffuseFactor.Set(1.0);
+ mMaterials.push_back(interiorMat);
+ }
+ else
+ {
+ if (mInteriorIndex < 0) mInteriorIndex = 0;
+ if (static_cast<size_t>(mInteriorIndex) >= mMaterials.size()) mInteriorIndex = 0;
+ }
+
}
@@ -416,7 +433,7 @@ uint32_t FbxFileWriter::createChunkRecursive(uint32_t currentCpIdx, uint32_t chu
mesh->AddPolygon(currentCpIdx + cpIdx + 1);
mesh->AddPolygon(currentCpIdx + cpIdx + 2);
mesh->EndPolygon();
- int32_t material = (tri.materialId != MATERIAL_INTERIOR) ? ((tri.materialId < int32_t(mMaterials.size() - 1)) ? tri.materialId : 0) : int32_t(mMaterials.size() - 1);
+ int32_t material = (tri.materialId != MATERIAL_INTERIOR) ? ((tri.materialId < int32_t(mMaterials.size())) ? tri.materialId : 0) : ((mInteriorIndex == -1) ? int32_t(mMaterials.size() - 1): mInteriorIndex);
matElement->GetIndexArray().SetAt(polyCount, material);
if (smElement)
{
@@ -648,7 +665,7 @@ void FbxFileWriter::createChunkRecursiveNonSkinned(const std::string& meshName,
geUV->GetDirectArray().Add(uv);
}
mesh->EndPolygon();
- int32_t material = (geo.materialId != MATERIAL_INTERIOR) ? ((geo.materialId < int32_t(mMaterials.size() - 1))? geo.materialId : 0) : int32_t(mMaterials.size() - 1);
+ int32_t material = (geo.materialId != MATERIAL_INTERIOR) ? ((geo.materialId < int32_t(mMaterials.size()))? geo.materialId : 0) : ((mInteriorIndex == -1)? int32_t(mMaterials.size() - 1) : mInteriorIndex);
matElement->GetIndexArray().SetAt(polyCount, material);
if (smElement)
diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h
index f8490cb..b6d5859 100644
--- a/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h
+++ b/sdk/extensions/exporter/source/NvBlastExtExporterFbxWriter.h
@@ -84,11 +84,16 @@ public:
virtual bool appendMesh(const ExporterMeshData& meshData, const char* assetName, bool nonSkinned) override;
/**
- Save scene to file.
+ Save scene to file.
*/
virtual bool saveToFile(const char* assetName, const char* outputPath) override;
/**
+ Set interior material index.
+ */
+ virtual void setInteriorIndex(int32_t index) override;
+
+ /**
Set true if FBX should be saved in ASCII mode.
*/
bool bOutputFBXAscii;
@@ -129,6 +134,8 @@ private:
void generateSmoothingGroups(fbxsdk::FbxMesh* mesh, FbxSkin* skin);
void removeDuplicateControlPoints(fbxsdk::FbxMesh* mesh, FbxSkin* skin);
+
+ int32_t mInteriorIndex;
};
}
diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.cpp b/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.cpp
index fb6e9b3..9eba7d5 100644
--- a/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.cpp
+++ b/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.cpp
@@ -60,7 +60,18 @@ void ObjFileReader::loadFromFile(const char* filename)
std::vector<tinyobj::material_t> mats;
std::string err;
std::string mtlPath;
- bool ret = tinyobj::LoadObj(shapes, mats, err, filename);
+
+ int32_t lastDelimeter = strlen(filename);
+
+ while (lastDelimeter > 0 && filename[lastDelimeter] != '/' && filename[lastDelimeter] != '\\')
+ {
+ lastDelimeter--;
+ }
+ mtlPath = std::string(filename, filename + lastDelimeter);
+ mtlPath += '/';
+
+ bool ret = tinyobj::LoadObj(shapes, mats, err, filename, mtlPath.c_str());
+
// can't load?
if (!ret)
{
@@ -71,6 +82,18 @@ void ObjFileReader::loadFromFile(const char* filename)
std::cout << "Can load only one object per mesh" << std::endl;
}
+ if (!mats.empty())
+ {
+ if (mats.size() == 1 && mats[0].name == "")
+ {
+ mats[0].name = "Default";
+ }
+ for (uint32_t i = 0; i < mats.size(); ++i)
+ {
+ mMaterialNames.push_back(mats[i].name);
+ }
+ }
+
mVertexPositions.clear();
mVertexNormals.clear();
mVertexUv.clear();
@@ -93,6 +116,14 @@ void ObjFileReader::loadFromFile(const char* filename)
}
mIndices = shapes[0].mesh.indices;
+ mPerFaceMatId = shapes[0].mesh.material_ids;
+ for (uint32_t i = 0; i < mPerFaceMatId.size(); ++i)
+ {
+ if (mPerFaceMatId[i] == -1) // TinyOBJ loader sets ID to -1 when .mtl file not found. Set to default 0 material.
+ {
+ mPerFaceMatId[i] = 0;
+ }
+ }
}
diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.h b/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.h
index 8990287..2e8c955 100644
--- a/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.h
+++ b/sdk/extensions/exporter/source/NvBlastExtExporterObjReader.h
@@ -90,9 +90,9 @@ public:
virtual uint32_t* getIndexArray() override;
/**
- Get loaded per triangle material ids. Currently not supported by OBJ.
+ Get loaded per triangle material ids.
*/
- int32_t* getMaterialIds() override { return nullptr; };
+ int32_t* getMaterialIds() override { return mPerFaceMatId.data(); };
/**
Get loaded per triangle smoothing groups. Currently not supported by OBJ.
@@ -100,20 +100,24 @@ public:
int32_t* getSmoothingGroups() override { return nullptr; };
/**
- Get material name. Currently not supported by OBJ.
+ Get material name.
*/
- const char* getMaterialName(int32_t id) override { return nullptr; }
+ const char* getMaterialName(int32_t id) override { return mMaterialNames[id].c_str(); }
/**
Get material count.
*/
- int32_t getMaterialCount() { return 0; };
+ int32_t getMaterialCount() { return mMaterialNames.size(); };
private:
std::vector<physx::PxVec3> mVertexPositions;
std::vector<physx::PxVec3> mVertexNormals;
std::vector<physx::PxVec2> mVertexUv;
std::vector<uint32_t> mIndices;
+
+ std::vector<std::string> mMaterialNames;
+ std::vector<int32_t> mPerFaceMatId;
+
};
}
diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.cpp b/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.cpp
index a49e28f..b453a62 100644
--- a/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.cpp
+++ b/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.cpp
@@ -31,6 +31,7 @@
#include <sstream>
#include "NvBlastExtAuthoringTypes.h"
#include "NvBlastExtAuthoringMesh.h"
+#include <algorithm>
using namespace physx;
@@ -43,6 +44,16 @@ void ObjFileWriter::release()
delete this;
}
+void ObjFileWriter::setInteriorIndex(int32_t index)
+{
+ mIntSurfaceMatIndex = index;
+}
+
+bool CompByMaterial(const Triangle& a, const Triangle& b)
+{
+ return a.materialId < b.materialId;
+}
+
bool ObjFileWriter::appendMesh(const AuthoringResult& aResult, const char* /*assetName*/, bool /*nonSkinned*/)
{
mMeshData = std::shared_ptr<ExporterMeshData>(new ExporterMeshData(), [](ExporterMeshData* md)
@@ -55,34 +66,90 @@ bool ObjFileWriter::appendMesh(const AuthoringResult& aResult, const char* /*ass
delete[] md->positions;
delete[] md->submeshOffsets;
//delete[] md->texIndex;
- delete[] md->submeshNames;
+ delete[] md->submeshMats;
delete[] md->uvs;
delete md;
});
+
+
ExporterMeshData& md = *mMeshData.get();
uint32_t triCount = aResult.geometryOffset[aResult.chunkCount];
md.meshCount = aResult.chunkCount;
- md.submeshOffsets = new uint32_t[md.meshCount + 1];
- for (uint32_t i = 0; i < md.meshCount + 1; i++)
- {
- md.submeshOffsets[i] = aResult.geometryOffset[i] * 3;
+ md.submeshCount = aResult.materialCount;
+
+ int32_t additionalMats = 0;
+
+ if (mIntSurfaceMatIndex == -1 || mIntSurfaceMatIndex >= (int32_t)md.submeshCount)
+ {
+ md.submeshCount += 1;
+ mIntSurfaceMatIndex = md.submeshCount - 1;
+ additionalMats = 1;
+ }
+
+ md.submeshOffsets = new uint32_t[md.meshCount * md.submeshCount + 1];
+ md.submeshMats = new Materials[md.submeshCount];
+
+ for (uint32_t i = 0; i < md.submeshCount - additionalMats; ++i)
+ {
+ md.submeshMats[i].name = aResult.materialNames[i];
+ md.submeshMats[i].diffuse_tex = nullptr;
+ }
+
+ if (additionalMats)
+ {
+ md.submeshMats[mIntSurfaceMatIndex].name = interiorNameStr.c_str();
+ md.submeshMats[mIntSurfaceMatIndex].diffuse_tex = nullptr;
}
- //md.submeshOffsets = md.meshOffsets;
- md.submeshCount = 1;
- //md.indicesCount = triCount * 3;
md.positionsCount = triCount * 3;
md.normalsCount = md.positionsCount;
md.uvsCount = md.positionsCount;
md.positions = new PxVec3[md.positionsCount];
md.normals = new PxVec3[md.normalsCount];
md.uvs = new PxVec2[md.uvsCount];
+
md.posIndex = new uint32_t[triCount * 3];
md.normIndex = md.posIndex;
md.texIndex = md.posIndex;
- md.submeshNames = new const char*[1]{ gTexPath };
+
+
+
+ /**
+ Now we need to sort input trianles chunk they belong to, then by material;
+ */
+ std::vector<Triangle> sorted;
+ sorted.reserve(triCount);
+
+
+ int32_t perChunkOffset = 0;
+ for (uint32_t i = 0; i < md.meshCount; ++i)
+ {
+ std::vector<uint32_t> perMaterialCount(md.submeshCount);
+
+ uint32_t first = aResult.geometryOffset[i];
+ uint32_t last = aResult.geometryOffset[i + 1];
+ uint32_t firstInSorted = sorted.size();
+ for (uint32_t t = first; t < last; ++t)
+ {
+ sorted.push_back(aResult.geometry[t]);
+ int32_t cmat = sorted.back().materialId;
+ if (cmat == MATERIAL_INTERIOR)
+ {
+ cmat = mIntSurfaceMatIndex;
+ }
+ perMaterialCount[cmat]++;
+ }
+ for (uint32_t mof = 0; mof < md.submeshCount; ++mof)
+ {
+ md.submeshOffsets[i * md.submeshCount + mof] = perChunkOffset * 3;
+ perChunkOffset += perMaterialCount[mof];
+ }
+ std::sort(sorted.begin() + firstInSorted, sorted.end(), CompByMaterial);
+ }
+ md.submeshOffsets[md.meshCount * md.submeshCount] = perChunkOffset * 3;
+
for (uint32_t vc = 0; vc < triCount; ++vc)
{
- Triangle& tri = aResult.geometry[vc];
+ Triangle& tri = sorted[vc];
uint32_t i = vc * 3;
md.positions[i+0] = tri.a.p;
md.positions[i+1] = tri.b.p;
@@ -129,8 +196,15 @@ bool ObjFileWriter::saveToFile(const char* assetName, const char* outputPath)
for (uint32_t submeshIndex = 0; submeshIndex < md.submeshCount; ++submeshIndex)
{
- fprintf(f, "newmtl mat%d\n", submeshIndex);
- fprintf(f, "\tmap_Kd %s\n", md.submeshNames[submeshIndex]);
+ fprintf(f, "newmtl %s\n", md.submeshMats[submeshIndex].name);
+ if (md.submeshMats[submeshIndex].diffuse_tex != nullptr)
+ {
+ fprintf(f, "\tmap_Kd %s\n", md.submeshMats[submeshIndex].diffuse_tex);
+ }
+ else
+ {
+ fprintf(f, "\tKd %f %f %f\n", float(rand()) / RAND_MAX, float(rand()) / RAND_MAX, float(rand()) / RAND_MAX);
+ }
fprintf(f, "\n");
}
@@ -165,13 +239,16 @@ bool ObjFileWriter::saveToFile(const char* assetName, const char* outputPath)
for (uint32_t chunkIndex = 0; chunkIndex < chunkCount; ++chunkIndex)
{
+ fprintf(f, "g %d \n", chunkIndex);
for (uint32_t submeshIndex = 0; submeshIndex < md.submeshCount; ++submeshIndex)
{
uint32_t firstIdx = md.submeshOffsets[chunkIndex * md.submeshCount + submeshIndex];
uint32_t lastIdx = md.submeshOffsets[chunkIndex * md.submeshCount + submeshIndex + 1];
- fprintf(f, "g %d_%d \n", chunkIndex, submeshIndex);
- fprintf(f, "usemtl mat%d\n", submeshIndex);
-
+ if (firstIdx == lastIdx) // There is no trianlges in this submesh.
+ {
+ continue;
+ }
+ fprintf(f, "usemtl %s\n", md.submeshMats[submeshIndex].name);
for (uint32_t i = firstIdx; i < lastIdx; i += 3)
{
fprintf(f, "f %d/%d/%d ", md.posIndex[i] + 1, md.texIndex[i] + 1, md.normIndex[i] + 1);
diff --git a/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.h b/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.h
index 3152d42..65e3f0f 100644
--- a/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.h
+++ b/sdk/extensions/exporter/source/NvBlastExtExporterObjWriter.h
@@ -34,7 +34,7 @@
#include <vector>
#include <PxVec2.h>
#include <PxVec3.h>
-
+#include <string>
struct NvBlastAsset;
namespace Nv
@@ -46,7 +46,7 @@ class ObjFileWriter : public IMeshFileWriter
{
public:
- ObjFileWriter() {};
+ ObjFileWriter(): mIntSurfaceMatIndex(-1), interiorNameStr("INTERIOR_MATERIAL") { };
~ObjFileWriter() = default;
virtual void release() override;
@@ -63,8 +63,15 @@ public:
*/
virtual bool saveToFile(const char* assetName, const char* outputPath) override;
+ /**
+ Set interior material index. Not supported in OBJ since AuthoringTool doesn't created OBJ with materials currently.
+ */
+ virtual void setInteriorIndex(int32_t index) override;
+
private:
std::shared_ptr<ExporterMeshData> mMeshData;
+ int32_t mIntSurfaceMatIndex;
+ std::string interiorNameStr;
};
}
diff --git a/sdk/lowlevel/source/NvBlastActor.cpp b/sdk/lowlevel/source/NvBlastActor.cpp
index 8c90d0f..7d55bd7 100644
--- a/sdk/lowlevel/source/NvBlastActor.cpp
+++ b/sdk/lowlevel/source/NvBlastActor.cpp
@@ -262,7 +262,8 @@ void Actor::generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBla
size_t Actor::splitRequiredScratch() const
{
- return FamilyGraph::findIslandsRequiredScratch(getGraph()->m_nodeCount);
+ // Scratch is reused, just need the max of these two values
+ return std::max(m_graphNodeCount * sizeof(uint32_t), static_cast<size_t>(FamilyGraph::findIslandsRequiredScratch(getGraph()->m_nodeCount)));
}
@@ -334,6 +335,27 @@ uint32_t Actor::split(NvBlastActorSplitEvent* result, uint32_t newActorsMaxCount
}
#endif
+ // Reuse scratch for node list
+ uint32_t* graphNodeIndexList = reinterpret_cast<uint32_t*>(scratch);
+
+ // Get the family header
+ FamilyHeader* header = getFamilyHeader();
+ NVBLAST_ASSERT(header != nullptr); // If m_actorEntryDataIndex is valid, this should be too
+
+ // Record nodes in this actor before splitting
+ const uint32_t* graphNodeIndexLinks = header->getGraphNodeIndexLinks(); // Get the links for the graph nodes
+ uint32_t graphNodeIndexCount = 0;
+ for (uint32_t graphNodeIndex = m_firstGraphNodeIndex; !isInvalidIndex(graphNodeIndex); graphNodeIndex = graphNodeIndexLinks[graphNodeIndex])
+ {
+ if (graphNodeIndexCount >= m_graphNodeCount)
+ {
+ // Safety, splitRequiredScratch() only guarantees m_graphNodeCount elements. In any case, this condition shouldn't happen.
+ NVBLAST_ASSERT(graphNodeIndexCount < m_graphNodeCount);
+ break;
+ }
+ graphNodeIndexList[graphNodeIndexCount++] = graphNodeIndex;
+ }
+
actorsCount = partitionMultipleGraphNodes(newActors, newActorsMaxCount, logFn);
if (actorsCount > 1)
@@ -345,19 +367,40 @@ uint32_t Actor::split(NvBlastActorSplitEvent* result, uint32_t newActorsMaxCount
}
#endif
- // Recalculate visible chunk lists if the graph nodes have been redistributed
+ // Get various pointers and values to iterate
+ const Asset* asset = getAsset();
+ Actor* actors = header->getActors();
+ IndexDLink<uint32_t>* visibleChunkIndexLinks = header->getVisibleChunkIndexLinks();
+ uint32_t* chunkActorIndices = header->getChunkActorIndices();
+ const SupportGraph& graph = asset->m_graph;
+ const uint32_t* graphChunkIndices = graph.getChunkIndices();
+ const NvBlastChunk* chunks = asset->getChunks();
+ const uint32_t upperSupportChunkCount = asset->getUpperSupportChunkCount();
+ const uint32_t* familyGraphIslandIDs = header->getFamilyGraph()->getIslandIds();
+
+ // Iterate over all graph nodes and update visible chunk lists
+ for (uint32_t graphNodeNum = 0; graphNodeNum < graphNodeIndexCount; ++graphNodeNum)
+ {
+ const uint32_t graphNodeIndex = graphNodeIndexList[graphNodeNum];
+ const uint32_t supportChunkIndex = graphChunkIndices[graphNodeIndex];
+ if (!isInvalidIndex(supportChunkIndex)) // Invalid if this is the world chunk
+ {
+ updateVisibleChunksFromSupportChunk<Actor>(actors, visibleChunkIndexLinks, chunkActorIndices, familyGraphIslandIDs[graphNodeIndex], graphChunkIndices[graphNodeIndex], chunks, upperSupportChunkCount);
+ }
+ }
+
+ // Remove actors with no visible chunks - this can happen if we've split such that the world node is by itself
uint32_t actualActorsCount = 0;
for (uint32_t i = 0; i < actorsCount; ++i)
{
newActors[actualActorsCount] = newActors[i];
- newActors[actualActorsCount]->updateVisibleChunksFromGraphNodes();
- if (newActors[actualActorsCount]->getVisibleChunkCount() > 0) // If we've split such that the world node is by itself, it will have no visible chunks
+ if (newActors[actualActorsCount]->getVisibleChunkCount() > 0)
{
++actualActorsCount;
}
else
{
- getFamilyHeader()->returnActor(*newActors[actualActorsCount]);
+ header->returnActor(*newActors[actualActorsCount]);
}
}
actorsCount = actualActorsCount;
diff --git a/sdk/lowlevel/source/NvBlastAsset.cpp b/sdk/lowlevel/source/NvBlastAsset.cpp
index 354a3ac..2cfc73e 100644
--- a/sdk/lowlevel/source/NvBlastAsset.cpp
+++ b/sdk/lowlevel/source/NvBlastAsset.cpp
@@ -591,7 +591,7 @@ Asset* Asset::create(void* mem, const NvBlastAssetDesc* desc, void* scratch, NvB
{
break; // Only iterate through root chunks at this level
}
- const uint32_t enumeratedChunkCount = enumerateChunkHierarchyBreadthFirst(breadthFirstChunkIndices, desc->chunkCount, chunks, startChunkIndex, false);
+ const uint32_t enumeratedChunkCount = enumerateChunkHierarchyBreadthFirst(breadthFirstChunkIndices, desc->chunkCount, chunks, startChunkIndex);
for (uint32_t chunkNum = enumeratedChunkCount; chunkNum--;)
{
const uint32_t chunkIndex = breadthFirstChunkIndices[chunkNum];
@@ -600,8 +600,10 @@ Asset* Asset::create(void* mem, const NvBlastAssetDesc* desc, void* scratch, NvB
{
subtreeLeafChunkCounts[chunkIndex] = 1;
}
- NVBLAST_ASSERT(!isInvalidIndex(chunk.parentChunkIndex)); // Parent index is valid because root chunk is not included in this list (because of 'false' passed into enumerateChunkHierarchyBreadthFirst, above)
- subtreeLeafChunkCounts[chunk.parentChunkIndex] += subtreeLeafChunkCounts[chunkIndex];
+ if (!isInvalidIndex(chunk.parentChunkIndex))
+ {
+ subtreeLeafChunkCounts[chunk.parentChunkIndex] += subtreeLeafChunkCounts[chunkIndex];
+ }
}
}