From 420bc85f97c0c19ffca4b4f26b70d171f7d5761e Mon Sep 17 00:00:00 2001 From: Maxim Nikolenko Date: Tue, 30 Sep 2025 19:28:21 +0300 Subject: [PATCH 01/65] #4751 Restore navigation UI for Marketplace and Search --- indra/newview/llfloatermarketplace.cpp | 22 +- indra/newview/llfloatermarketplace.h | 8 +- indra/newview/llfloatersearch.cpp | 14 +- indra/newview/llfloatersearch.h | 4 +- indra/newview/llstatusbar.cpp | 7 +- .../default/xui/en/floater_marketplace.xml | 221 +++++++++++++++-- .../skins/default/xui/en/floater_search.xml | 222 ++++++++++++++++-- 7 files changed, 444 insertions(+), 54 deletions(-) diff --git a/indra/newview/llfloatermarketplace.cpp b/indra/newview/llfloatermarketplace.cpp index 889daf84ab3..4abea64302b 100644 --- a/indra/newview/llfloatermarketplace.cpp +++ b/indra/newview/llfloatermarketplace.cpp @@ -27,10 +27,11 @@ #include "llviewerprecompiledheaders.h" #include "llfloatermarketplace.h" +#include "llviewercontrol.h" #include "lluictrlfactory.h" LLFloaterMarketplace::LLFloaterMarketplace(const LLSD& key) - : LLFloater(key) + : LLFloaterWebContent(key) { } @@ -38,10 +39,25 @@ LLFloaterMarketplace::~LLFloaterMarketplace() { } +// just to override LLFloaterWebContent +void LLFloaterMarketplace::onClose(bool app_quitting) +{ +} + bool LLFloaterMarketplace::postBuild() { - enableResizeCtrls(true, true, false); + LLFloaterWebContent::postBuild(); + mWebBrowser = getChild("marketplace_contents"); + mWebBrowser->addObserver(this); + return true; } - +void LLFloaterMarketplace::openMarketplace() +{ + std::string url = gSavedSettings.getString("MarketplaceURL"); + if (mCurrentURL != url) + { + mWebBrowser->navigateTo(url, HTTP_CONTENT_TEXT_HTML); + } +} diff --git a/indra/newview/llfloatermarketplace.h b/indra/newview/llfloatermarketplace.h index 2ae4d0d64a2..9524c94eeec 100644 --- a/indra/newview/llfloatermarketplace.h +++ b/indra/newview/llfloatermarketplace.h @@ -27,14 +27,20 @@ #pragma once #include "llfloater.h" +#include "llfloaterwebcontent.h" class LLFloaterMarketplace: - public LLFloater + public LLFloaterWebContent { friend class LLFloaterReg; + +public: + void openMarketplace(); + private: LLFloaterMarketplace(const LLSD& key); ~LLFloaterMarketplace(); bool postBuild() override; + void onClose(bool app_quitting) override; }; diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp index 8e6a47dce5d..7ee1b88f05b 100644 --- a/indra/newview/llfloatersearch.cpp +++ b/indra/newview/llfloatersearch.cpp @@ -66,7 +66,7 @@ class LLSearchHandler : public LLCommandHandler { LLSearchHandler gSearchHandler; LLFloaterSearch::LLFloaterSearch(const LLSD& key) - : LLFloater(key) + : LLFloaterWebContent(key) { mSearchType.insert("standard"); mSearchType.insert("land"); @@ -86,6 +86,12 @@ LLFloaterSearch::~LLFloaterSearch() void LLFloaterSearch::onOpen(const LLSD& tokens) { initiateSearch(tokens); + mWebBrowser->setFocus(true); +} + +// just to override LLFloaterWebContent +void LLFloaterSearch::onClose(bool app_quitting) +{ } void LLFloaterSearch::initiateSearch(const LLSD& tokens) @@ -161,7 +167,11 @@ void LLFloaterSearch::initiateSearch(const LLSD& tokens) bool LLFloaterSearch::postBuild() { - enableResizeCtrls(true, true, false); + LLFloaterWebContent::postBuild(); + mWebBrowser = getChild("search_contents"); + mWebBrowser->addObserver(this); + getChildView("address")->setEnabled(false); + getChildView("popexternal")->setEnabled(false); // This call is actioned by the preload code in llViewerWindow // that creates the search floater during the login process diff --git a/indra/newview/llfloatersearch.h b/indra/newview/llfloatersearch.h index e8a2be4797c..6d93474f4a6 100644 --- a/indra/newview/llfloatersearch.h +++ b/indra/newview/llfloatersearch.h @@ -27,13 +27,15 @@ #pragma once #include "llfloater.h" +#include "llfloaterwebcontent.h" class LLFloaterSearch: - public LLFloater { + public LLFloaterWebContent { friend class LLFloaterReg; public: void onOpen(const LLSD& key) override; + void onClose(bool app_quitting) override; private: LLFloaterSearch(const LLSD& key); diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index 82c959d7f77..2727127633d 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -41,6 +41,7 @@ #include "llpanelpresetscamerapulldown.h" #include "llpanelpresetspulldown.h" #include "llpanelvolumepulldown.h" +#include "llfloatermarketplace.h" #include "llfloaterregioninfo.h" #include "llfloaterscriptdebug.h" #include "llhints.h" @@ -523,7 +524,11 @@ void LLStatusBar::onClickBuyCurrency() void LLStatusBar::onClickShop() { - LLFloaterReg::toggleInstanceOrBringToFront("marketplace"); + LLFloaterReg::showInstanceOrBringToFront("marketplace"); + if (LLFloaterMarketplace* marketplace = LLFloaterReg::getTypedInstance("marketplace")) + { + marketplace->openMarketplace(); + } } void LLStatusBar::onMouseEnterPresetsCamera() diff --git a/indra/newview/skins/default/xui/en/floater_marketplace.xml b/indra/newview/skins/default/xui/en/floater_marketplace.xml index 2299e02c63e..99fb3a1ad82 100644 --- a/indra/newview/skins/default/xui/en/floater_marketplace.xml +++ b/indra/newview/skins/default/xui/en/floater_marketplace.xml @@ -1,26 +1,201 @@ - + legacy_header_height="18" + can_minimize="true" + can_close="true" + can_resize="true" + height="775" + layout="topleft" + min_height="500" + min_width="600" + name="Marketplace" + save_rect="true" + single_instance="true" + save_visibility="true" + title="MARKETPLACE" + tab_stop="true" + width="780"> + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en/floater_search.xml b/indra/newview/skins/default/xui/en/floater_search.xml index 76a486e211f..43c4aa1b9de 100644 --- a/indra/newview/skins/default/xui/en/floater_search.xml +++ b/indra/newview/skins/default/xui/en/floater_search.xml @@ -1,26 +1,202 @@ - + legacy_header_height="18" + can_minimize="true" + can_close="true" + can_resize="true" + height="775" + layout="topleft" + min_height="500" + min_width="600" + name="Search" + save_rect="true" + single_instance="true" + save_visibility="true" + title="SEARCH" + tab_stop="true" + width="780"> + + + + + + + + + + + + + + + + + + + + From 589498fece769446310c085e9feb08ccd8400c2c Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Wed, 1 Oct 2025 17:50:28 -0700 Subject: [PATCH 02/65] Reenable havok llphysicsextensions on Darwin x86_64 with llphysicsextensions_stub used for aarch64 --- autobuild.xml | 40 +++------------------------ build.sh | 1 - indra/cmake/LLPhysicsExtensions.cmake | 9 +++++- indra/newview/CMakeLists.txt | 9 ++++++ 4 files changed, 21 insertions(+), 38 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index c9355c73c04..280078b4c26 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1432,53 +1432,21 @@ platforms - darwin64 - - archive - - creds - github - hash - 7facda95e2f00c260513f3d4db42588fa8ba703c - hash_algorithm - sha1 - url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/196289774 - - name - darwin64 - - linux64 - - archive - - creds - github - hash - 01d08f13c7bc8d1b95b0330fa6833b7d8274e4d0 - hash_algorithm - sha1 - url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/196289775 - - name - linux64 - - windows64 + common archive creds github hash - 6d00345c7d3471bc5f7c1218e014dd0f1a2c069b + 1949ae355a70a4cbe2f0969de636680a0b5d7b15 hash_algorithm sha1 url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/196289778 + https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/299453833 name - windows64 + common license diff --git a/build.sh b/build.sh index 36e332cf42c..325386e7e94 100755 --- a/build.sh +++ b/build.sh @@ -158,7 +158,6 @@ pre_build() if [[ "$arch" == "Darwin" ]] then - HAVOK=OFF SIGNING=("-DENABLE_SIGNING:BOOL=YES" \ "-DSIGNING_IDENTITY:STRING=Developer ID Application: Linden Research, Inc.") fi diff --git a/indra/cmake/LLPhysicsExtensions.cmake b/indra/cmake/LLPhysicsExtensions.cmake index 80d243d9f8b..6112621b5ad 100644 --- a/indra/cmake/LLPhysicsExtensions.cmake +++ b/indra/cmake/LLPhysicsExtensions.cmake @@ -22,7 +22,14 @@ if (HAVOK) include(Havok) use_prebuilt_binary(llphysicsextensions_source) set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/src) - target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions) + if(DARWIN) + set(LLPHYSICSEXTENSIONS_STUB_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/stub) + # can't set these library dependencies per-arch here, need to do it using XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=*] in newview/CMakeLists.txt + #target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions) + #target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensionsstub) + else() + target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions) + endif() elseif (HAVOK_TPV) use_prebuilt_binary(llphysicsextensions_tpv) target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions_tpv) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c727d5ae572..72baac73ae3 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -59,6 +59,10 @@ if (NOT HAVOK_TPV) # which means we need to duct tape this togther ... add_subdirectory(${LLPHYSICSEXTENSIONS_SRC_DIR} llphysicsextensions) + if (NOT "${LLPHYSICSEXTENSIONS_STUB_DIR}" EQUAL "") + # for darwin universal builds we need both real llphysicsextensions and the stub for aarch64 fallback + add_subdirectory(${LLPHYSICSEXTENSIONS_STUB_DIR} llphysicsextensionsstub) + endif() # Another hack that works with newer cmake versions: cmake_policy( SET CMP0079 NEW) @@ -1952,6 +1956,11 @@ elseif (DARWIN) RESOURCE SecondLife.xib #LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Xlinker -dead_strip -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP" LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Xlinker -dead_strip" + # arch specific flags for universal builds: https://stackoverflow.com/a/77942065 + XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=x86_64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_FULL" + XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=arm64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_STUB" + XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions" + XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensionsstub/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsstub" ) else (WINDOWS) # Linux From b8cf54d909263e14622d9669b192c9ad14dc7173 Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Wed, 1 Oct 2025 18:17:16 -0700 Subject: [PATCH 03/65] Fix configurations like windows where LLPHYSICSEXTENSIONS_STUB_DIR is unset --- indra/newview/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 72baac73ae3..1444eeaba36 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -59,7 +59,7 @@ if (NOT HAVOK_TPV) # which means we need to duct tape this togther ... add_subdirectory(${LLPHYSICSEXTENSIONS_SRC_DIR} llphysicsextensions) - if (NOT "${LLPHYSICSEXTENSIONS_STUB_DIR}" EQUAL "") + if (NOT "${LLPHYSICSEXTENSIONS_STUB_DIR}" STREQUAL "") # for darwin universal builds we need both real llphysicsextensions and the stub for aarch64 fallback add_subdirectory(${LLPHYSICSEXTENSIONS_STUB_DIR} llphysicsextensionsstub) endif() From fc41f57a06065957bc377d7a1716c5c04b8fec70 Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Thu, 2 Oct 2025 11:26:48 -0700 Subject: [PATCH 04/65] Attempt to fix build error --- indra/newview/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 1444eeaba36..9e566096fcc 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -61,6 +61,7 @@ if (NOT HAVOK_TPV) add_subdirectory(${LLPHYSICSEXTENSIONS_SRC_DIR} llphysicsextensions) if (NOT "${LLPHYSICSEXTENSIONS_STUB_DIR}" STREQUAL "") # for darwin universal builds we need both real llphysicsextensions and the stub for aarch64 fallback + # this will only be set when HAVOK is ON, otherwise the normal stub fallback will be in effect add_subdirectory(${LLPHYSICSEXTENSIONS_STUB_DIR} llphysicsextensionsstub) endif() @@ -73,6 +74,9 @@ if (NOT HAVOK_TPV) target_link_libraries(llphysicsextensions llrender ) if (DARWIN) target_compile_options( llphysicsextensions PRIVATE -Wno-unused-local-typedef) + + # TODO - move this into llphysicsextensions/CMakeLists.txt once I've proved it works + set_target_properties(llphysicsextensions PROPERTIES OSX_ARCHITECTURES "x86_64") endif (DARWIN) endif() endif (NOT HAVOK_TPV) From 99d8f22c99e54ba4c22a2b5834bba2c0de549fca Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Thu, 2 Oct 2025 22:24:59 +0300 Subject: [PATCH 05/65] #4757 Model uploader warning when physics library is missing --- indra/newview/llviewermenufile.cpp | 18 +++++++++++++++++- .../skins/default/xui/en/notifications.xml | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 36954780610..801ff3c2122 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -912,7 +912,23 @@ class LLFileUploadModel : public view_listener_t { bool handleEvent(const LLSD& userdata) { - LLFloaterModelPreview::showModelPreview(); + if (LLConvexDecomposition::isFunctional()) + { + LLFloaterModelPreview::showModelPreview(); + } + else + { + if (gGLManager.mIsApple) + { + LLNotificationsUtil::add("ModelUploaderMissingPhysicsApple"); + } + else + { + // TPV? + LLNotificationsUtil::add("ModelUploaderMissingPhysics"); + LLFloaterModelPreview::showModelPreview(); + } + } return true; } }; diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 7fc96af55d7..536008c85cf 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2237,6 +2237,25 @@ Couldn't open uploaded sound file for reading: fail + +Model upload is not yet available on Apple Silicon, but will be supported in an upcoming release. + +Workaround: Right-click the Second Life app in Finder, select +"Get Info", then check "Open using Rosetta" + fail + + + +Physics library is not present, some of the model uploader's functionality might not work or might not work correctly. + fail + + Date: Thu, 2 Oct 2025 13:35:54 -0700 Subject: [PATCH 06/65] new llphysicsextensions_source package that sets OSX_ARCHITECTURES properly and should hopefully package llphysicsextensions_tpv successfully --- autobuild.xml | 4 ++-- indra/newview/CMakeLists.txt | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 280078b4c26..56831346c11 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1439,11 +1439,11 @@ creds github hash - 1949ae355a70a4cbe2f0969de636680a0b5d7b15 + fff82c79edb900c547c40dca9a0e3ebac5a8c7da hash_algorithm sha1 url - https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/299453833 + https://api.github.com/repos/secondlife/llphysicsextensions_source/releases/assets/299858950 name common diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9e566096fcc..0d50aeca82d 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -74,9 +74,6 @@ if (NOT HAVOK_TPV) target_link_libraries(llphysicsextensions llrender ) if (DARWIN) target_compile_options( llphysicsextensions PRIVATE -Wno-unused-local-typedef) - - # TODO - move this into llphysicsextensions/CMakeLists.txt once I've proved it works - set_target_properties(llphysicsextensions PROPERTIES OSX_ARCHITECTURES "x86_64") endif (DARWIN) endif() endif (NOT HAVOK_TPV) From e30bc61d9a63ed29985bd6e07405f0051bfceaf6 Mon Sep 17 00:00:00 2001 From: Brad Linden Date: Thu, 2 Oct 2025 14:51:14 -0700 Subject: [PATCH 07/65] Restore creation of secondlife-bin.MAP file on Darwin x86_64 for production of llphysicsextensions_tpv --- indra/newview/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 0d50aeca82d..eb40067930a 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1955,12 +1955,12 @@ elseif (DARWIN) set_target_properties(${VIEWER_BINARY_NAME} PROPERTIES RESOURCE SecondLife.xib - #LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Xlinker -dead_strip -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP" LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Xlinker -dead_strip" # arch specific flags for universal builds: https://stackoverflow.com/a/77942065 XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=x86_64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_FULL" XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=arm64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_STUB" - XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions" + # only generate the .MAP file for llphysicsextensions_tpv on x86_64 + XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP" XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensionsstub/$,$,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsstub" ) else (WINDOWS) From 9a413357ad5bc5f5492ece90b2c9cc9dbe6087c8 Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 2 Oct 2025 06:18:22 -0400 Subject: [PATCH 08/65] Fix infinite growth of mLoadingPhysicsShapes in mesh repository --- indra/newview/llmeshrepository.cpp | 45 +++++++++++++++++++++++------- indra/newview/llmeshrepository.h | 5 +++- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 8d5f94cdbb0..1f6a0469190 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -256,6 +256,7 @@ // mDecompositionRequests mMutex rw.repo.mMutex, ro.repo.none [5] // mPhysicsShapeRequests mMutex rw.repo.mMutex, ro.repo.none [5] // mDecompositionQ mMutex rw.repo.mLoadedMutex, rw.main.mLoadedMutex [5] (was: [0]) +// mPhysicsQ mMutex rw.repo.mLoadedMutex, rw.main.mLoadedMutex [5] (was: [0]) // mHeaderReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex // mLODReqQ mMutex ro.repo.none [5], rw.repo.mMutex, rw.any.mMutex // mUnavailableQ mMutex rw.repo.none [0], ro.main.none [5], rw.main.mLoadedMutex @@ -982,6 +983,12 @@ LLMeshRepoThread::~LLMeshRepoThread() mDecompositionQ.pop_front(); } + while (!mPhysicsQ.empty()) + { + delete mPhysicsQ.front(); + mPhysicsQ.pop_front(); + } + delete mHttpRequest; mHttpRequest = nullptr; delete mMutex; @@ -2565,7 +2572,7 @@ EMeshProcessingResult LLMeshRepoThread::physicsShapeReceived(const LLUUID& mesh_ { LLMutexLock lock(mLoadedMutex); - mDecompositionQ.push_back(d); + mPhysicsQ.push_back(d); } return MESH_OK; } @@ -3441,13 +3448,14 @@ void LLMeshRepoThread::notifyLoadedMeshes() } } - if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || ! mDecompositionQ.empty()) + if (!mSkinInfoQ.empty() || !mSkinUnavailableQ.empty() || !mDecompositionQ.empty() || !mPhysicsQ.empty()) { if (mLoadedMutex->trylock()) { std::deque> skin_info_q; std::deque skin_info_unavail_q; std::list decomp_q; + std::list physics_q; if (! mSkinInfoQ.empty()) { @@ -3464,6 +3472,11 @@ void LLMeshRepoThread::notifyLoadedMeshes() decomp_q.swap(mDecompositionQ); } + if (!mPhysicsQ.empty()) + { + physics_q.swap(mPhysicsQ); + } + mLoadedMutex->unlock(); // Process the elements free of the lock @@ -3480,9 +3493,15 @@ void LLMeshRepoThread::notifyLoadedMeshes() while (! decomp_q.empty()) { - gMeshRepo.notifyDecompositionReceived(decomp_q.front()); + gMeshRepo.notifyDecompositionReceived(decomp_q.front(), false); decomp_q.pop_front(); } + + while (!physics_q.empty()) + { + gMeshRepo.notifyDecompositionReceived(physics_q.front(), true); + physics_q.pop_front(); + } } } @@ -4724,13 +4743,13 @@ void LLMeshRepository::notifySkinInfoUnavailable(const LLUUID& mesh_id) } } -void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp) +void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decomp, bool physics_mesh) { - decomposition_map::iterator iter = mDecompositionMap.find(decomp->mMeshID); + LLUUID decomp_id = decomp->mMeshID; // Copy to avoid invalidation in below deletion + decomposition_map::iterator iter = mDecompositionMap.find(decomp_id); if (iter == mDecompositionMap.end()) { //just insert decomp into map - mDecompositionMap[decomp->mMeshID] = decomp; - mLoadingDecompositions.erase(decomp->mMeshID); + mDecompositionMap[decomp_id] = decomp; sCacheBytesDecomps += decomp->sizeBytes(); } else @@ -4738,10 +4757,17 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom sCacheBytesDecomps -= iter->second->sizeBytes(); iter->second->merge(decomp); sCacheBytesDecomps += iter->second->sizeBytes(); - - mLoadingDecompositions.erase(decomp->mMeshID); delete decomp; } + + if (physics_mesh) + { + mLoadingPhysicsShapes.erase(decomp_id); + } + else + { + mLoadingDecompositions.erase(decomp_id); + } } void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume, S32 lod) @@ -4888,7 +4914,6 @@ void LLMeshRepository::fetchPhysicsShape(const LLUUID& mesh_id) std::unordered_set::iterator iter = mLoadingPhysicsShapes.find(mesh_id); if (iter == mLoadingPhysicsShapes.end()) { //no request pending for this skin info - // *FIXME: Nothing ever deletes entries, can't be right mLoadingPhysicsShapes.insert(mesh_id); mPendingPhysicsShapeRequests.push(mesh_id); } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index ab17b921d6c..cf058071644 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -521,6 +521,9 @@ class LLMeshRepoThread : public LLThread // list of completed Decomposition info requests std::list mDecompositionQ; + // list of completed Physics Mesh info requests + std::list mPhysicsQ; + //queue of requested headers std::queue mHeaderReqQ; @@ -855,7 +858,7 @@ class LLMeshRepository void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 request_lod, S32 volume_lod); void notifySkinInfoReceived(LLMeshSkinInfo* info); void notifySkinInfoUnavailable(const LLUUID& info); - void notifyDecompositionReceived(LLModel::Decomposition* info); + void notifyDecompositionReceived(LLModel::Decomposition* info, bool physics_mesh); S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); static S32 getActualMeshLOD(LLMeshHeader& header, S32 lod); From b277cc6a8fd2a48ed3ba6498734d9fae209d181e Mon Sep 17 00:00:00 2001 From: Rye Date: Thu, 18 Sep 2025 13:38:54 -0400 Subject: [PATCH 09/65] Initial VHACD based llconvexdecomposition --- autobuild.xml | 68 +-- indra/CMakeLists.txt | 3 + indra/cmake/CMakeLists.txt | 1 + indra/cmake/LLPhysicsExtensions.cmake | 8 +- indra/cmake/VHACD.cmake | 9 + indra/llconvexdecomposition/CMakeLists.txt | 39 ++ .../llconvexdecomposition.cpp | 83 +++ .../llconvexdecomposition.h | 231 ++++++++ .../llconvexdecompositionvhacd.cpp | 492 ++++++++++++++++++ .../llconvexdecompositionvhacd.h | 339 ++++++++++++ indra/llprimitive/CMakeLists.txt | 6 + indra/llprimitive/llmodel.cpp | 4 +- indra/llprimitive/llmodel.h | 3 +- indra/newview/CMakeLists.txt | 8 +- indra/newview/llfloatermodelpreview.cpp | 12 +- indra/newview/llmodelpreview.cpp | 14 +- indra/newview/llviewerobjectlist.cpp | 4 +- .../default/xui/en/floater_model_preview.xml | 149 +++++- 18 files changed, 1418 insertions(+), 55 deletions(-) create mode 100644 indra/cmake/VHACD.cmake create mode 100644 indra/llconvexdecomposition/CMakeLists.txt create mode 100644 indra/llconvexdecomposition/llconvexdecomposition.cpp create mode 100644 indra/llconvexdecomposition/llconvexdecomposition.h create mode 100644 indra/llconvexdecomposition/llconvexdecompositionvhacd.cpp create mode 100644 indra/llconvexdecomposition/llconvexdecompositionvhacd.h diff --git a/autobuild.xml b/autobuild.xml index c9355c73c04..8a022cd304b 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -1496,41 +1496,19 @@ platforms - darwin64 - - archive - - hash - f290b000b31f9e36f2489946cbc99f5e - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/59995/563653/llphysicsextensions_stub-1.0.542456-darwin64-542456.tar.bz2 - - name - darwin64 - - linux64 - - archive - - hash - 711f4ec769e4b5f59ba25ee43c11bcbc - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4724/14846/llphysicsextensions_stub-1.0.504712-linux64-504712.tar.bz2 - - name - linux64 - - windows64 + common archive hash - 2e5f1f7046a49d8b0bc295aa878116bc + bc41438b10ac6474cf5560465a3662a64f9e65a81342e4c33f18f6694581c7ee28c9ee6f091c36e80a0b1e10c68205be71eb5f8e40fef115d2c744fc2bbfcb43 + hash_algorithm + blake2b url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/60043/564063/llphysicsextensions_stub-1.0.542456-windows-542456.tar.bz2 + https://github.com/AlchemyViewer/llphysicsextensions_stub/releases/download/v1.0-cb4900e/llphysicsextensions_stub-1.0-common-17836965684.tar.zst name - windows64 + common license @@ -1540,7 +1518,7 @@ copyright Copyright (c) 2010, Linden Research, Inc. version - 1.0.542456 + 1.0 name llphysicsextensions_stub @@ -2966,6 +2944,38 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors description Discord Social SDK + vhacd + + platforms + + common + + archive + + hash + 140d8fc952a10edb5f2d72ab405336019ef32cadfa64f0cfce76c9de4bc6268cbc87cc8cd89d3417fb78b531d441701afc8d016bafe4bd275df2707f7daf1387 + hash_algorithm + blake2b + url + https://github.com/AlchemyViewer/3p-vhacd/releases/download/v4.1.0-r2/vhacd-4.1.0-r2-common-18166921729.tar.zst + + name + common + + + license + BSD + license_file + LICENSES/vhacd.txt + copyright + Copyright (c) 2011, Khaled Mamou + version + 4.1.0-r2 + name + vhacd + description + Voxelized Hierarchical Approximate Convex Decomposition + package_description diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 6504002dd98..246bd7bfa0b 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -61,6 +61,9 @@ add_subdirectory(cmake) add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llappearance) add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) +if (NOT HAVOK AND NOT HAVOK_TPV) + add_subdirectory(${LIBS_OPEN_PREFIX}llconvexdecomposition) +endif () add_subdirectory(${LIBS_OPEN_PREFIX}llcommon) add_subdirectory(${LIBS_OPEN_PREFIX}llcorehttp) add_subdirectory(${LIBS_OPEN_PREFIX}llimage) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 4608f68f509..2ba282bdb78 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -62,6 +62,7 @@ set(cmake_SOURCE_FILES UI.cmake UnixInstall.cmake Variables.cmake + VHACD.cmake ViewerMiscLibs.cmake VisualLeakDetector.cmake LibVLCPlugin.cmake diff --git a/indra/cmake/LLPhysicsExtensions.cmake b/indra/cmake/LLPhysicsExtensions.cmake index 80d243d9f8b..bb6cb703d28 100644 --- a/indra/cmake/LLPhysicsExtensions.cmake +++ b/indra/cmake/LLPhysicsExtensions.cmake @@ -23,9 +23,15 @@ if (HAVOK) use_prebuilt_binary(llphysicsextensions_source) set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/src) target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions) + target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 ) elseif (HAVOK_TPV) use_prebuilt_binary(llphysicsextensions_tpv) - target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions_tpv) + if(WINDOWS) + target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/llphysicsextensions_tpv.lib) + else() + target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/libllphysicsextensions_tpv.a) + endif() + target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 ) else (HAVOK) use_prebuilt_binary(llphysicsextensions_stub) set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/stub) diff --git a/indra/cmake/VHACD.cmake b/indra/cmake/VHACD.cmake new file mode 100644 index 00000000000..9f37f32b2d8 --- /dev/null +++ b/indra/cmake/VHACD.cmake @@ -0,0 +1,9 @@ +# -*- cmake -*- +include(Prebuilt) + +add_library(ll::vhacd INTERFACE IMPORTED) + +use_system_binary(vhacd) +use_prebuilt_binary(vhacd) + +target_include_directories(ll::vhacd SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/vhacd/) diff --git a/indra/llconvexdecomposition/CMakeLists.txt b/indra/llconvexdecomposition/CMakeLists.txt new file mode 100644 index 00000000000..7dae884db8c --- /dev/null +++ b/indra/llconvexdecomposition/CMakeLists.txt @@ -0,0 +1,39 @@ +# -*- cmake -*- + +project(llconvexdecomposition) + +include(00-Common) +include(LLCommon) +include(LLMath) +include(VHACD) + +set(llconvexdecomposition_SOURCE_FILES + llconvexdecomposition.cpp + llconvexdecompositionvhacd.cpp + ) + +set(llconvexdecomposition_HEADER_FILES + CMakeLists.txt + llconvexdecomposition.h + llconvexdecompositionvhacd.h + ) + +set_source_files_properties(${llconvexdecomposition_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND llconvexdecomposition_SOURCE_FILES ${llconvexdecomposition_HEADER_FILES}) + +add_library (llconvexdecomposition ${llconvexdecomposition_SOURCE_FILES}) +target_include_directories(llconvexdecomposition INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(llconvexdecomposition + llcommon + llmath + ll::vhacd) + +if(WINDOWS) + target_compile_options(llconvexdecomposition PRIVATE /bigobj) +endif() + +# Add tests + diff --git a/indra/llconvexdecomposition/llconvexdecomposition.cpp b/indra/llconvexdecomposition/llconvexdecomposition.cpp new file mode 100644 index 00000000000..7b9d775c537 --- /dev/null +++ b/indra/llconvexdecomposition/llconvexdecomposition.cpp @@ -0,0 +1,83 @@ +/** +* @file llconvexdecomposition.cpp +* @author falcon@lindenlab.com +* @brief Inner implementation of LLConvexDecomposition interface +* +* $LicenseInfo:firstyear=2011&license=lgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2011, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "linden_common.h" + +#include "llconvexdecompositionvhacd.h" +#include "llconvexdecomposition.h" + +bool LLConvexDecomposition::s_isInitialized = false; + +// static +bool LLConvexDecomposition::isFunctional() +{ + return LLConvexDecompositionVHACD::isFunctional(); +} + +// static +LLConvexDecomposition* LLConvexDecomposition::getInstance() +{ + if ( !s_isInitialized ) + { + return nullptr; + } + else + { + return LLConvexDecompositionVHACD::getInstance(); + } +} + +// static +LLCDResult LLConvexDecomposition::initSystem() +{ + LLCDResult result = LLConvexDecompositionVHACD::initSystem(); + if ( result == LLCD_OK ) + { + s_isInitialized = true; + } + return result; +} + +// static +LLCDResult LLConvexDecomposition::initThread() +{ + return LLConvexDecompositionVHACD::initThread(); +} + +// static +LLCDResult LLConvexDecomposition::quitThread() +{ + return LLConvexDecompositionVHACD::quitThread(); +} + +// static +LLCDResult LLConvexDecomposition::quitSystem() +{ + return LLConvexDecompositionVHACD::quitSystem(); +} + + diff --git a/indra/llconvexdecomposition/llconvexdecomposition.h b/indra/llconvexdecomposition/llconvexdecomposition.h new file mode 100644 index 00000000000..8008bc6e123 --- /dev/null +++ b/indra/llconvexdecomposition/llconvexdecomposition.h @@ -0,0 +1,231 @@ +/** + * @file llconvexdecomposition.cpp + * @brief LLConvexDecomposition interface definition + * + * $LicenseInfo:firstyear=2011&license=lgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_CONVEX_DECOMPOSITION +#define LL_CONVEX_DECOMPOSITION + +typedef int bool32; + +#if defined(_WIN32) || defined(_WIN64) +#define LLCD_CALL __cdecl +#else +#define LLCD_CALL +#endif + +struct LLCDParam +{ + enum LLCDParamType + { + LLCD_INVALID = 0, + LLCD_INTEGER, + LLCD_FLOAT, + LLCD_BOOLEAN, + LLCD_ENUM + }; + + struct LLCDEnumItem + { + const char* mName; + int mValue; + }; + + union LLCDValue + { + float mFloat; + int mIntOrEnumValue; + bool32 mBool; + }; + + union LLCDParamDetails + { + struct { + LLCDValue mLow; + LLCDValue mHigh; + LLCDValue mDelta; + } mRange; + + struct { + int mNumEnums; + LLCDEnumItem* mEnumsArray; + } mEnumValues; + }; + + const char* mName; + const char* mDescription; + LLCDParamType mType; + LLCDParamDetails mDetails; + LLCDValue mDefault; + int mStage; + + // WARNING: Only the LLConvexDecomposition implementation + // should change this value + int mReserved; +}; + +struct LLCDStageData +{ + const char* mName; + const char* mDescription; + bool32 mSupportsCallback; +}; + +struct LLCDMeshData +{ + enum IndexType + { + INT_16, + INT_32 + }; + + const float* mVertexBase; + int mVertexStrideBytes; + int mNumVertices; + const void* mIndexBase; + IndexType mIndexType; + int mIndexStrideBytes; + int mNumTriangles; +}; + +struct LLCDHull +{ + const float* mVertexBase; + int mVertexStrideBytes; + int mNumVertices; +}; + +enum LLCDResult +{ + LLCD_OK = 0, + LLCD_UNKOWN_ERROR, + LLCD_NULL_PTR, + LLCD_INVALID_STAGE, + LLCD_UNKNOWN_PARAM, + LLCD_BAD_VALUE, + LLCD_REQUEST_OUT_OF_RANGE, + LLCD_INVALID_MESH_DATA, + LLCD_INVALID_HULL_DATA, + LLCD_STAGE_NOT_READY, + LLCD_INVALID_THREAD, + LLCD_NOT_IMPLEMENTED +}; + +// This callback will receive a string describing the current subtask being performed +// as well as a pair of numbers indicating progress. (The values should not be interpreted +// as a completion percentage as 'current' may be greater than 'final'.) +// If the callback returns zero, the decomposition will be terminated +typedef int (LLCD_CALL *llcdCallbackFunc)(const char* description, int current_progress, int final_progress); + +class LLConvexDecomposition +{ +public: + // Obtain a pointer to the actual implementation + static LLConvexDecomposition* getInstance(); + + /// @returns false if this is the stub + static bool isFunctional(); + + static LLCDResult initSystem(); + static LLCDResult initThread(); + static LLCDResult quitThread(); + static LLCDResult quitSystem(); + + // Generate a decomposition object handle + virtual void genDecomposition(int& decomp) = 0; + // Delete decomposition object handle + virtual void deleteDecomposition(int decomp) = 0; + // Bind given decomposition handle + // Commands operate on currently bound decomposition + virtual void bindDecomposition(int decomp) = 0; + + // Sets *paramsOut to the address of the LLCDParam array and returns + // the number of parameters + virtual int getParameters(const LLCDParam** paramsOut) = 0; + + + // Sets *stagesOut to the address of the LLCDStageData array and returns + // the number of stages + virtual int getStages(const LLCDStageData** stagesOut) = 0; + + + // Set a parameter by name. Pass enum values as integers. + virtual LLCDResult setParam(const char* name, float val) = 0; + virtual LLCDResult setParam(const char* name, int val) = 0; + virtual LLCDResult setParam(const char* name, bool val) = 0; + + + // Set incoming mesh data. Data is copied to local buffers and will + // persist until the next setMeshData call + virtual LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based ) = 0; + + + // Register a callback to be called periodically during the specified stage + // See the typedef above for more information + virtual LLCDResult registerCallback( int stage, llcdCallbackFunc callback ) = 0; + + + // Execute the specified decomposition stage + virtual LLCDResult executeStage(int stage) = 0; + virtual LLCDResult buildSingleHull() = 0 ; + + + // Gets the number of hulls generated by the specified decompositions stage + virtual int getNumHullsFromStage(int stage) = 0; + + + // Populates hullOut to reference the internal copy of the requested hull + // The data will persist only until the next executeStage call for that stage. + virtual LLCDResult getHullFromStage( int stage, int hull, LLCDHull* hullOut ) = 0; + + virtual LLCDResult getSingleHull( LLCDHull* hullOut ) = 0 ; + + + // TODO: Implement lock of some kind to disallow this call if data not yet ready + // Populates the meshDataOut to reference the utility's copy of the mesh geometry + // for the hull and stage specified. + // You must copy this data if you want to continue using it after the next executeStage + // call + virtual LLCDResult getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut) = 0; + + + // Creates a mesh from hullIn and temporarily stores it internally in the utility. + // The mesh data persists only until the next call to getMeshFromHull + virtual LLCDResult getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ) = 0; + + // Takes meshIn, generates a single convex hull from it, converts that to a mesh + // stored internally, and populates meshOut to reference the internally stored data. + // The data is persistent only until the next call to generateSingleHullMeshFromMesh + virtual LLCDResult generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut) = 0; + + // + /// Debug + virtual void loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut) = 0; + +private: + static bool s_isInitialized; +}; + +#endif //LL_CONVEX_DECOMPOSITION + diff --git a/indra/llconvexdecomposition/llconvexdecompositionvhacd.cpp b/indra/llconvexdecomposition/llconvexdecompositionvhacd.cpp new file mode 100644 index 00000000000..78876f9f365 --- /dev/null +++ b/indra/llconvexdecomposition/llconvexdecompositionvhacd.cpp @@ -0,0 +1,492 @@ +/** +* @file llconvexdecompositionvhacd.cpp +* @author rye@alchemyviewer.org +* @brief A VHACD based implementation of LLConvexDecomposition +* +* $LicenseInfo:firstyear=2025&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2025, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#include "linden_common.h" + +#include "llmath.h" +#include "v3math.h" + +#include +#include + +#define ENABLE_VHACD_IMPLEMENTATION 1 +#include "VHACD.h" + +#include "llconvexdecompositionvhacd.h" + +constexpr S32 MAX_HULLS = 256; +constexpr S32 MAX_VERTICES_PER_HULL = 256; + +bool LLConvexDecompositionVHACD::isFunctional() +{ + return true; +} + +LLConvexDecomposition* LLConvexDecompositionVHACD::getInstance() +{ + return LLSimpleton::getInstance(); +} + +LLCDResult LLConvexDecompositionVHACD::initSystem() +{ + createInstance(); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::initThread() +{ + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::quitThread() +{ + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::quitSystem() +{ + deleteSingleton(); + return LLCD_OK; +} + +LLConvexDecompositionVHACD::LLConvexDecompositionVHACD() +{ + //Create our vhacd instance and setup default parameters + mVHACD = VHACD::CreateVHACD(); + + mVHACDParameters.m_callback = &mVHACDCallback; + mVHACDParameters.m_logger = &mVHACDLogger; + + mDecompStages[0].mName = "Analyze"; + mDecompStages[0].mDescription = nullptr; + + LLCDParam param; + param.mName = "Fill Mode"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_ENUM; + param.mDetails.mEnumValues.mNumEnums = 3; + + static LLCDParam::LLCDEnumItem fill_enums[3]; + fill_enums[(size_t)VHACD::FillMode::FLOOD_FILL].mName = "Flood"; + fill_enums[(size_t)VHACD::FillMode::FLOOD_FILL].mValue = (int)VHACD::FillMode::FLOOD_FILL; + fill_enums[(size_t)VHACD::FillMode::SURFACE_ONLY].mName = "Surface Only"; + fill_enums[(size_t)VHACD::FillMode::SURFACE_ONLY].mValue = (int)VHACD::FillMode::SURFACE_ONLY; + fill_enums[(size_t)VHACD::FillMode::RAYCAST_FILL].mName = "Raycast"; + fill_enums[(size_t)VHACD::FillMode::RAYCAST_FILL].mValue = (int)VHACD::FillMode::RAYCAST_FILL; + + param.mDetails.mEnumValues.mEnumsArray = fill_enums; + param.mDefault.mIntOrEnumValue = (int)VHACD::FillMode::FLOOD_FILL; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + enum EVoxelQualityLevels + { + E_LOW_QUALITY = 0, + E_NORMAL_QUALITY, + E_HIGH_QUALITY, + E_VERY_HIGH_QUALITY, + E_ULTRA_QUALITY, + E_MAX_QUALITY, + E_NUM_QUALITY_LEVELS + }; + + param.mName = "Voxel Resolution"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_ENUM; + param.mDetails.mEnumValues.mNumEnums = E_NUM_QUALITY_LEVELS; + + static LLCDParam::LLCDEnumItem voxel_quality_enums[E_NUM_QUALITY_LEVELS]; + voxel_quality_enums[E_LOW_QUALITY].mName = "Low"; + voxel_quality_enums[E_LOW_QUALITY].mValue = 200000; + voxel_quality_enums[E_NORMAL_QUALITY].mName = "Normal"; + voxel_quality_enums[E_NORMAL_QUALITY].mValue = 400000; + voxel_quality_enums[E_HIGH_QUALITY].mName = "High"; + voxel_quality_enums[E_HIGH_QUALITY].mValue = 800000; + voxel_quality_enums[E_VERY_HIGH_QUALITY].mName = "Very High"; + voxel_quality_enums[E_VERY_HIGH_QUALITY].mValue = 1200000; + voxel_quality_enums[E_ULTRA_QUALITY].mName = "Ultra"; + voxel_quality_enums[E_ULTRA_QUALITY].mValue = 1600000; + voxel_quality_enums[E_MAX_QUALITY].mName = "Maximum"; + voxel_quality_enums[E_MAX_QUALITY].mValue = 2000000; + + param.mDetails.mEnumValues.mEnumsArray = voxel_quality_enums; + param.mDefault.mIntOrEnumValue = 400000; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + param.mName = "Num Hulls"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_FLOAT; + param.mDetails.mRange.mLow.mFloat = 1.f; + param.mDetails.mRange.mHigh.mFloat = MAX_HULLS; + param.mDetails.mRange.mDelta.mFloat = 1.f; + param.mDefault.mFloat = 8.f; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + param.mName = "Num Vertices"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_FLOAT; + param.mDetails.mRange.mLow.mFloat = 3.f; + param.mDetails.mRange.mHigh.mFloat = MAX_VERTICES_PER_HULL; + param.mDetails.mRange.mDelta.mFloat = 1.f; + param.mDefault.mFloat = 32.f; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + param.mName = "Error Tolerance"; + param.mDescription = nullptr; + param.mType = LLCDParam::LLCD_FLOAT; + param.mDetails.mRange.mLow.mFloat = 0.0001f; + param.mDetails.mRange.mHigh.mFloat = 99.f; + param.mDetails.mRange.mDelta.mFloat = 0.001f; + param.mDefault.mFloat = 1.f; + param.mStage = 0; + param.mReserved = -1; + mDecompParams.push_back(param); + + for (const LLCDParam& param : mDecompParams) + { + const char* const name = param.mName; + + switch (param.mType) + { + case LLCDParam::LLCD_FLOAT: + { + setParam(name, param.mDefault.mFloat); + break; + } + case LLCDParam::LLCD_ENUM: + case LLCDParam::LLCD_INTEGER: + { + setParam(name, param.mDefault.mIntOrEnumValue); + break; + } + case LLCDParam::LLCD_BOOLEAN: + { + setParam(name, (param.mDefault.mBool != 0)); + break; + } + case LLCDParam::LLCD_INVALID: + default: + { + break; + } + } + } +} + +LLConvexDecompositionVHACD::~LLConvexDecompositionVHACD() +{ + mBoundDecomp = nullptr; + mDecompData.clear(); + + mVHACD->Release(); +} + +void LLConvexDecompositionVHACD::genDecomposition(int& decomp) +{ + int new_decomp_id = static_cast(mDecompData.size()) + 1; + mDecompData[new_decomp_id] = LLDecompData(); + decomp = new_decomp_id; +} + +void LLConvexDecompositionVHACD::deleteDecomposition(int decomp) +{ + auto iter = mDecompData.find(decomp); + if (iter != mDecompData.end()) + { + if (mBoundDecomp == &iter->second) + { + mBoundDecomp = nullptr; + } + mDecompData.erase(iter); + } +} + +void LLConvexDecompositionVHACD::bindDecomposition(int decomp) +{ + auto iter = mDecompData.find(decomp); + if (iter != mDecompData.end()) + { + mBoundDecomp = &iter->second; + } + else + { + LL_WARNS() << "Failed to bind unknown decomposition: " << decomp << LL_ENDL; + mBoundDecomp = nullptr; + } +} + +LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, float val) +{ + if (name == std::string("Num Hulls")) + { + mVHACDParameters.m_maxConvexHulls = llclamp(ll_round(val), 1, MAX_HULLS); + } + else if (name == std::string("Num Vertices")) + { + mVHACDParameters.m_maxNumVerticesPerCH = llclamp(ll_round(val), 3, MAX_VERTICES_PER_HULL); + } + else if (name == std::string("Error Tolerance")) + { + mVHACDParameters.m_minimumVolumePercentErrorAllowed = val; + } + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, bool val) +{ + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, int val) +{ + if (name == std::string("Fill Mode")) + { + mVHACDParameters.m_fillMode = (VHACD::FillMode)val; + } + else if (name == std::string("Voxel Resolution")) + { + mVHACDParameters.m_resolution = val; + } + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::setMeshData( const LLCDMeshData* data, bool vertex_based ) +{ + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + return mBoundDecomp->mSourceMesh.from(data, vertex_based); +} + +LLCDResult LLConvexDecompositionVHACD::registerCallback(int stage, llcdCallbackFunc callback ) +{ + if (stage == 0) + { + mVHACDCallback.setCallbackFunc(callback); + return LLCD_OK; + } + else + { + return LLCD_INVALID_STAGE; + } +} + +LLCDResult LLConvexDecompositionVHACD::executeStage(int stage) +{ + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + if (stage != 0) + { + return LLCD_INVALID_STAGE; + } + + mBoundDecomp->mDecomposedHulls.clear(); + + const auto& decomp_mesh = mBoundDecomp->mSourceMesh; + if (!mVHACD->Compute((const double* const)decomp_mesh.mVertices.data(), static_cast(decomp_mesh.mVertices.size()), (const uint32_t* const)decomp_mesh.mIndices.data(), static_cast(decomp_mesh.mIndices.size()), mVHACDParameters)) + { + return LLCD_INVALID_HULL_DATA; + } + + uint32_t num_nulls = mVHACD->GetNConvexHulls(); + if (num_nulls == 0) + { + return LLCD_INVALID_HULL_DATA; + } + + for (uint32_t i = 0; num_nulls > i; ++i) + { + VHACD::IVHACD::ConvexHull ch; + if (!mVHACD->GetConvexHull(i, ch)) + continue; + + LLConvexMesh out_mesh; + out_mesh.setVertices(ch.m_points); + out_mesh.setIndices(ch.m_triangles); + + mBoundDecomp->mDecomposedHulls.push_back(std::move(out_mesh)); + } + + mVHACD->Clean(); + + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::buildSingleHull() +{ + LL_INFOS() << "Building single hull mesh" << LL_ENDL; + if (!mBoundDecomp || mBoundDecomp->mSourceMesh.mVertices.empty()) + { + return LLCD_NULL_PTR; + } + + mBoundDecomp->mSingleHullMesh.clear(); + + VHACD::QuickHull quickhull; + uint32_t num_tris = quickhull.ComputeConvexHull(mBoundDecomp->mSourceMesh.mVertices, MAX_VERTICES_PER_HULL); + if (num_tris > 0) + { + mBoundDecomp->mSingleHullMesh.setVertices(quickhull.GetVertices()); + mBoundDecomp->mSingleHullMesh.setIndices(quickhull.GetIndices()); + + return LLCD_OK; + } + + return LLCD_INVALID_MESH_DATA; +} + +int LLConvexDecompositionVHACD::getNumHullsFromStage(int stage) +{ + if (!mBoundDecomp || stage != 0) + { + return 0; + } + + return narrow(mBoundDecomp->mDecomposedHulls.size()); +} + +LLCDResult LLConvexDecompositionVHACD::getSingleHull( LLCDHull* hullOut ) +{ + memset( hullOut, 0, sizeof(LLCDHull) ); + + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + if (mBoundDecomp->mSingleHullMesh.vertices.empty()) + { + return LLCD_INVALID_HULL_DATA; + } + + mBoundDecomp->mSingleHullMesh.to(hullOut); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::getHullFromStage( int stage, int hull, LLCDHull* hullOut ) +{ + memset( hullOut, 0, sizeof(LLCDHull) ); + + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + if (stage != 0) + { + return LLCD_INVALID_STAGE; + } + + if (mBoundDecomp->mDecomposedHulls.empty() || mBoundDecomp->mDecomposedHulls.size() <= hull) + { + return LLCD_REQUEST_OUT_OF_RANGE; + } + + mBoundDecomp->mDecomposedHulls[hull].to(hullOut); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut ) +{ + memset( meshDataOut, 0, sizeof(LLCDMeshData)); + if (!mBoundDecomp) + { + return LLCD_NULL_PTR; + } + + if (stage != 0) + { + return LLCD_INVALID_STAGE; + } + + if (mBoundDecomp->mDecomposedHulls.empty() || mBoundDecomp->mDecomposedHulls.size() <= hull) + { + return LLCD_REQUEST_OUT_OF_RANGE; + } + + mBoundDecomp->mDecomposedHulls[hull].to(meshDataOut); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ) +{ + memset(meshOut, 0, sizeof(LLCDMeshData)); + + LLVHACDMesh inMesh(hullIn); + + VHACD::QuickHull quickhull; + uint32_t num_tris = quickhull.ComputeConvexHull(inMesh.mVertices, MAX_VERTICES_PER_HULL); + if (num_tris > 0) + { + mMeshFromHullData.setVertices(quickhull.GetVertices()); + mMeshFromHullData.setIndices(quickhull.GetIndices()); + + mMeshFromHullData.to(meshOut); + return LLCD_OK; + } + + return LLCD_INVALID_HULL_DATA; +} + +LLCDResult LLConvexDecompositionVHACD::generateSingleHullMeshFromMesh(LLCDMeshData* meshIn, LLCDMeshData* meshOut) +{ + memset( meshOut, 0, sizeof(LLCDMeshData) ); + + LLVHACDMesh inMesh(meshIn, true); + + VHACD::QuickHull quickhull; + uint32_t num_tris = quickhull.ComputeConvexHull(inMesh.mVertices, MAX_VERTICES_PER_HULL); + if (num_tris > 0) + { + mSingleHullMeshFromMeshData.setVertices(quickhull.GetVertices()); + mSingleHullMeshFromMeshData.setIndices(quickhull.GetIndices()); + + mSingleHullMeshFromMeshData.to(meshOut); + return LLCD_OK; + } + + return LLCD_INVALID_MESH_DATA; +} + +void LLConvexDecompositionVHACD::loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut) +{ + static LLCDMeshData meshData; + memset( &meshData, 0, sizeof(LLCDMeshData) ); + *meshDataOut = &meshData; +} diff --git a/indra/llconvexdecomposition/llconvexdecompositionvhacd.h b/indra/llconvexdecomposition/llconvexdecompositionvhacd.h new file mode 100644 index 00000000000..675356629c4 --- /dev/null +++ b/indra/llconvexdecomposition/llconvexdecompositionvhacd.h @@ -0,0 +1,339 @@ +/** +* @file llconvexdecompositionvhacd.h +* @author rye@alchemyviewer.org +* @brief A VHACD based implementation of LLConvexDecomposition +* +* $LicenseInfo:firstyear=2025&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2025, Linden Research, Inc. +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; +* version 2.1 of the License only. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +* $/LicenseInfo$ +*/ + +#ifndef LL_CONVEX_DECOMP_UTIL_VHACD_H +#define LL_CONVEX_DECOMP_UTIL_VHACD_H + +#include "llconvexdecomposition.h" +#include "llsingleton.h" +#include "llmath.h" + +#include + +#include "VHACD.h" + +class LLDecompDataVHACD; + +class LLConvexDecompositionVHACD : public LLSimpleton, public LLConvexDecomposition +{ + class VHACDCallback : public VHACD::IVHACD::IUserCallback + { + public: + void Update(const double overallProgress, const double stageProgress, const char* const stage, const char* operation) override + { + std::string out_msg = llformat("Stage: %s Operation: %s", stage, operation); + if (mCurrentStage != stage && mCurrentOperation != operation) + { + mCurrentStage = stage; + mCurrentOperation = operation; + LL_INFOS("VHACD") << out_msg << LL_ENDL; + } + + if(mCallbackFunc) + { + mCallbackFunc(out_msg.c_str(), ll_round(static_cast(stageProgress)), ll_round(static_cast(overallProgress))); + } + } + + void setCallbackFunc(llcdCallbackFunc func) + { + mCallbackFunc = func; + } + + private: + std::string mCurrentStage; + std::string mCurrentOperation; + llcdCallbackFunc mCallbackFunc = nullptr; + }; + + class VHACDLogger : public VHACD::IVHACD::IUserLogger + { + void Log(const char* const msg) override + { + LL_INFOS("VHACD") << msg << LL_ENDL; + } + }; + +public: + + LLConvexDecompositionVHACD(); + virtual ~LLConvexDecompositionVHACD(); + + static bool isFunctional(); + static LLConvexDecomposition* getInstance(); + static LLCDResult initSystem(); + static LLCDResult initThread(); + static LLCDResult quitThread(); + static LLCDResult quitSystem(); + + void genDecomposition(int& decomp); + void deleteDecomposition(int decomp); + void bindDecomposition(int decomp); + + // Sets *paramsOut to the address of the LLCDParam array and returns + // the length of the array + int getParameters(const LLCDParam** paramsOut) + { + *paramsOut = mDecompParams.data(); + return narrow(mDecompParams.size()); + } + + int getStages(const LLCDStageData** stagesOut) + { + *stagesOut = mDecompStages.data(); + return narrow(mDecompStages.size()); + } + + // Set a parameter by name. Returns false if out of bounds or unsupported parameter + LLCDResult setParam(const char* name, float val); + LLCDResult setParam(const char* name, int val); + LLCDResult setParam(const char* name, bool val); + LLCDResult setMeshData( const LLCDMeshData* data, bool vertex_based ); + LLCDResult registerCallback(int stage, llcdCallbackFunc callback ); + + LLCDResult executeStage(int stage); + LLCDResult buildSingleHull(); + + int getNumHullsFromStage(int stage); + + LLCDResult getHullFromStage( int stage, int hull, LLCDHull* hullOut ); + LLCDResult getSingleHull( LLCDHull* hullOut ) ; + + // TODO: Implement lock of some kind to disallow this call if data not yet ready + LLCDResult getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut); + LLCDResult getMeshFromHull( LLCDHull* hullIn, LLCDMeshData* meshOut ); + + // For visualizing convex hull shapes in the viewer physics shape display + LLCDResult generateSingleHullMeshFromMesh( LLCDMeshData* meshIn, LLCDMeshData* meshOut); + + /// Debug + void loadMeshData(const char* fileIn, LLCDMeshData** meshDataOut); + +private: + std::vector mDecompParams; + std::array mDecompStages; + + struct LLVHACDMesh + { + using vertex_type = VHACD::Vertex; + using index_type = VHACD::Triangle; + using vertex_array_type = std::vector; + using index_array_type = std::vector; + + LLVHACDMesh() = default; + LLVHACDMesh(const LLCDHull* hullIn) + { + if (hullIn) + { + from(hullIn); + } + }; + + LLVHACDMesh(const LLCDMeshData* meshIn, bool vertex_based) + { + if (meshIn) + { + from(meshIn, vertex_based); + } + }; + + void clear() + { + mVertices.clear(); + mIndices.clear(); + } + + void setVertices(const float* data, int num_vertices, int vertex_stride_bytes) + { + vertex_array_type vertices; + vertices.reserve(num_vertices); + + const int stride = vertex_stride_bytes / sizeof(float); + for (int i = 0; i < num_vertices; ++i) + { + vertices.emplace_back(data[i * stride + 0], + data[i * stride + 1], + data[i * stride + 2]); + } + + mVertices = std::move(vertices); + } + + void setIndices(const void* data, int num_indices, int index_stride_bytes, LLCDMeshData::IndexType type) + { + index_array_type indices; + indices.reserve(num_indices); + + if (type == LLCDMeshData::INT_16) + { + const U16* index_data = static_cast(data); + const int stride = index_stride_bytes / sizeof(U16); + for (int i = 0; i < num_indices; ++i) + { + indices.emplace_back(index_data[i * stride + 0], + index_data[i * stride + 1], + index_data[i * stride + 2]); + } + } + else + { + const U32* index_data = static_cast(data); + const int stride = index_stride_bytes / sizeof(U32); + for (int i = 0; i < num_indices; ++i) + { + indices.emplace_back(index_data[i * stride + 0], + index_data[i * stride + 1], + index_data[i * stride + 2]); + } + } + + mIndices = std::move(indices); + } + + LLCDResult from(const LLCDHull* hullIn) + { + clear(); + + if (!hullIn || !hullIn->mVertexBase || (hullIn->mNumVertices < 3) || (hullIn->mVertexStrideBytes != 12 && hullIn->mVertexStrideBytes != 16)) + { + return LLCD_INVALID_HULL_DATA; + } + + setVertices(hullIn->mVertexBase, hullIn->mNumVertices, hullIn->mVertexStrideBytes); + + return LLCD_OK; + } + + LLCDResult from(const LLCDMeshData* meshIn, bool vertex_based) + { + clear(); + + if (!meshIn || !meshIn->mVertexBase || (meshIn->mNumVertices < 3) || (meshIn->mVertexStrideBytes != 12 && meshIn->mVertexStrideBytes != 16)) + { + return LLCD_INVALID_MESH_DATA; + } + + if (!vertex_based && ((meshIn->mNumTriangles < 1) || !meshIn->mIndexBase)) + { + return LLCD_INVALID_MESH_DATA; + } + + setVertices(meshIn->mVertexBase, meshIn->mNumVertices, meshIn->mVertexStrideBytes); + if(!vertex_based) + { + setIndices(meshIn->mIndexBase, meshIn->mNumTriangles, meshIn->mIndexStrideBytes, meshIn->mIndexType); + } + + return LLCD_OK; + } + + vertex_array_type mVertices; + index_array_type mIndices; + }; + + struct LLConvexMesh + { + using vertex_type = glm::vec3; + using index_type = glm::u32vec3; + using vertex_array_type = std::vector; + using index_array_type = std::vector; + + LLConvexMesh() = default; + + void clear() + { + vertices.clear(); + indices.clear(); + } + + void setVertices(const std::vector& in_vertices) + { + vertices.clear(); + vertices.reserve(in_vertices.size()); + + for (const auto& vertex : in_vertices) + { + vertices.emplace_back(narrow(vertex.mX), narrow(vertex.mY), narrow(vertex.mZ)); + } + } + + void setIndices(const std::vector& in_indices) + { + indices.clear(); + indices.reserve(in_indices.size()); + + for (const auto& triangle : in_indices) + { + indices.emplace_back(narrow(triangle.mI0), narrow(triangle.mI1), narrow(triangle.mI2)); + } + } + + void to(LLCDHull* meshOut) const + { + meshOut->mVertexBase = (float*)vertices.data(); + meshOut->mVertexStrideBytes = sizeof(vertex_type); + meshOut->mNumVertices = (int)vertices.size(); + } + + void to(LLCDMeshData* meshOut) const + { + meshOut->mVertexBase = (float*)vertices.data(); + meshOut->mVertexStrideBytes = sizeof(vertex_type); + meshOut->mNumVertices = (int)vertices.size(); + + meshOut->mIndexType = LLCDMeshData::INT_32; + meshOut->mIndexBase = indices.data(); + meshOut->mIndexStrideBytes = sizeof(index_type); + meshOut->mNumTriangles = (int)indices.size(); + } + + vertex_array_type vertices; + index_array_type indices; + }; + + struct LLDecompData + { + LLVHACDMesh mSourceMesh; + LLConvexMesh mSingleHullMesh; + + std::vector mDecomposedHulls; + }; + + std::unordered_map mDecompData; + + LLDecompData* mBoundDecomp = nullptr; + + VHACD::IVHACD* mVHACD = nullptr; + VHACDCallback mVHACDCallback; + VHACDLogger mVHACDLogger; + VHACD::IVHACD::Parameters mVHACDParameters; + + LLConvexMesh mMeshFromHullData; + LLConvexMesh mSingleHullMeshFromMeshData; +}; + +#endif //LL_CONVEX_DECOMP_UTIL_VHACD_H diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index e13f0bbd96d..9e90314a51a 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -70,6 +70,12 @@ target_link_libraries(llprimitive ll::glm ) +if (TARGET llconvexdecomposition) + target_link_libraries(llprimitive + llconvexdecomposition + ) +endif () + #add unit tests if (LL_TESTS) INCLUDE(LLAddBuildTest) diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 00ef79ce7fd..8055bffd32a 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -1296,10 +1296,10 @@ LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos) } void LLModel::setConvexHullDecomposition( - const LLModel::convex_hull_decomposition& decomp) + const LLModel::convex_hull_decomposition& decomp, const std::vector& decomp_mesh) { mPhysics.mHull = decomp; - mPhysics.mMesh.clear(); + mPhysics.mMesh = decomp_mesh; updateHullCenters(); } diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index 6501b3dc502..ac88af18f02 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -305,7 +305,8 @@ class LLModel : public LLVolume S32 mDecompID; void setConvexHullDecomposition( - const convex_hull_decomposition& decomp); + const convex_hull_decomposition& decomp, + const std::vector& decomp_mesh); void updateHullCenters(); LLVector3 mCenterOfHullCenters; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c727d5ae572..0f7670a57aa 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1709,10 +1709,6 @@ if (WINDOWS) list(APPEND viewer_SOURCE_FILES ${viewer_INSTALLER_FILES}) endif (WINDOWS) -if (HAVOK OR HAVOK_TPV) - set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_HAVOK") -endif (HAVOK OR HAVOK_TPV) - if( DEFINED LLSTARTUP_COMPILE_FLAGS ) # progress view disables/enables icons based on available packages set_source_files_properties(llprogressview.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}") @@ -2029,6 +2025,10 @@ if( TARGET ll::nvapi ) target_link_libraries(${VIEWER_BINARY_NAME} ll::nvapi ) endif() +if ( TARGET llconvexdecomposition ) + target_link_libraries(${VIEWER_BINARY_NAME} llconvexdecomposition ) +endif () + set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH "Path to artwork files.") diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index f76f39222b9..ef29be8200c 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -1035,8 +1035,13 @@ void LLFloaterModelPreview::onPhysicsStageExecute(LLUICtrl* ctrl, void* data) gMeshRepo.mDecompThread->submitRequest(request); } } - - if (stage == "Decompose") + if (stage == "Analyze") + { + sInstance->setStatusMessage(sInstance->getString("decomposing")); + sInstance->childSetVisible("Analyze", false); + sInstance->childSetVisible("analyze_cancel", true); + } + else if (stage == "Decompose") { sInstance->setStatusMessage(sInstance->getString("decomposing")); sInstance->childSetVisible("Decompose", false); @@ -1137,6 +1142,7 @@ void LLFloaterModelPreview::initDecompControls() childSetCommitCallback("simplify_cancel", onPhysicsStageCancel, NULL); childSetCommitCallback("decompose_cancel", onPhysicsStageCancel, NULL); + childSetCommitCallback("analyze_cancel", onPhysicsStageCancel, NULL); childSetCommitCallback("physics_lod_combo", onPhysicsUseLOD, NULL); childSetCommitCallback("physics_browse", onPhysicsBrowse, NULL); @@ -2018,7 +2024,7 @@ void LLFloaterModelPreview::DecompRequest::completed() { //called from the main thread if (mContinue) { - mModel->setConvexHullDecomposition(mHull); + mModel->setConvexHullDecomposition(mHull, mHullMesh); if (sInstance) { diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index e0649e1d88b..d71f3682327 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -2626,7 +2626,16 @@ void LLModelPreview::updateStatusMessages() //enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean(); //enable/disable "analysis" UI - LLPanel* panel = fmp->getChild("physics analysis"); +#if LL_HAVOK + LLPanel* panel = fmp->getChild("physics simplification"); + panel->setVisible(true); + + panel = fmp->getChild("physics analysis havok"); + panel->setVisible(true); +#else + LLPanel* panel = fmp->getChild("physics analysis vhacd"); + panel->setVisible(true); +#endif LLView* child = panel->getFirstChild(); while (child) { @@ -2650,6 +2659,8 @@ void LLModelPreview::updateStatusMessages() fmp->childSetVisible("simplify_cancel", false); fmp->childSetVisible("Decompose", true); fmp->childSetVisible("decompose_cancel", false); + fmp->childSetVisible("Analyze", true); + fmp->childSetVisible("analyze_cancel", false); if (phys_hulls > 0) { @@ -2659,6 +2670,7 @@ void LLModelPreview::updateStatusMessages() if (phys_tris || phys_hulls > 0) { fmp->childEnable("Decompose"); + fmp->childEnable("Analyze"); } } else diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index d72d428c085..fb9e5fd0a98 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -1070,7 +1070,7 @@ void LLViewerObjectList::fetchObjectCostsCoro(std::string url) if (diff.empty()) { - LL_INFOS() << "No outstanding object IDs to request. Pending count: " << mPendingObjectCost.size() << LL_ENDL; + LL_DEBUGS() << "No outstanding object IDs to request. Pending count: " << mPendingObjectCost.size() << LL_ENDL; return; } @@ -1205,7 +1205,7 @@ void LLViewerObjectList::fetchPhisicsFlagsCoro(std::string url) if (idList.size() < 1) { - LL_INFOS() << "No outstanding object physics flags to request." << LL_ENDL; + LL_DEBUGS() << "No outstanding object physics flags to request." << LL_ENDL; return; } diff --git a/indra/newview/skins/default/xui/en/floater_model_preview.xml b/indra/newview/skins/default/xui/en/floater_model_preview.xml index f11d6878405..39e9de09801 100644 --- a/indra/newview/skins/default/xui/en/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_model_preview.xml @@ -40,7 +40,7 @@ Simplifying... TBD One or more textures in this model were scaled to be within the allowed limits. - + Skinning disabled due to too many joints: [JOINTS], maximum: [MAX] Rigged to unrecognized joint name [NAME] @@ -807,7 +807,7 @@ help_topic="upload_model_physics" label="Physics" name="physics_panel"> - + --> - + - + + + Step 2: Convert to hulls (optional) + + + Fill Mode: + + + Resolution: + + + Hulls per Mesh: + + + Vertices per hull: + + + Error tolerance: + + + + + + +