diff options
Diffstat (limited to 'indra/llphysicsextensionsos/llconvexdecompositionvhacd.cpp')
| -rw-r--r-- | indra/llphysicsextensionsos/llconvexdecompositionvhacd.cpp | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/indra/llphysicsextensionsos/llconvexdecompositionvhacd.cpp b/indra/llphysicsextensionsos/llconvexdecompositionvhacd.cpp new file mode 100644 index 0000000000..b7c4b5e591 --- /dev/null +++ b/indra/llphysicsextensionsos/llconvexdecompositionvhacd.cpp @@ -0,0 +1,551 @@ +/** +* @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 <string.h> +#include <memory> + +#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() +{ + // Setup default parameters + 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() +{ + { + LLMutexLock lock(&mDecompDataMutex); + mBoundDecompID = INVALID_DECOMP_ID; + mDecompData.clear(); + } +} + +void LLConvexDecompositionVHACD::genDecomposition(int& decomp) +{ + LLMutexLock lock(&mDecompDataMutex); + + mDecompData[mNextDecompID] = std::make_shared<LLDecompData>(); + decomp = mNextDecompID; + + ++mNextDecompID; // Increment decomposition ID. Never reuse to protect downstream consumers from misuse +} + +void LLConvexDecompositionVHACD::deleteDecomposition(int decomp) +{ + LLMutexLock lock(&mDecompDataMutex); + + auto iter = mDecompData.find(decomp); + if (iter != mDecompData.end()) + { + if (mBoundDecompID == decomp) + { + mBoundDecompID = INVALID_DECOMP_ID; + } + mDecompData.erase(iter); + } +} + +void LLConvexDecompositionVHACD::bindDecomposition(int decomp) +{ + LLMutexLock lock(&mDecompDataMutex); + + if (mDecompData.contains(decomp)) + { + mBoundDecompID = decomp; + } + else + { + LL_WARNS() << "Failed to bind unknown decomposition: " << decomp << LL_ENDL; + mBoundDecompID = INVALID_DECOMP_ID; + } +} + +LLConvexDecompositionVHACD::data_ptr_t LLConvexDecompositionVHACD::getBoundDecomp() +{ + data_ptr_t bound_decomp; + { + LLMutexLock lock(&mDecompDataMutex); + auto it = mDecompData.find(mBoundDecompID); + if (it != mDecompData.end()) + { + bound_decomp = it->second; // Take a copy of the shared_ptr to avoid potential deletion + } + } + return bound_decomp; +} + +LLCDResult LLConvexDecompositionVHACD::setParam(const char* name, float val) +{ + LLMutexLock lock(&mParamsMutex); + + using namespace std::literals; + + if (name == "Num Hulls"sv) + { + mVHACDParameters.m_maxConvexHulls = llclamp(ll_round(val), 1, MAX_HULLS); + } + else if (name == "Num Vertices"sv) + { + mVHACDParameters.m_maxNumVerticesPerCH = llclamp(ll_round(val), 3, MAX_VERTICES_PER_HULL); + } + else if (name == "Error Tolerance"sv) + { + 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) +{ + LLMutexLock lock(&mParamsMutex); + + using namespace std::literals; + + if (name == "Fill Mode"sv) + { + mVHACDParameters.m_fillMode = (VHACD::FillMode)val; + } + else if (name == "Voxel Resolution"sv) + { + mVHACDParameters.m_resolution = val; + } + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::setMeshData( const LLCDMeshData* data, bool vertex_based ) +{ + data_ptr_t bound_decomp = getBoundDecomp(); + if (!bound_decomp) + { + return LLCD_NULL_PTR; + } + + return bound_decomp->mSourceMesh.from(data, vertex_based); +} + +LLCDResult LLConvexDecompositionVHACD::registerCallback(int stage, llcdCallbackFunc callback ) +{ + if (stage == 0) + { + LLMutexLock lock(&mParamsMutex); + mCurrentCallbackFunc = callback; + return LLCD_OK; + } + else + { + return LLCD_INVALID_STAGE; + } +} + +LLCDResult LLConvexDecompositionVHACD::executeStage(int stage) +{ + if (stage != 0) + { + return LLCD_INVALID_STAGE; + } + + data_ptr_t bound_decomp = getBoundDecomp(); + if (!bound_decomp) + { + return LLCD_NULL_PTR; + } + + bound_decomp->mDecomposedHulls.clear(); + + const auto& decomp_mesh = bound_decomp->mSourceMesh; + + VHACDCallback callbacks; + VHACD::IVHACD::Parameters current_params; + { + LLMutexLock lock(&mParamsMutex); + + current_params = mVHACDParameters; + callbacks.setCallbackFunc(mCurrentCallbackFunc); + } + current_params.m_callback = &callbacks; + + auto vhacd_impl = VHACD::CreateVHACD(); + if (!vhacd_impl) + { + LL_WARNS() << "Failed to create VHACD instance" << LL_ENDL; + return LLCD_NULL_PTR; + } + + + if (!vhacd_impl->Compute((const double*)decomp_mesh.mVertices.data(), static_cast<uint32_t>(decomp_mesh.mVertices.size()), + (const uint32_t*)decomp_mesh.mIndices.data(), static_cast<uint32_t>(decomp_mesh.mIndices.size()), + current_params)) + { + vhacd_impl->Release(); + return LLCD_INVALID_HULL_DATA; + } + + uint32_t num_convex_hulls = vhacd_impl->GetNConvexHulls(); + if (num_convex_hulls == 0) + { + vhacd_impl->Release(); + return LLCD_INVALID_HULL_DATA; + } + + for (uint32_t i = 0; num_convex_hulls > i; ++i) + { + VHACD::IVHACD::ConvexHull ch; + if (!vhacd_impl->GetConvexHull(i, ch)) + continue; + + LLConvexMesh out_mesh; + out_mesh.setVertices(ch.m_points); + out_mesh.setIndices(ch.m_triangles); + + bound_decomp->mDecomposedHulls.push_back(std::move(out_mesh)); + } + + vhacd_impl->Release(); + + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::buildSingleHull() +{ + LL_INFOS() << "Building single hull mesh" << LL_ENDL; + + data_ptr_t bound_decomp = getBoundDecomp(); + if (!bound_decomp || bound_decomp->mSourceMesh.mVertices.empty()) + { + return LLCD_NULL_PTR; + } + + bound_decomp->mSingleHullMesh.clear(); + + VHACD::QuickHull quickhull; + uint32_t num_tris = quickhull.ComputeConvexHull(bound_decomp->mSourceMesh.mVertices, MAX_VERTICES_PER_HULL); + if (num_tris > 0) + { + bound_decomp->mSingleHullMesh.setVertices(quickhull.GetVertices()); + bound_decomp->mSingleHullMesh.setIndices(quickhull.GetIndices()); + + return LLCD_OK; + } + + return LLCD_INVALID_MESH_DATA; +} + +int LLConvexDecompositionVHACD::getNumHullsFromStage(int stage) +{ + data_ptr_t bound_decomp = getBoundDecomp(); + if (!bound_decomp || stage != 0) + { + return 0; + } + + return narrow(bound_decomp->mDecomposedHulls.size()); +} + +LLCDResult LLConvexDecompositionVHACD::getSingleHull( LLCDHull* hullOut ) +{ + memset( hullOut, 0, sizeof(LLCDHull) ); + + data_ptr_t bound_decomp = getBoundDecomp(); + if (!bound_decomp) + { + return LLCD_NULL_PTR; + } + + if (bound_decomp->mSingleHullMesh.vertices.empty()) + { + return LLCD_INVALID_HULL_DATA; + } + + bound_decomp->mSingleHullMesh.to(hullOut); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::getHullFromStage( int stage, int hull, LLCDHull* hullOut ) +{ + memset( hullOut, 0, sizeof(LLCDHull) ); + + data_ptr_t bound_decomp = getBoundDecomp(); + if (!bound_decomp) + { + return LLCD_NULL_PTR; + } + + if (stage != 0) + { + return LLCD_INVALID_STAGE; + } + + if (bound_decomp->mDecomposedHulls.empty() || S32(bound_decomp->mDecomposedHulls.size()) <= hull) + { + return LLCD_REQUEST_OUT_OF_RANGE; + } + + bound_decomp->mDecomposedHulls[hull].to(hullOut); + return LLCD_OK; +} + +LLCDResult LLConvexDecompositionVHACD::getMeshFromStage( int stage, int hull, LLCDMeshData* meshDataOut ) +{ + memset( meshDataOut, 0, sizeof(LLCDMeshData)); + + data_ptr_t bound_decomp = getBoundDecomp(); + if (!bound_decomp) + { + return LLCD_NULL_PTR; + } + + if (stage != 0) + { + return LLCD_INVALID_STAGE; + } + + if (bound_decomp->mDecomposedHulls.empty() || S32(bound_decomp->mDecomposedHulls.size()) <= hull) + { + return LLCD_REQUEST_OUT_OF_RANGE; + } + + bound_decomp->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; +} |
