Initial VHACD based llconvexdecomposition

master
Rye 2025-09-18 13:38:54 -04:00 committed by Hecklezz
parent fba445762f
commit b6ba43bed5
17 changed files with 1418 additions and 153 deletions

View File

@ -185,54 +185,6 @@
<key>version</key> <key>version</key>
<string>5.1.0</string> <string>5.1.0</string>
</map> </map>
<key>ndPhysicsStub</key>
<map>
<key>license</key>
<string>HACD/LGPL</string>
<key>license_file</key>
<string>LICENSES/ndPhysicsStub.txt</string>
<key>name</key>
<string>ndPhysicsStub</string>
<key>platforms</key>
<map>
<key>darwin64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>b0f4fd59a72cf04db2cc426241a3850c</string>
<key>url</key>
<string>https://3p.firestormviewer.org/ndPhysicsStub-1.0-darwin64-252581439.tar.bz2</string>
</map>
<key>name</key>
<string>darwin</string>
</map>
<key>linux64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>c266a8d6124fc11e41a82c288f2bf8e4</string>
<key>url</key>
<string>https://3p.firestormviewer.org/ndPhysicsStub-1.202321033-linux64-202321033.tar.bz2</string>
</map>
<key>name</key>
<string>linux64</string>
</map>
<key>windows64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>02f70159e14c7b7213b22a0225508c46</string>
<key>url</key>
<string>https://3p.firestormviewer.org/ndPhysicsStub-1.0-windows64-202121823.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
</map>
</map>
<key>glod</key> <key>glod</key>
<map> <map>
<key>copyright</key> <key>copyright</key>
@ -1726,41 +1678,19 @@
<map> <map>
<key>platforms</key> <key>platforms</key>
<map> <map>
<key>darwin64</key> <key>common</key>
<map> <map>
<key>archive</key> <key>archive</key>
<map> <map>
<key>hash</key> <key>hash</key>
<string>f290b000b31f9e36f2489946cbc99f5e</string> <string>bc41438b10ac6474cf5560465a3662a64f9e65a81342e4c33f18f6694581c7ee28c9ee6f091c36e80a0b1e10c68205be71eb5f8e40fef115d2c744fc2bbfcb43</string>
<key>hash_algorithm</key>
<string>blake2b</string>
<key>url</key> <key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/59995/563653/llphysicsextensions_stub-1.0.542456-darwin64-542456.tar.bz2</string> <string>https://github.com/AlchemyViewer/llphysicsextensions_stub/releases/download/v1.0-cb4900e/llphysicsextensions_stub-1.0-common-17836965684.tar.zst</string>
</map> </map>
<key>name</key> <key>name</key>
<string>darwin64</string> <string>common</string>
</map>
<key>linux64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>711f4ec769e4b5f59ba25ee43c11bcbc</string>
<key>url</key>
<string>https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4724/14846/llphysicsextensions_stub-1.0.504712-linux64-504712.tar.bz2</string>
</map>
<key>name</key>
<string>linux64</string>
</map>
<key>windows64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>2e5f1f7046a49d8b0bc295aa878116bc</string>
<key>url</key>
<string>http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/60043/564063/llphysicsextensions_stub-1.0.542456-windows-542456.tar.bz2</string>
</map>
<key>name</key>
<string>windows64</string>
</map> </map>
</map> </map>
<key>license</key> <key>license</key>
@ -1770,7 +1700,7 @@
<key>copyright</key> <key>copyright</key>
<string>Copyright (c) 2010, Linden Research, Inc.</string> <string>Copyright (c) 2010, Linden Research, Inc.</string>
<key>version</key> <key>version</key>
<string>1.0.542456</string> <string>1.0</string>
<key>name</key> <key>name</key>
<string>llphysicsextensions_stub</string> <string>llphysicsextensions_stub</string>
</map> </map>
@ -3247,6 +3177,38 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>description</key> <key>description</key>
<string>Discord Social SDK</string> <string>Discord Social SDK</string>
</map> </map>
<key>vhacd</key>
<map>
<key>platforms</key>
<map>
<key>common</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>140d8fc952a10edb5f2d72ab405336019ef32cadfa64f0cfce76c9de4bc6268cbc87cc8cd89d3417fb78b531d441701afc8d016bafe4bd275df2707f7daf1387</string>
<key>hash_algorithm</key>
<string>blake2b</string>
<key>url</key>
<string>https://github.com/AlchemyViewer/3p-vhacd/releases/download/v4.1.0-r2/vhacd-4.1.0-r2-common-18166921729.tar.zst</string>
</map>
<key>name</key>
<string>common</string>
</map>
</map>
<key>license</key>
<string>BSD</string>
<key>license_file</key>
<string>LICENSES/vhacd.txt</string>
<key>copyright</key>
<string>Copyright (c) 2011, Khaled Mamou</string>
<key>version</key>
<string>4.1.0-r2</string>
<key>name</key>
<string>vhacd</string>
<key>description</key>
<string>Voxelized Hierarchical Approximate Convex Decomposition</string>
</map>
</map> </map>
<key>package_description</key> <key>package_description</key>
<map> <map>

View File

@ -127,6 +127,9 @@ endif (USE_TRACY)
add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llaudio)
add_subdirectory(${LIBS_OPEN_PREFIX}llappearance) add_subdirectory(${LIBS_OPEN_PREFIX}llappearance)
add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) 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}llcommon)
add_subdirectory(${LIBS_OPEN_PREFIX}llcorehttp) add_subdirectory(${LIBS_OPEN_PREFIX}llcorehttp)
add_subdirectory(${LIBS_OPEN_PREFIX}llimage) add_subdirectory(${LIBS_OPEN_PREFIX}llimage)

View File

@ -64,6 +64,7 @@ set(cmake_SOURCE_FILES
UI.cmake UI.cmake
UnixInstall.cmake UnixInstall.cmake
Variables.cmake Variables.cmake
VHACD.cmake
ViewerMiscLibs.cmake ViewerMiscLibs.cmake
VisualLeakDetector.cmake VisualLeakDetector.cmake
LibVLCPlugin.cmake LibVLCPlugin.cmake

View File

@ -22,69 +22,21 @@ if (HAVOK)
include(Havok) include(Havok)
use_prebuilt_binary(llphysicsextensions_source) use_prebuilt_binary(llphysicsextensions_source)
set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/src) set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/src)
if(DARWIN) target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions)
set(LLPHYSICSEXTENSIONS_STUB_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/stub) target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 )
# 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)
target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 )
endif()
elseif (HAVOK_TPV) elseif (HAVOK_TPV)
use_prebuilt_binary(llphysicsextensions_tpv) use_prebuilt_binary(llphysicsextensions_tpv)
# <FS:TJ> Done in newview/CMakeLists.txt for darwin if Havok is enabled if(WINDOWS)
if (NOT DARWIN) target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/llphysicsextensions_tpv.lib)
target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions_tpv) else()
target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 ) target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/libllphysicsextensions_tpv.a)
endif() endif()
# </FS:TJ> target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 )
# <FS:ND> include paths for LLs version and ours are different. else (HAVOK)
target_include_directories( llphysicsextensions_impl INTERFACE ${LIBS_PREBUILT_DIR}/include/llphysicsextensions) use_prebuilt_binary(llphysicsextensions_stub)
# </FS:ND> set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/stub)
target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensionsstub)
# <FS:ND> havok lib get installed to packages/lib endif (HAVOK)
link_directories( ${LIBS_PREBUILT_DIR}/lib )
# </FS:ND>
endif ()
if ((NOT HAVOK AND NOT HAVOK_TPV) OR DARWIN) # <FS:TJ/> ARM64 requires ndPhysicsStub
use_prebuilt_binary( ndPhysicsStub )
# <FS:ND> Don't set this variable, there is no need to build any stub source if using ndPhysicsStub
# set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/stub)
# </FS:ND>
# <FS:TJ> Use find_library to make our lives easier
find_library(ND_HACDCONVEXDECOMPOSITION_LIBRARY
NAMES
nd_hacdConvexDecomposition.lib
libnd_hacdConvexDecomposition.a
PATHS "${ARCH_PREBUILT_DIRS_RELEASE}" REQUIRED NO_DEFAULT_PATH)
find_library(HACD_LIBRARY
NAMES
hacd.lib
libhacd.a
PATHS "${ARCH_PREBUILT_DIRS_RELEASE}" REQUIRED NO_DEFAULT_PATH)
find_library(ND_PATHING_LIBRARY
NAMES
nd_pathing.lib
libnd_pathing.a
libnd_Pathing.a
PATHS "${ARCH_PREBUILT_DIRS_RELEASE}" REQUIRED NO_DEFAULT_PATH)
if (NOT HAVOK AND NOT HAVOK_TPV) # <FS:TJ/> Done in newview/CMakeLists.txt for darwin if Havok is enabled
target_link_libraries(llphysicsextensions_impl INTERFACE ${ND_HACDCONVEXDECOMPOSITION_LIBRARY} ${HACD_LIBRARY} ${ND_PATHING_LIBRARY})
endif()
# </FS:TJ>
# <FS:ND> include paths for LLs version and ours are different.
target_include_directories( llphysicsextensions_impl INTERFACE ${LIBS_PREBUILT_DIR}/include/ )
# </FS:ND>
endif ()
# <FS:ND> include paths for LLs version and ours are different. # <FS:ND> include paths for LLs version and ours are different.
#target_include_directories( llphysicsextensions_impl INTERFACE ${LIBS_PREBUILT_DIR}/include/llphysicsextensions) #target_include_directories( llphysicsextensions_impl INTERFACE ${LIBS_PREBUILT_DIR}/include/llphysicsextensions)

9
indra/cmake/VHACD.cmake Normal file
View File

@ -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/)

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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 <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()
{
//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<int>(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<uint32_t>(decomp_mesh.mVertices.size()), (const uint32_t* const)decomp_mesh.mIndices.data(), static_cast<uint32_t>(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;
}

View File

@ -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 <vector>
#include "VHACD.h"
class LLDecompDataVHACD;
class LLConvexDecompositionVHACD : public LLSimpleton<LLConvexDecompositionVHACD>, 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<F32>(stageProgress)), ll_round(static_cast<F32>(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<LLCDParam> mDecompParams;
std::array<LLCDStageData, 1> mDecompStages;
struct LLVHACDMesh
{
using vertex_type = VHACD::Vertex;
using index_type = VHACD::Triangle;
using vertex_array_type = std::vector<vertex_type>;
using index_array_type = std::vector<index_type>;
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<const U16*>(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<const U32*>(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<vertex_type>;
using index_array_type = std::vector<index_type>;
LLConvexMesh() = default;
void clear()
{
vertices.clear();
indices.clear();
}
void setVertices(const std::vector<VHACD::Vertex>& 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<VHACD::Triangle>& 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<LLConvexMesh> mDecomposedHulls;
};
std::unordered_map<int, LLDecompData> 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

View File

@ -71,6 +71,12 @@ target_link_libraries(llprimitive
ll::glm ll::glm
) )
if (TARGET llconvexdecomposition)
target_link_libraries(llprimitive
llconvexdecomposition
)
endif ()
if(LINUX) if(LINUX)
# GLIB uses pcre, so we need to keep it for Linux # GLIB uses pcre, so we need to keep it for Linux
target_link_libraries(ll::pcre) target_link_libraries(ll::pcre)

View File

@ -1314,10 +1314,10 @@ LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos)
} }
void LLModel::setConvexHullDecomposition( void LLModel::setConvexHullDecomposition(
const LLModel::convex_hull_decomposition& decomp) const LLModel::convex_hull_decomposition& decomp, const std::vector<LLModel::PhysicsMesh>& decomp_mesh)
{ {
mPhysics.mHull = decomp; mPhysics.mHull = decomp;
mPhysics.mMesh.clear(); mPhysics.mMesh = decomp_mesh;
updateHullCenters(); updateHullCenters();
} }

View File

@ -307,7 +307,8 @@ public:
S32 mDecompID; S32 mDecompID;
void setConvexHullDecomposition( void setConvexHullDecomposition(
const convex_hull_decomposition& decomp); const convex_hull_decomposition& decomp,
const std::vector<LLModel::PhysicsMesh>& decomp_mesh);
void updateHullCenters(); void updateHullCenters();
LLVector3 mCenterOfHullCenters; LLVector3 mCenterOfHullCenters;

View File

@ -2627,6 +2627,10 @@ if( TARGET ll::nvapi )
target_link_libraries(${VIEWER_BINARY_NAME} ll::nvapi ) target_link_libraries(${VIEWER_BINARY_NAME} ll::nvapi )
endif() endif()
if ( TARGET llconvexdecomposition )
target_link_libraries(${VIEWER_BINARY_NAME} llconvexdecomposition )
endif ()
if (LINUX) if (LINUX)
# <FS:Zi> put these additional libraries in the viewer build target here as it didn't # <FS:Zi> put these additional libraries in the viewer build target here as it didn't
# work to put them in via their cmake/* files # work to put them in via their cmake/* files

View File

@ -1136,8 +1136,13 @@ void LLFloaterModelPreview::onPhysicsStageExecute(LLUICtrl* ctrl, void* data)
gMeshRepo.mDecompThread->submitRequest(request); gMeshRepo.mDecompThread->submitRequest(request);
} }
} }
if (stage == "Analyze")
if (stage == "Decompose") {
sInstance->setStatusMessage(sInstance->getString("decomposing"));
sInstance->childSetVisible("Analyze", false);
sInstance->childSetVisible("analyze_cancel", true);
}
else if (stage == "Decompose")
{ {
sInstance->setStatusMessage(sInstance->getString("decomposing")); sInstance->setStatusMessage(sInstance->getString("decomposing"));
sInstance->childSetVisible("Decompose", false); sInstance->childSetVisible("Decompose", false);
@ -1320,6 +1325,7 @@ void LLFloaterModelPreview::initDecompControls()
childSetCommitCallback("simplify_cancel", onPhysicsStageCancel, NULL); childSetCommitCallback("simplify_cancel", onPhysicsStageCancel, NULL);
childSetCommitCallback("decompose_cancel", onPhysicsStageCancel, NULL); childSetCommitCallback("decompose_cancel", onPhysicsStageCancel, NULL);
childSetCommitCallback("analyze_cancel", onPhysicsStageCancel, NULL);
childSetCommitCallback("physics_lod_combo", onPhysicsUseLOD, NULL); childSetCommitCallback("physics_lod_combo", onPhysicsUseLOD, NULL);
childSetCommitCallback("physics_browse", onPhysicsBrowse, NULL); childSetCommitCallback("physics_browse", onPhysicsBrowse, NULL);
@ -2214,7 +2220,7 @@ void LLFloaterModelPreview::DecompRequest::completed()
{ //called from the main thread { //called from the main thread
if (mContinue) if (mContinue)
{ {
mModel->setConvexHullDecomposition(mHull); mModel->setConvexHullDecomposition(mHull, mHullMesh);
if (sInstance) if (sInstance)
{ {

View File

@ -3627,7 +3627,16 @@ void LLModelPreview::updateStatusMessages()
//enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean(); //enable = enable && !use_hull && fmp->childGetValue("physics_optimize").asBoolean();
//enable/disable "analysis" UI //enable/disable "analysis" UI
LLPanel* panel = fmp->getChild<LLPanel>("physics analysis"); #if LL_HAVOK
LLPanel* panel = fmp->getChild<LLPanel>("physics simplification");
panel->setVisible(true);
panel = fmp->getChild<LLPanel>("physics analysis havok");
panel->setVisible(true);
#else
LLPanel* panel = fmp->getChild<LLPanel>("physics analysis vhacd");
panel->setVisible(true);
#endif
LLView* child = panel->getFirstChild(); LLView* child = panel->getFirstChild();
while (child) while (child)
{ {
@ -3651,6 +3660,8 @@ void LLModelPreview::updateStatusMessages()
fmp->childSetVisible("simplify_cancel", false); fmp->childSetVisible("simplify_cancel", false);
fmp->childSetVisible("Decompose", true); fmp->childSetVisible("Decompose", true);
fmp->childSetVisible("decompose_cancel", false); fmp->childSetVisible("decompose_cancel", false);
fmp->childSetVisible("Analyze", true);
fmp->childSetVisible("analyze_cancel", false);
if (phys_hulls > 0) if (phys_hulls > 0)
{ {
@ -3660,6 +3671,7 @@ void LLModelPreview::updateStatusMessages()
if (phys_tris || phys_hulls > 0) if (phys_tris || phys_hulls > 0)
{ {
fmp->childEnable("Decompose"); fmp->childEnable("Decompose");
fmp->childEnable("Analyze");
} }
} }
else else

View File

@ -45,7 +45,7 @@
<string name="simplifying">Simplifying...</string> <string name="simplifying">Simplifying...</string>
<string name="tbd">TBD</string> <string name="tbd">TBD</string>
<string name="ModelTextureScaling">One or more textures in this model were scaled to be within the allowed limits.</string> <string name="ModelTextureScaling">One or more textures in this model were scaled to be within the allowed limits.</string>
<!-- Warnings and info from model loader--> <!-- Warnings and info from model loader-->
<string name="TooManyJoint">Skinning disabled due to too many joints: [JOINTS], maximum: [MAX]</string> <string name="TooManyJoint">Skinning disabled due to too many joints: [JOINTS], maximum: [MAX]</string>
<string name="UnrecognizedJoint">Rigged to unrecognized joint name [NAME]</string> <string name="UnrecognizedJoint">Rigged to unrecognized joint name [NAME]</string>
@ -831,7 +831,7 @@
help_topic="upload_model_physics" help_topic="upload_model_physics"
label="Physics" label="Physics"
name="physics_panel"> name="physics_panel">
<!-- ==== STEP 1: Level of Detail ==== --> <!-- ==== STEP 1: Level of Detail ==== -->
<view_border <view_border
bevel_style="none" bevel_style="none"
@ -900,7 +900,7 @@
<!-- <check_box name="physics_optimize" follows="left|top" width="130" left="10" top_pad="5" height="20" label="Optimize"/> <!-- <check_box name="physics_optimize" follows="left|top" width="130" left="10" top_pad="5" height="20" label="Optimize"/>
<check_box name="physics_use_hull" follows="left|top" width="130" left_pad="5" height="20" label="Use Convex Hull"/> --> <check_box name="physics_use_hull" follows="left|top" width="130" left_pad="5" height="20" label="Use Convex Hull"/> -->
</panel> </panel>
<!-- ==== STEP 2: Analyze ==== --> <!-- ==== STEP 2: Analyze ==== -->
<view_border <view_border
bevel_style="none" bevel_style="none"
@ -917,9 +917,9 @@
height="65" height="65"
follows="top|left" follows="top|left"
left="18" left="18"
name="physics analysis" name="physics analysis havok"
top_pad="10" top_pad="10"
visible="true" visible="false"
width="589"> width="589">
<text <text
follows="left|top" follows="left|top"
@ -1007,7 +1007,131 @@
visible="false" visible="false"
width="90"/> width="90"/>
</panel> </panel>
<panel
bg_alpha_color="0 0 0 0"
bg_opaque_color="0 0 0 0.3"
height="65"
follows="top|left"
left="18"
name="physics analysis vhacd"
top_delta="0"
visible="false"
width="589">
<text
follows="left|top"
font="SansSerif"
height="20"
layout="topleft"
left="0"
name="method_label"
text_color="White"
top_pad="0">
Step 2: Convert to hulls (optional)
</text>
<text
follows="top|left"
height="15"
layout="topleft"
name="fill_mode_label"
top_pad="10"
width="100">
Fill Mode:
</text>
<text
follows="top|left"
height="15"
name="resolution_label"
layout="topleft"
left_pad="5"
width="85">
Resolution:
</text>
<text
follows="top|left"
height="15"
name="hulls_per_mesh_label"
layout="topleft"
left_pad="25"
width="95">
Hulls per Mesh:
</text>
<text
follows="top|left"
height="15"
name="vertices_per_hull_label"
layout="topleft"
left_pad="5"
width="95">
Vertices per hull:
</text>
<text
follows="top|left"
height="15"
name="tolerance_label"
layout="topleft"
left_pad="5"
width="100">
Error tolerance:
</text>
<combo_box
follows="top|left"
layout="topleft"
left="0"
name="Fill Mode"
top_pad="0"
height="20"
width="100"/>
<combo_box
follows="top|left"
layout="topleft"
left_pad="5"
name="Voxel Resolution"
height="20"
width="100"/>
<spinner
follows="top|left"
name="Num Hulls"
height="20"
left_pad="10"
width="60"
decimal_digits="0"
allow_digits_only="true"/>
<spinner
follows="top|left"
name="Num Vertices"
height="20"
left_pad="40"
width="60"
decimal_digits="0"
allow_digits_only="true"/>
<spinner
follows="top|left"
name="Error Tolerance"
height="20"
left_pad="40"
width="60"
decimal_digits="4"
allow_digits_only="true"/>
<button
bottom="1"
follows="top|right"
height="20"
label="Analyze"
layout="bottomleft"
name="Analyze"
right="-1"
width="90"/>
<button
follows="top|left"
height="20"
label="Cancel"
layout="topleft"
left_delta="0"
name="analyze_cancel"
visible="false"
width="90"/>
</panel>
<!-- ==== STEP 3: Simplify ==== --> <!-- ==== STEP 3: Simplify ==== -->
<view_border <view_border
bevel_style="none" bevel_style="none"
@ -1026,7 +1150,8 @@
left="18" left="18"
name="physics simplification" name="physics simplification"
top_pad="10" top_pad="10"
width="589"> width="589"
visible="false">
<text <text
text_color="White" text_color="White"
follows="left|top" follows="left|top"
@ -1115,7 +1240,7 @@
name="simplify_cancel" name="simplify_cancel"
width="90"/> width="90"/>
</panel> </panel>
<!-- ==== Results ==== --> <!-- ==== Results ==== -->
<view_border <view_border
bevel_style="none" bevel_style="none"
@ -1988,7 +2113,7 @@ Model:
[MODEL] [MODEL]
</text> </text>
</panel> </panel>
<!-- <!--
Streaming breakdown numbers are available but not fully understood Streaming breakdown numbers are available but not fully understood
uncommenting the following sections will display the numbers for debugging purposes uncommenting the following sections will display the numbers for debugging purposes
<text <text