diff options
| author | Bryan Galdrikian <[email protected]> | 2018-01-22 14:04:16 -0800 |
|---|---|---|
| committer | Bryan Galdrikian <[email protected]> | 2018-01-22 14:04:16 -0800 |
| commit | 1dc1a87fba520bb45c1ce8165e8ea2c83c0a308d (patch) | |
| tree | 5f8ca75a6b92c60fb5cf3b14282fc4cc1c127eb2 /sdk | |
| parent | Updating readme.md to show updated UE4 Blast integration branches (diff) | |
| download | blast-1dc1a87fba520bb45c1ce8165e8ea2c83c0a308d.tar.xz blast-1dc1a87fba520bb45c1ce8165e8ea2c83c0a308d.zip | |
Changes for 1.1.2 release candidate
See README.md, docs/release_notes.txt
Diffstat (limited to 'sdk')
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]; + } } } |