Initial VHACD based llconvexdecomposition

master
Rye 2025-09-18 13:38:54 -04:00
parent 9a413357ad
commit b277cc6a8f
18 changed files with 1418 additions and 55 deletions

View File

@ -1496,41 +1496,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>http://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>http://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>
@ -1540,7 +1518,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>
@ -2966,6 +2944,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

@ -61,6 +61,9 @@ add_subdirectory(cmake)
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

@ -62,6 +62,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

@ -23,9 +23,15 @@ if (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)
target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions) target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions)
target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 )
elseif (HAVOK_TPV) elseif (HAVOK_TPV)
use_prebuilt_binary(llphysicsextensions_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) else (HAVOK)
use_prebuilt_binary(llphysicsextensions_stub) use_prebuilt_binary(llphysicsextensions_stub)
set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/stub) set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/stub)

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

@ -70,6 +70,12 @@ target_link_libraries(llprimitive
ll::glm ll::glm
) )
if (TARGET llconvexdecomposition)
target_link_libraries(llprimitive
llconvexdecomposition
)
endif ()
#add unit tests #add unit tests
if (LL_TESTS) if (LL_TESTS)
INCLUDE(LLAddBuildTest) INCLUDE(LLAddBuildTest)

View File

@ -1296,10 +1296,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

@ -305,7 +305,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

@ -1709,10 +1709,6 @@ if (WINDOWS)
list(APPEND viewer_SOURCE_FILES ${viewer_INSTALLER_FILES}) list(APPEND viewer_SOURCE_FILES ${viewer_INSTALLER_FILES})
endif (WINDOWS) 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 ) if( DEFINED LLSTARTUP_COMPILE_FLAGS )
# progress view disables/enables icons based on available packages # progress view disables/enables icons based on available packages
set_source_files_properties(llprogressview.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}") 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 ) target_link_libraries(${VIEWER_BINARY_NAME} ll::nvapi )
endif() endif()
if ( TARGET llconvexdecomposition )
target_link_libraries(${VIEWER_BINARY_NAME} llconvexdecomposition )
endif ()
set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH
"Path to artwork files.") "Path to artwork files.")

View File

@ -1035,8 +1035,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);
@ -1137,6 +1142,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);
@ -2018,7 +2024,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

@ -2626,7 +2626,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)
{ {
@ -2650,6 +2659,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)
{ {
@ -2659,6 +2670,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

@ -1070,7 +1070,7 @@ void LLViewerObjectList::fetchObjectCostsCoro(std::string url)
if (diff.empty()) 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; return;
} }
@ -1205,7 +1205,7 @@ void LLViewerObjectList::fetchPhisicsFlagsCoro(std::string url)
if (idList.size() < 1) 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; return;
} }

View File

@ -890,9 +890,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"
@ -980,6 +980,130 @@
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="analysis_method_label"
top_pad="10"
width="100">
Fill Mode:
</text>
<text
follows="top|left"
height="15"
name="quality_label"
layout="topleft"
left_pad="5"
width="85">
Resolution:
</text>
<text
follows="top|left"
height="15"
name="quality_label"
layout="topleft"
left_pad="25"
width="95">
Hulls per Mesh:
</text>
<text
follows="top|left"
height="15"
name="smooth_method_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
@ -999,7 +1123,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"