commit
bf347d1580
|
|
@ -1,6 +1,7 @@
|
|||
# Replace tabs with spaces
|
||||
1b68f71348ecf3983b76b40d7940da8377f049b7
|
||||
33418a77b716e122da9778869cdbabe97c83ff37
|
||||
6b974724826a038b0db794460b322eb4921da735
|
||||
# Trim trailing whitespace
|
||||
a0b3021bdcf76859054fda8e30abb3ed47749e83
|
||||
8444cd9562a6a7b755fcb075864e205122354192
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
version: 2
|
||||
updates:
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
name: Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches: ["main", "release/*", "project/*"]
|
||||
|
|
@ -85,7 +86,7 @@ jobs:
|
|||
variants: ${{ matrix.configuration }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
|
|
@ -94,14 +95,14 @@ jobs:
|
|||
with:
|
||||
python-version: "3.11"
|
||||
- name: Checkout build variables
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: secondlife/build-variables
|
||||
ref: master
|
||||
path: .build-variables
|
||||
|
||||
- name: Checkout master-message-template
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: secondlife/master-message-template
|
||||
path: .master-message-template
|
||||
|
|
@ -309,7 +310,7 @@ jobs:
|
|||
steps:
|
||||
- name: Sign and package Windows viewer
|
||||
if: env.AZURE_KEY_VAULT_URI && env.AZURE_CERT_NAME && env.AZURE_CLIENT_ID && env.AZURE_CLIENT_SECRET && env.AZURE_TENANT_ID
|
||||
uses: secondlife/viewer-build-util/sign-pkg-windows@v2
|
||||
uses: secondlife/viewer-build-util/sign-pkg-windows@v2.0.4
|
||||
with:
|
||||
vault_uri: "${{ env.AZURE_KEY_VAULT_URI }}"
|
||||
cert_name: "${{ env.AZURE_CERT_NAME }}"
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ jobs:
|
|||
pre-commit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.x
|
||||
- uses: pre-commit/action@v3.0.0
|
||||
- uses: pre-commit/action@v3.0.1
|
||||
|
|
|
|||
|
|
@ -7,14 +7,14 @@ repos:
|
|||
- id: no-trigraphs
|
||||
- id: copyright
|
||||
- id: end-of-file
|
||||
files: \.(cpp|c|h|py|glsl|cmake|txt)$
|
||||
files: \.(cpp|c|m|mm|h|py|glsl|cmake|txt)$
|
||||
exclude: language.txt
|
||||
- id: indent-with-spaces
|
||||
files: \.(cpp|c|h|inl|py|glsl|cmake)$
|
||||
files: \.(cpp|c|m|mm|h|inl|py|glsl|cmake)$
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: check-xml
|
||||
- id: mixed-line-ending
|
||||
- id: trailing-whitespace
|
||||
files: \.(cpp|c|h|inl|py|glsl|cmake|yaml|sh)$
|
||||
files: \.(cpp|c|m|mm|h|inl|py|glsl|cmake|yaml|sh)$
|
||||
|
|
|
|||
|
|
@ -1460,58 +1460,6 @@
|
|||
<key>name</key>
|
||||
<string>llphysicsextensions_source</string>
|
||||
</map>
|
||||
<key>llphysicsextensions_stub</key>
|
||||
<map>
|
||||
<key>platforms</key>
|
||||
<map>
|
||||
<key>darwin64</key>
|
||||
<map>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>f290b000b31f9e36f2489946cbc99f5e</string>
|
||||
<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>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>darwin64</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>
|
||||
<key>license</key>
|
||||
<string>internal</string>
|
||||
<key>license_file</key>
|
||||
<string>LICENSES/llphysicsextensions.txt</string>
|
||||
<key>copyright</key>
|
||||
<string>Copyright (c) 2010, Linden Research, Inc.</string>
|
||||
<key>version</key>
|
||||
<string>1.0.542456</string>
|
||||
<key>name</key>
|
||||
<string>llphysicsextensions_stub</string>
|
||||
</map>
|
||||
<key>llphysicsextensions_tpv</key>
|
||||
<map>
|
||||
<key>platforms</key>
|
||||
|
|
@ -2934,6 +2882,38 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
|||
<key>description</key>
|
||||
<string>Discord Social SDK</string>
|
||||
</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>
|
||||
<key>package_description</key>
|
||||
<map>
|
||||
|
|
|
|||
2
build.sh
2
build.sh
|
|
@ -70,7 +70,7 @@ installer_Darwin()
|
|||
{
|
||||
local package_name="$1"
|
||||
local package_dir="$(build_dir_Darwin)/newview/"
|
||||
local pattern=".*$(viewer_channel_suffix ${package_name})_[0-9]+_[0-9]+_[0-9]+_[0-9]+_x86_64\\.dmg\$"
|
||||
local pattern=".*$(viewer_channel_suffix ${package_name})_[0-9]+_[0-9]+_[0-9]+_[0-9]+_universal\\.dmg\$"
|
||||
# since the additional packages are built after the base package,
|
||||
# sorting oldest first ensures that the unqualified package is returned
|
||||
# even if someone makes a qualified name that duplicates the last word of the base name
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llinventory)
|
|||
add_subdirectory(${LIBS_OPEN_PREFIX}llmath)
|
||||
add_subdirectory(${LIBS_OPEN_PREFIX}llmeshoptimizer)
|
||||
add_subdirectory(${LIBS_OPEN_PREFIX}llmessage)
|
||||
add_subdirectory(${LIBS_OPEN_PREFIX}llphysicsextensionsos)
|
||||
add_subdirectory(${LIBS_OPEN_PREFIX}llprimitive)
|
||||
add_subdirectory(${LIBS_OPEN_PREFIX}llrender)
|
||||
add_subdirectory(${LIBS_OPEN_PREFIX}llfilesystem)
|
||||
|
|
|
|||
|
|
@ -31,3 +31,8 @@ if(DARWIN)
|
|||
endif()
|
||||
|
||||
target_include_directories(ll::apr SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/apr-1)
|
||||
|
||||
# Fix erroneous check for __attribute__ definition introduced with APR 1.7.5, causing lots of "this declaration may not have extern 'C' linkage" errors in VS
|
||||
file(READ ${LIBS_PREBUILT_DIR}/include/apr-1/apr.h APR_HEADER_CONTENTS)
|
||||
string(REPLACE "#if !(defined(__attribute__) || defined(__has_attribute))" "#if !defined(__attribute__)" APR_HEADER_CONTENTS "${APR_HEADER_CONTENTS}")
|
||||
file(WRITE ${LIBS_PREBUILT_DIR}/include/apr-1/apr.h "${APR_HEADER_CONTENTS}")
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ set(cmake_SOURCE_FILES
|
|||
UI.cmake
|
||||
UnixInstall.cmake
|
||||
Variables.cmake
|
||||
VHACD.cmake
|
||||
ViewerMiscLibs.cmake
|
||||
VisualLeakDetector.cmake
|
||||
LibVLCPlugin.cmake
|
||||
|
|
|
|||
|
|
@ -29,14 +29,18 @@ if (HAVOK)
|
|||
#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()
|
||||
target_include_directories( llphysicsextensions_impl INTERFACE ${LIBS_PREBUILT_DIR}/include/llphysicsextensions)
|
||||
elseif (HAVOK_TPV)
|
||||
use_prebuilt_binary(llphysicsextensions_tpv)
|
||||
target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensions_tpv)
|
||||
else (HAVOK)
|
||||
use_prebuilt_binary(llphysicsextensions_stub)
|
||||
set(LLPHYSICSEXTENSIONS_SRC_DIR ${LIBS_PREBUILT_DIR}/llphysicsextensions/stub)
|
||||
target_link_libraries( llphysicsextensions_impl INTERFACE llphysicsextensionsstub)
|
||||
endif (HAVOK)
|
||||
|
||||
target_include_directories( llphysicsextensions_impl INTERFACE ${LIBS_PREBUILT_DIR}/include/llphysicsextensions)
|
||||
if (NOT DARWIN)
|
||||
if(WINDOWS)
|
||||
target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/llphysicsextensions_tpv.lib)
|
||||
elseif(LINUX)
|
||||
target_link_libraries( llphysicsextensions_impl INTERFACE ${ARCH_PREBUILT_DIRS}/libllphysicsextensions_tpv.a)
|
||||
endif()
|
||||
target_compile_definitions( llphysicsextensions_impl INTERFACE LL_HAVOK=1 )
|
||||
endif()
|
||||
target_include_directories( llphysicsextensions_impl INTERFACE ${LIBS_PREBUILT_DIR}/include/llphysicsextensions)
|
||||
endif ()
|
||||
|
|
|
|||
|
|
@ -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/)
|
||||
|
|
@ -283,6 +283,7 @@ bool LLPolyMeshSharedData::loadMesh( const std::string& fileName )
|
|||
LLFILE* fp = LLFile::fopen(fileName, "rb"); /*Flawfinder: ignore*/
|
||||
if (!fp)
|
||||
{
|
||||
LLError::LLUserWarningMsg::showMissingFiles();
|
||||
LL_ERRS() << "can't open: " << fileName << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1890,7 +1890,10 @@ LLGLTexture* LLTexLayerStaticImageList::getTexture(const std::string& file_name,
|
|||
|
||||
image_raw->copyUnscaledAlphaMask(alpha_image_raw, LLColor4U::black);
|
||||
}
|
||||
tex->createGLTexture(0, image_raw, 0, true, LLGLTexture::LOCAL);
|
||||
if (!tex->createGLTexture(0, image_raw, 0, true, LLGLTexture::LOCAL))
|
||||
{
|
||||
LL_WARNS() << "Failed to create GL texture for image: " << file_name << LL_ENDL;
|
||||
}
|
||||
|
||||
gGL.getTexUnit(0)->bind(tex);
|
||||
tex->setAddressMode(LLTexUnit::TAM_CLAMP);
|
||||
|
|
|
|||
|
|
@ -338,7 +338,10 @@ bool LLTexLayerParamAlpha::render(S32 x, S32 y, S32 width, S32 height)
|
|||
// Create the GL texture, and then hang onto it for future use.
|
||||
if (mNeedsCreateTexture)
|
||||
{
|
||||
mCachedProcessedTexture->createGLTexture(0, mStaticImageRaw);
|
||||
if (!mCachedProcessedTexture->createGLTexture(0, mStaticImageRaw))
|
||||
{
|
||||
LL_WARNS() << "Failed to create GL texture for image: " << mCachedProcessedTexture->getID() << LL_ENDL;
|
||||
}
|
||||
mNeedsCreateTexture = false;
|
||||
gGL.getTexUnit(0)->bind(mCachedProcessedTexture);
|
||||
mCachedProcessedTexture->setAddressMode(LLTexUnit::TAM_CLAMP);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@
|
|||
|
||||
#if LL_WINDOWS
|
||||
#include "llwin32headers.h"
|
||||
#include <stdlib.h> // Windows errno
|
||||
#include <vector>
|
||||
#else
|
||||
#include <errno.h>
|
||||
|
|
@ -49,6 +48,86 @@ static std::string empty;
|
|||
// variants of strerror() to report errors.
|
||||
|
||||
#if LL_WINDOWS
|
||||
// For the situations where we directly call into Windows API functions we need to translate
|
||||
// the Windows error codes into errno values
|
||||
namespace
|
||||
{
|
||||
struct errentry
|
||||
{
|
||||
unsigned long oserr; // Windows OS error value
|
||||
int errcode; // System V error code
|
||||
};
|
||||
}
|
||||
|
||||
// translation table between Windows OS error value and System V errno code
|
||||
static errentry const errtable[]
|
||||
{
|
||||
{ ERROR_INVALID_FUNCTION, EINVAL }, // 1
|
||||
{ ERROR_FILE_NOT_FOUND, ENOENT }, // 2
|
||||
{ ERROR_PATH_NOT_FOUND, ENOENT }, // 3
|
||||
{ ERROR_TOO_MANY_OPEN_FILES, EMFILE }, // 4
|
||||
{ ERROR_ACCESS_DENIED, EACCES }, // 5
|
||||
{ ERROR_INVALID_HANDLE, EBADF }, // 6
|
||||
{ ERROR_ARENA_TRASHED, ENOMEM }, // 7
|
||||
{ ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, // 8
|
||||
{ ERROR_INVALID_BLOCK, ENOMEM }, // 9
|
||||
{ ERROR_BAD_ENVIRONMENT, E2BIG }, // 10
|
||||
{ ERROR_BAD_FORMAT, ENOEXEC }, // 11
|
||||
{ ERROR_INVALID_ACCESS, EINVAL }, // 12
|
||||
{ ERROR_INVALID_DATA, EINVAL }, // 13
|
||||
{ ERROR_INVALID_DRIVE, ENOENT }, // 15
|
||||
{ ERROR_CURRENT_DIRECTORY, EACCES }, // 16
|
||||
{ ERROR_NOT_SAME_DEVICE, EXDEV }, // 17
|
||||
{ ERROR_NO_MORE_FILES, ENOENT }, // 18
|
||||
{ ERROR_LOCK_VIOLATION, EACCES }, // 33
|
||||
{ ERROR_BAD_NETPATH, ENOENT }, // 53
|
||||
{ ERROR_NETWORK_ACCESS_DENIED, EACCES }, // 65
|
||||
{ ERROR_BAD_NET_NAME, ENOENT }, // 67
|
||||
{ ERROR_FILE_EXISTS, EEXIST }, // 80
|
||||
{ ERROR_CANNOT_MAKE, EACCES }, // 82
|
||||
{ ERROR_FAIL_I24, EACCES }, // 83
|
||||
{ ERROR_INVALID_PARAMETER, EINVAL }, // 87
|
||||
{ ERROR_NO_PROC_SLOTS, EAGAIN }, // 89
|
||||
{ ERROR_DRIVE_LOCKED, EACCES }, // 108
|
||||
{ ERROR_BROKEN_PIPE, EPIPE }, // 109
|
||||
{ ERROR_DISK_FULL, ENOSPC }, // 112
|
||||
{ ERROR_INVALID_TARGET_HANDLE, EBADF }, // 114
|
||||
{ ERROR_WAIT_NO_CHILDREN, ECHILD }, // 128
|
||||
{ ERROR_CHILD_NOT_COMPLETE, ECHILD }, // 129
|
||||
{ ERROR_DIRECT_ACCESS_HANDLE, EBADF }, // 130
|
||||
{ ERROR_NEGATIVE_SEEK, EINVAL }, // 131
|
||||
{ ERROR_SEEK_ON_DEVICE, EACCES }, // 132
|
||||
{ ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, // 145
|
||||
{ ERROR_NOT_LOCKED, EACCES }, // 158
|
||||
{ ERROR_BAD_PATHNAME, ENOENT }, // 161
|
||||
{ ERROR_MAX_THRDS_REACHED, EAGAIN }, // 164
|
||||
{ ERROR_LOCK_FAILED, EACCES }, // 167
|
||||
{ ERROR_ALREADY_EXISTS, EEXIST }, // 183
|
||||
{ ERROR_FILENAME_EXCED_RANGE, ENOENT }, // 206
|
||||
{ ERROR_NESTING_NOT_ALLOWED, EAGAIN }, // 215
|
||||
{ ERROR_NO_UNICODE_TRANSLATION, EILSEQ }, // 1113
|
||||
{ ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816
|
||||
};
|
||||
|
||||
static int set_errno_from_oserror(unsigned long oserr)
|
||||
{
|
||||
if (!oserr)
|
||||
return 0;
|
||||
|
||||
// Check the table for the Windows OS error code
|
||||
for (const struct errentry &entry : errtable)
|
||||
{
|
||||
if (oserr == entry.oserr)
|
||||
{
|
||||
_set_errno(entry.errcode);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
_set_errno(EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// On Windows, use strerror_s().
|
||||
std::string strerr(int errn)
|
||||
{
|
||||
|
|
@ -57,7 +136,67 @@ std::string strerr(int errn)
|
|||
return buffer;
|
||||
}
|
||||
|
||||
typedef std::basic_ios<char,std::char_traits < char > > _Myios;
|
||||
inline bool is_slash(wchar_t const c)
|
||||
{
|
||||
return c == L'\\' || c == L'/';
|
||||
}
|
||||
|
||||
static std::wstring utf8path_to_wstring(const std::string& utf8path)
|
||||
{
|
||||
if (utf8path.size() >= MAX_PATH)
|
||||
{
|
||||
// By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names
|
||||
std::wstring utf16path = L"\\\\?\\" + ll_convert<std::wstring>(utf8path);
|
||||
// We need to make sure that the path does not contain forward slashes as above
|
||||
// prefix does bypass the path normalization that replaces slashes with backslashes
|
||||
// before passing the path to kernel mode APIs
|
||||
std::replace(utf16path.begin(), utf16path.end(), L'/', L'\\');
|
||||
return utf16path;
|
||||
}
|
||||
return ll_convert<std::wstring>(utf8path);
|
||||
}
|
||||
|
||||
static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollowSymLink = false)
|
||||
{
|
||||
unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||||
if (dontFollowSymLink)
|
||||
{
|
||||
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
|
||||
}
|
||||
HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES,
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
nullptr, OPEN_EXISTING, flags, nullptr);
|
||||
if (file_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FILE_ATTRIBUTE_TAG_INFO attribute_info;
|
||||
if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info)))
|
||||
{
|
||||
// A volume path alone (only drive letter) is not recognized as directory while it technically is
|
||||
bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
|
||||
(iswalpha(utf16path[0]) && utf16path[1] == ':' &&
|
||||
(!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3])));
|
||||
unsigned short st_mode = is_directory ? S_IFDIR :
|
||||
(attribute_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? S_IFLNK : S_IFREG);
|
||||
st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE;
|
||||
// we do not try to guess executable flag
|
||||
|
||||
// propagate user bits to group/other fields:
|
||||
st_mode |= (st_mode & 0700) >> 3;
|
||||
st_mode |= (st_mode & 0700) >> 6;
|
||||
|
||||
CloseHandle(file_handle);
|
||||
return st_mode;
|
||||
}
|
||||
}
|
||||
// Retrieve last error and set errno before calling CloseHandle()
|
||||
set_errno_from_oserror(GetLastError());
|
||||
|
||||
if (file_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(file_handle);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
// On Posix we want to call strerror_r(), but alarmingly, there are two
|
||||
|
|
@ -108,18 +247,13 @@ std::string strerr(int errn)
|
|||
}
|
||||
#endif // ! LL_WINDOWS
|
||||
|
||||
// On either system, shorthand call just infers global 'errno'.
|
||||
std::string strerr()
|
||||
{
|
||||
return strerr(errno);
|
||||
}
|
||||
|
||||
int warnif(const std::string& desc, const std::string& filename, int rc, int accept=0)
|
||||
static int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0)
|
||||
{
|
||||
if (rc < 0)
|
||||
{
|
||||
// Capture errno before we start emitting output
|
||||
int errn = errno;
|
||||
|
||||
// For certain operations, a particular errno value might be
|
||||
// acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit
|
||||
// EEXIST. Don't warn if caller explicitly says this errno is okay.
|
||||
|
|
@ -176,62 +310,59 @@ int warnif(const std::string& desc, const std::string& filename, int rc, int acc
|
|||
// static
|
||||
int LLFile::mkdir(const std::string& dirname, int perms)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
// permissions are ignored on Windows
|
||||
std::wstring utf16dirname = ll_convert<std::wstring>(dirname);
|
||||
int rc = _wmkdir(utf16dirname.c_str());
|
||||
#else
|
||||
int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
|
||||
#endif
|
||||
// We often use mkdir() to ensure the existence of a directory that might
|
||||
// already exist. There is no known case in which we want to call out as
|
||||
// an error the requested directory already existing.
|
||||
#if LL_WINDOWS
|
||||
// permissions are ignored on Windows
|
||||
int rc = 0;
|
||||
std::wstring utf16dirname = utf8path_to_wstring(dirname);
|
||||
if (!CreateDirectoryW(utf16dirname.c_str(), nullptr))
|
||||
{
|
||||
// Only treat other errors than an already existing file as a real error
|
||||
unsigned long oserr = GetLastError();
|
||||
if (oserr != ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
rc = set_errno_from_oserror(oserr);
|
||||
}
|
||||
}
|
||||
#else
|
||||
int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
|
||||
if (rc < 0 && errno == EEXIST)
|
||||
{
|
||||
// this is not the error you want, move along
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
// anything else might be a problem
|
||||
return warnif("mkdir", dirname, rc, EEXIST);
|
||||
return warnif("mkdir", dirname, rc);
|
||||
}
|
||||
|
||||
// static
|
||||
int LLFile::rmdir(const std::string& dirname)
|
||||
int LLFile::rmdir(const std::string& dirname, int suppress_error)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
// permissions are ignored on Windows
|
||||
std::wstring utf16dirname = ll_convert<std::wstring>(dirname);
|
||||
std::wstring utf16dirname = utf8path_to_wstring(dirname);
|
||||
int rc = _wrmdir(utf16dirname.c_str());
|
||||
#else
|
||||
int rc = ::rmdir(dirname.c_str());
|
||||
#endif
|
||||
return warnif("rmdir", dirname, rc);
|
||||
return warnif("rmdir", dirname, rc, suppress_error);
|
||||
}
|
||||
|
||||
// static
|
||||
LLFILE* LLFile::fopen(const std::string& filename, const char* mode) /* Flawfinder: ignore */
|
||||
LLFILE* LLFile::fopen(const std::string& filename, const char* mode)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
std::wstring utf16filename = ll_convert<std::wstring>(filename);
|
||||
std::wstring utf16filename = utf8path_to_wstring(filename);
|
||||
std::wstring utf16mode = ll_convert<std::wstring>(std::string(mode));
|
||||
return _wfopen(utf16filename.c_str(),utf16mode.c_str());
|
||||
return _wfopen(utf16filename.c_str(), utf16mode.c_str());
|
||||
#else
|
||||
return ::fopen(filename.c_str(),mode); /* Flawfinder: ignore */
|
||||
#endif
|
||||
}
|
||||
|
||||
LLFILE* LLFile::_fsopen(const std::string& filename, const char* mode, int sharingFlag)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
std::wstring utf16filename = ll_convert<std::wstring>(filename);
|
||||
std::wstring utf16mode = ll_convert<std::wstring>(std::string(mode));
|
||||
return _wfsopen(utf16filename.c_str(),utf16mode.c_str(),sharingFlag);
|
||||
#else
|
||||
llassert(0);//No corresponding function on non-windows
|
||||
return NULL;
|
||||
return ::fopen(filename.c_str(),mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
// static
|
||||
int LLFile::close(LLFILE * file)
|
||||
{
|
||||
int ret_value = 0;
|
||||
|
|
@ -242,9 +373,10 @@ int LLFile::close(LLFILE * file)
|
|||
return ret_value;
|
||||
}
|
||||
|
||||
// static
|
||||
std::string LLFile::getContents(const std::string& filename)
|
||||
{
|
||||
LLFILE* fp = fopen(filename, "rb"); /* Flawfinder: ignore */
|
||||
LLFILE* fp = LLFile::fopen(filename, "rb");
|
||||
if (fp)
|
||||
{
|
||||
fseek(fp, 0, SEEK_END);
|
||||
|
|
@ -261,42 +393,80 @@ std::string LLFile::getContents(const std::string& filename)
|
|||
return LLStringUtil::null;
|
||||
}
|
||||
|
||||
int LLFile::remove(const std::string& filename, int supress_error)
|
||||
// static
|
||||
int LLFile::remove(const std::string& filename, int suppress_error)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
std::wstring utf16filename = ll_convert<std::wstring>(filename);
|
||||
int rc = _wremove(utf16filename.c_str());
|
||||
// Posix remove() works on both files and directories although on Windows
|
||||
// remove() and its wide char variant _wremove() only removes files just
|
||||
// as its siblings unlink() and _wunlink().
|
||||
// If we really only want to support files we should instead use
|
||||
// unlink() in the non-Windows part below too
|
||||
int rc = -1;
|
||||
std::wstring utf16filename = utf8path_to_wstring(filename);
|
||||
unsigned short st_mode = get_fileattr(utf16filename);
|
||||
if (S_ISDIR(st_mode))
|
||||
{
|
||||
rc = _wrmdir(utf16filename.c_str());
|
||||
}
|
||||
else if (S_ISREG(st_mode))
|
||||
{
|
||||
rc = _wunlink(utf16filename.c_str());
|
||||
}
|
||||
else if (st_mode)
|
||||
{
|
||||
// it is something else than a file or directory
|
||||
// this should not really happen as long as we do not allow for symlink
|
||||
// detection in the optional parameter to get_fileattr()
|
||||
rc = set_errno_from_oserror(ERROR_INVALID_PARAMETER);
|
||||
}
|
||||
else
|
||||
{
|
||||
// get_fileattr() failed and already set errno, preserve it for correct error reporting
|
||||
}
|
||||
#else
|
||||
int rc = ::remove(filename.c_str());
|
||||
#endif
|
||||
return warnif("remove", filename, rc, supress_error);
|
||||
return warnif("remove", filename, rc, suppress_error);
|
||||
}
|
||||
|
||||
int LLFile::rename(const std::string& filename, const std::string& newname, int supress_error)
|
||||
// static
|
||||
int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
std::wstring utf16filename = ll_convert<std::wstring>(filename);
|
||||
std::wstring utf16newname = ll_convert<std::wstring>(newname);
|
||||
int rc = _wrename(utf16filename.c_str(),utf16newname.c_str());
|
||||
// Posix rename() will gladly overwrite a file at newname if it exists, the Windows
|
||||
// rename(), respectively _wrename(), will bark on that. Instead call directly the Windows
|
||||
// API MoveFileEx() and use its flags to specify that overwrite is allowed.
|
||||
std::wstring utf16filename = utf8path_to_wstring(filename);
|
||||
std::wstring utf16newname = utf8path_to_wstring(newname);
|
||||
int rc = 0;
|
||||
if (!MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
|
||||
{
|
||||
rc = set_errno_from_oserror(GetLastError());
|
||||
}
|
||||
#else
|
||||
int rc = ::rename(filename.c_str(),newname.c_str());
|
||||
#endif
|
||||
return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, supress_error);
|
||||
return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error);
|
||||
}
|
||||
|
||||
// Make this a define rather than using magic numbers multiple times in the code
|
||||
#define LLFILE_COPY_BUFFER_SIZE 16384
|
||||
|
||||
// static
|
||||
bool LLFile::copy(const std::string& from, const std::string& to)
|
||||
{
|
||||
bool copied = false;
|
||||
LLFILE* in = LLFile::fopen(from, "rb"); /* Flawfinder: ignore */
|
||||
LLFILE* in = LLFile::fopen(from, "rb");
|
||||
if (in)
|
||||
{
|
||||
LLFILE* out = LLFile::fopen(to, "wb"); /* Flawfinder: ignore */
|
||||
LLFILE* out = LLFile::fopen(to, "wb");
|
||||
if (out)
|
||||
{
|
||||
char buf[16384]; /* Flawfinder: ignore */
|
||||
char buf[LLFILE_COPY_BUFFER_SIZE];
|
||||
size_t readbytes;
|
||||
bool write_ok = true;
|
||||
while(write_ok && (readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */
|
||||
while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in)))
|
||||
{
|
||||
if (fwrite(buf, 1, readbytes, out) != readbytes)
|
||||
{
|
||||
|
|
@ -315,33 +485,62 @@ bool LLFile::copy(const std::string& from, const std::string& to)
|
|||
return copied;
|
||||
}
|
||||
|
||||
int LLFile::stat(const std::string& filename, llstat* filestatus)
|
||||
// static
|
||||
int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_error)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
std::wstring utf16filename = ll_convert<std::wstring>(filename);
|
||||
int rc = _wstat(utf16filename.c_str(),filestatus);
|
||||
std::wstring utf16filename = utf8path_to_wstring(filename);
|
||||
int rc = _wstat64(utf16filename.c_str(), filestatus);
|
||||
#else
|
||||
int rc = ::stat(filename.c_str(),filestatus);
|
||||
int rc = ::stat(filename.c_str(), filestatus);
|
||||
#endif
|
||||
// We use stat() to determine existence (see isfile(), isdir()).
|
||||
// Don't spam the log if the subject pathname doesn't exist.
|
||||
return warnif("stat", filename, rc, ENOENT);
|
||||
return warnif("stat", filename, rc, suppress_error);
|
||||
}
|
||||
|
||||
// static
|
||||
unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
// _wstat64() is a bit heavyweight on Windows, use a more lightweight API
|
||||
// to just get the attributes
|
||||
int rc = -1;
|
||||
std::wstring utf16filename = utf8path_to_wstring(filename);
|
||||
unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink);
|
||||
if (st_mode)
|
||||
{
|
||||
return st_mode;
|
||||
}
|
||||
#else
|
||||
llstat filestatus;
|
||||
int rc = dontFollowSymLink ? ::lstat(filename.c_str(), &filestatus) : ::stat(filename.c_str(), &filestatus);
|
||||
if (rc == 0)
|
||||
{
|
||||
return filestatus.st_mode;
|
||||
}
|
||||
#endif
|
||||
warnif("getattr", filename, rc, suppress_error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// static
|
||||
bool LLFile::isdir(const std::string& filename)
|
||||
{
|
||||
llstat st;
|
||||
|
||||
return stat(filename, &st) == 0 && S_ISDIR(st.st_mode);
|
||||
return S_ISDIR(getattr(filename));
|
||||
}
|
||||
|
||||
// static
|
||||
bool LLFile::isfile(const std::string& filename)
|
||||
{
|
||||
llstat st;
|
||||
|
||||
return stat(filename, &st) == 0 && S_ISREG(st.st_mode);
|
||||
return S_ISREG(getattr(filename));
|
||||
}
|
||||
|
||||
// static
|
||||
bool LLFile::islink(const std::string& filename)
|
||||
{
|
||||
return S_ISLNK(getattr(filename, true));
|
||||
}
|
||||
|
||||
// static
|
||||
const char *LLFile::tmpdir()
|
||||
{
|
||||
static std::string utf8path;
|
||||
|
|
@ -368,75 +567,8 @@ const char *LLFile::tmpdir()
|
|||
return utf8path.c_str();
|
||||
}
|
||||
|
||||
|
||||
/***************** Modified file stream created to overcome the incorrect behaviour of posix fopen in windows *******************/
|
||||
|
||||
#if LL_WINDOWS
|
||||
|
||||
LLFILE * LLFile::_Fiopen(const std::string& filename,
|
||||
std::ios::openmode mode)
|
||||
{ // open a file
|
||||
static const char *mods[] =
|
||||
{ // fopen mode strings corresponding to valid[i]
|
||||
"r", "w", "w", "a", "rb", "wb", "wb", "ab",
|
||||
"r+", "w+", "a+", "r+b", "w+b", "a+b",
|
||||
0};
|
||||
static const int valid[] =
|
||||
{ // valid combinations of open flags
|
||||
ios_base::in,
|
||||
ios_base::out,
|
||||
ios_base::out | ios_base::trunc,
|
||||
ios_base::out | ios_base::app,
|
||||
ios_base::in | ios_base::binary,
|
||||
ios_base::out | ios_base::binary,
|
||||
ios_base::out | ios_base::trunc | ios_base::binary,
|
||||
ios_base::out | ios_base::app | ios_base::binary,
|
||||
ios_base::in | ios_base::out,
|
||||
ios_base::in | ios_base::out | ios_base::trunc,
|
||||
ios_base::in | ios_base::out | ios_base::app,
|
||||
ios_base::in | ios_base::out | ios_base::binary,
|
||||
ios_base::in | ios_base::out | ios_base::trunc
|
||||
| ios_base::binary,
|
||||
ios_base::in | ios_base::out | ios_base::app
|
||||
| ios_base::binary,
|
||||
0};
|
||||
|
||||
LLFILE *fp = 0;
|
||||
int n;
|
||||
ios_base::openmode atendflag = mode & ios_base::ate;
|
||||
ios_base::openmode norepflag = mode & ios_base::_Noreplace;
|
||||
|
||||
if (mode & ios_base::_Nocreate)
|
||||
mode |= ios_base::in; // file must exist
|
||||
mode &= ~(ios_base::ate | ios_base::_Nocreate | ios_base::_Noreplace);
|
||||
for (n = 0; valid[n] != 0 && valid[n] != mode; ++n)
|
||||
; // look for a valid mode
|
||||
|
||||
if (valid[n] == 0)
|
||||
return (0); // no valid mode
|
||||
else if (norepflag && mode & (ios_base::out | ios_base::app)
|
||||
&& (fp = LLFile::fopen(filename, "r")) != 0) /* Flawfinder: ignore */
|
||||
{ // file must not exist, close and fail
|
||||
fclose(fp);
|
||||
return (0);
|
||||
}
|
||||
else if (fp != 0 && fclose(fp) != 0)
|
||||
return (0); // can't close after test open
|
||||
// should open with protection here, if other than default
|
||||
else if ((fp = LLFile::fopen(filename, mods[n])) == 0) /* Flawfinder: ignore */
|
||||
return (0); // open failed
|
||||
|
||||
if (!atendflag || fseek(fp, 0, SEEK_END) == 0)
|
||||
return (fp); // no need to seek to end, or seek succeeded
|
||||
|
||||
fclose(fp); // can't position at end
|
||||
return (0);
|
||||
}
|
||||
|
||||
#endif /* LL_WINDOWS */
|
||||
|
||||
|
||||
#if LL_WINDOWS
|
||||
/************** input file stream ********************************/
|
||||
|
||||
llifstream::llifstream() {}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,9 @@ typedef FILE LLFILE;
|
|||
#include <sys/stat.h>
|
||||
|
||||
#if LL_WINDOWS
|
||||
// windows version of stat function and stat data structure are called _stat
|
||||
typedef struct _stat llstat;
|
||||
// The Windows version of stat function and stat data structure are called _stat64
|
||||
// We use _stat64 here to support 64-bit st_size and time_t values
|
||||
typedef struct _stat64 llstat;
|
||||
#else
|
||||
typedef struct stat llstat;
|
||||
#include <sys/types.h>
|
||||
|
|
@ -56,35 +57,110 @@ typedef struct stat llstat;
|
|||
# define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
|
||||
#endif
|
||||
|
||||
// Windows C runtime library does not define this and does not support symlink detection in the
|
||||
// stat functions but we do in our getattr() function
|
||||
#ifndef S_IFLNK
|
||||
#define S_IFLNK 0xA000 /* symlink */
|
||||
#endif
|
||||
|
||||
#ifndef S_ISLNK
|
||||
#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
|
||||
#endif
|
||||
|
||||
#include "llstring.h" // safe char* -> std::string conversion
|
||||
|
||||
/// LLFile is a class of static functions operating on paths
|
||||
/// All the functions with a path string input take UTF8 path/filenames
|
||||
class LL_COMMON_API LLFile
|
||||
{
|
||||
public:
|
||||
// All these functions take UTF8 path/filenames.
|
||||
static LLFILE* fopen(const std::string& filename,const char* accessmode); /* Flawfinder: ignore */
|
||||
static LLFILE* _fsopen(const std::string& filename,const char* accessmode,int sharingFlag);
|
||||
/// open a file with the specified access mode
|
||||
static LLFILE* fopen(const std::string& filename, const char* accessmode); /* Flawfinder: ignore */
|
||||
///< 'accessmode' follows the rules of the Posix fopen() mode parameter
|
||||
/// "r" open the file for reading only and positions the stream at the beginning
|
||||
/// "r+" open the file for reading and writing and positions the stream at the beginning
|
||||
/// "w" open the file for reading and writing and truncate it to zero length
|
||||
/// "w+" open or create the file for reading and writing and truncate to zero length if it existed
|
||||
/// "a" open the file for reading and writing and position the stream at the end of the file
|
||||
/// "a+" open or create the file for reading and writing and position the stream at the end of the file
|
||||
///
|
||||
/// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac
|
||||
/// this is strictly for compatibility and has no effect. On Windows this makes the file functions not
|
||||
/// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither
|
||||
/// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT.
|
||||
/// This means that it is always a good idea to append "b" specifically for binary file access to
|
||||
/// avoid corruption of the binary consistency of the data stream when reading or writing
|
||||
/// Other characters in 'accessmode' will usually cause an error as fopen will verify this parameter
|
||||
/// @returns a valid LLFILE* pointer on success or NULL on failure
|
||||
|
||||
static int close(LLFILE * file);
|
||||
|
||||
/// retrieve the content of a file into a string
|
||||
static std::string getContents(const std::string& filename);
|
||||
///< @returns the content of the file or an empty string on failure
|
||||
|
||||
// perms is a permissions mask like 0777 or 0700. In most cases it will
|
||||
// be overridden by the user's umask. It is ignored on Windows.
|
||||
// mkdir() considers "directory already exists" to be SUCCESS.
|
||||
/// create a directory
|
||||
static int mkdir(const std::string& filename, int perms = 0700);
|
||||
///< perms is a permissions mask like 0777 or 0700. In most cases it will be
|
||||
/// overridden by the user's umask. It is ignored on Windows.
|
||||
/// mkdir() considers "directory already exists" to be not an error.
|
||||
/// @returns 0 on success and -1 on failure.
|
||||
|
||||
static int rmdir(const std::string& filename);
|
||||
static int remove(const std::string& filename, int supress_error = 0);
|
||||
static int rename(const std::string& filename,const std::string& newname, int supress_error = 0);
|
||||
//// remove a directory
|
||||
static int rmdir(const std::string& filename, int suppress_error = 0);
|
||||
///< pass ENOENT in the optional 'suppress_error' parameter
|
||||
/// if you don't want a warning in the log when the directory does not exist
|
||||
/// @returns 0 on success and -1 on failure.
|
||||
|
||||
/// remove a file or directory
|
||||
static int remove(const std::string& filename, int suppress_error = 0);
|
||||
///< pass ENOENT in the optional 'suppress_error' parameter
|
||||
/// if you don't want a warning in the log when the directory does not exist
|
||||
/// @returns 0 on success and -1 on failure.
|
||||
|
||||
/// rename a file
|
||||
static int rename(const std::string& filename, const std::string& newname, int suppress_error = 0);
|
||||
///< it will silently overwrite newname if it exists without returning an error
|
||||
/// Posix guarantees that if newname already exists, then there will be no moment
|
||||
/// in which for other processes newname does not exist. There is no such guarantee
|
||||
/// under Windows at this time. It may do it in the same way but the used Windows API
|
||||
/// does not make such guarantees.
|
||||
/// @returns 0 on success and -1 on failure.
|
||||
|
||||
|
||||
/// copy the contents of file from 'from' to 'to' filename
|
||||
static bool copy(const std::string& from, const std::string& to);
|
||||
///< @returns true on success and false on failure.
|
||||
|
||||
static int stat(const std::string& filename,llstat* file_status);
|
||||
static bool isdir(const std::string& filename);
|
||||
static bool isfile(const std::string& filename);
|
||||
static LLFILE * _Fiopen(const std::string& filename,
|
||||
std::ios::openmode mode);
|
||||
/// return the file stat structure for filename
|
||||
static int stat(const std::string& filename, llstat* file_status, int suppress_error = ENOENT);
|
||||
///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the
|
||||
/// optional 'suppress_error' parameter to avoid spamming the log with warnings when the API
|
||||
/// is used to detect if a file exists
|
||||
/// @returns 0 on success and -1 on failure.
|
||||
|
||||
/// get the file or directory attributes for filename
|
||||
static unsigned short getattr(const std::string& filename, bool dontFollowSymLink = false, int suppress_error = ENOENT);
|
||||
///< a more lightweight function on Windows to stat, that just returns the file attribute flags
|
||||
/// dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it
|
||||
/// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with
|
||||
/// warnings when the file or directory does not exist
|
||||
/// @returns 0 on failure and a st_mode value with either S_IFDIR or S_IFREG set otherwise
|
||||
/// together with the three access bits which under Windows only the write bit is relevant.
|
||||
|
||||
/// check if filename is an existing directory
|
||||
static bool isdir(const std::string& filename);
|
||||
///< @returns true if the path is for an existing directory
|
||||
|
||||
/// check if filename is an existing file
|
||||
static bool isfile(const std::string& filename);
|
||||
///< @returns true if the path is for an existing file
|
||||
|
||||
/// check if filename is a symlink
|
||||
static bool islink(const std::string& filename);
|
||||
///< @returns true if the path is pointing at a symlink
|
||||
|
||||
/// return a path to the temporary directory on the system
|
||||
static const char * tmpdir();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -188,6 +188,17 @@ public:
|
|||
<< childout.peek(0, peeklen) << "..." << LL_ENDL;
|
||||
}
|
||||
|
||||
// Handle any remaining stderr data (partial lines) the same way as we do
|
||||
// for stdout: log it.
|
||||
LLProcess::ReadPipe& childerr(mChild->getReadPipe(LLProcess::STDERR));
|
||||
if (childerr.size())
|
||||
{
|
||||
LLProcess::ReadPipe::size_type
|
||||
peeklen((std::min)(LLProcess::ReadPipe::size_type(50), childerr.size()));
|
||||
LL_WARNS("LLLeap") << "Final stderr " << childerr.size() << " bytes: "
|
||||
<< childerr.peek(0, peeklen) << "..." << LL_ENDL;
|
||||
}
|
||||
|
||||
// Kill this instance. MUST BE LAST before return!
|
||||
delete this;
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ void LLQueuedThread::shutdown()
|
|||
mRequestQueue.close();
|
||||
}
|
||||
|
||||
S32 timeout = 100;
|
||||
S32 timeout = 50;
|
||||
for ( ; timeout>0; timeout--)
|
||||
{
|
||||
if (isStopped())
|
||||
|
|
@ -101,19 +101,34 @@ void LLQueuedThread::shutdown()
|
|||
}
|
||||
|
||||
QueuedRequest* req;
|
||||
S32 active_count = 0;
|
||||
S32 queued_count = 0;
|
||||
bool has_active = false;
|
||||
lockData();
|
||||
while ( (req = (QueuedRequest*)mRequestHash.pop_element()) )
|
||||
{
|
||||
if (req->getStatus() == STATUS_QUEUED || req->getStatus() == STATUS_INPROGRESS)
|
||||
if (req->getStatus() == STATUS_INPROGRESS)
|
||||
{
|
||||
++active_count;
|
||||
has_active = true;
|
||||
req->setFlags(FLAG_ABORT | FLAG_AUTO_COMPLETE);
|
||||
continue;
|
||||
}
|
||||
if (req->getStatus() == STATUS_QUEUED)
|
||||
{
|
||||
++queued_count;
|
||||
req->setStatus(STATUS_ABORTED); // avoid assert in deleteRequest
|
||||
}
|
||||
req->deleteRequest();
|
||||
}
|
||||
if (active_count)
|
||||
unlockData();
|
||||
if (queued_count)
|
||||
{
|
||||
LL_WARNS() << "~LLQueuedThread() called with active requests: " << active_count << LL_ENDL;
|
||||
LL_WARNS() << "~LLQueuedThread() called with unpocessed requests: " << queued_count << LL_ENDL;
|
||||
}
|
||||
if (has_active)
|
||||
{
|
||||
LL_WARNS() << "~LLQueuedThread() called with active requests!" << LL_ENDL;
|
||||
ms_sleep(100); // last chance for request to finish
|
||||
printQueueStats();
|
||||
}
|
||||
|
||||
mRequestQueue.close();
|
||||
|
|
@ -570,7 +585,12 @@ LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U3
|
|||
|
||||
LLQueuedThread::QueuedRequest::~QueuedRequest()
|
||||
{
|
||||
llassert_always(mStatus == STATUS_DELETE);
|
||||
if (mStatus != STATUS_DELETE)
|
||||
{
|
||||
// The only method to delete a request is deleteRequest(),
|
||||
// it should have set the status to STATUS_DELETE
|
||||
LL_ERRS() << "LLQueuedThread::QueuedRequest deleted with status " << mStatus << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
//virtual
|
||||
|
|
|
|||
|
|
@ -1348,10 +1348,6 @@ bool gunzip_file(const std::string& srcfile, const std::string& dstfile)
|
|||
} while(gzeof(src) == 0);
|
||||
fclose(dst);
|
||||
dst = NULL;
|
||||
#if LL_WINDOWS
|
||||
// Rename in windows needs the dstfile to not exist.
|
||||
LLFile::remove(dstfile, ENOENT);
|
||||
#endif
|
||||
if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
|
||||
retval = true;
|
||||
err:
|
||||
|
|
@ -1399,10 +1395,6 @@ bool gzip_file(const std::string& srcfile, const std::string& dstfile)
|
|||
|
||||
gzclose(dst);
|
||||
dst = NULL;
|
||||
#if LL_WINDOWS
|
||||
// Rename in windows needs the dstfile to not exist.
|
||||
LLFile::remove(dstfile);
|
||||
#endif
|
||||
if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
|
||||
retval = true;
|
||||
err:
|
||||
|
|
|
|||
|
|
@ -54,14 +54,29 @@ std::string apr_strerror_helper(apr_status_t rv)
|
|||
*****************************************************************************/
|
||||
|
||||
#define ensure_equals_(left, right) \
|
||||
ensure_equals(STRINGIZE(#left << " != " << #right), (left), (right))
|
||||
do { \
|
||||
auto _left_val = (left); \
|
||||
auto _right_val = (right); \
|
||||
if (_left_val != _right_val) { \
|
||||
std::string _msg = std::string(#left) + " != " + std::string(#right); \
|
||||
tut::ensure_equals(_msg, _left_val, _right_val); \
|
||||
} else { \
|
||||
tut::ensure_equals("", _left_val, _right_val); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define aprchk(expr) aprchk_(#expr, (expr))
|
||||
static void aprchk_(const char* call, apr_status_t rv, apr_status_t expected=APR_SUCCESS)
|
||||
{
|
||||
tut::ensure_equals(STRINGIZE(call << " => " << rv << ": " << apr_strerror_helper
|
||||
(rv)),
|
||||
rv, expected);
|
||||
if (rv != expected)
|
||||
{
|
||||
std::string msg = std::string(call) + " => " + std::to_string(rv) + ": " + apr_strerror_helper(rv);
|
||||
tut::ensure_equals(msg, rv, expected);
|
||||
}
|
||||
else
|
||||
{
|
||||
tut::ensure_equals("", rv, expected);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -78,11 +93,14 @@ static std::string readfile(const std::string& pathname, const std::string& desc
|
|||
std::string use_desc(desc);
|
||||
if (use_desc.empty())
|
||||
{
|
||||
use_desc = STRINGIZE("in " << pathname);
|
||||
use_desc = "in " + pathname;
|
||||
}
|
||||
std::ifstream inf(pathname.c_str());
|
||||
std::string output;
|
||||
tut::ensure(STRINGIZE("No output " << use_desc), bool(std::getline(inf, output)));
|
||||
if (!std::getline(inf, output))
|
||||
{
|
||||
tut::ensure("No output " + use_desc, false);
|
||||
}
|
||||
std::string more;
|
||||
while (std::getline(inf, more))
|
||||
{
|
||||
|
|
@ -108,8 +126,8 @@ void waitfor(LLProcess& proc, int timeout=60)
|
|||
{
|
||||
yield();
|
||||
}
|
||||
tut::ensure(STRINGIZE("process took longer than " << timeout << " seconds to terminate"),
|
||||
i < timeout);
|
||||
std::string msg = "process took longer than " + std::to_string(timeout) + " seconds to terminate";
|
||||
tut::ensure(msg, i < timeout);
|
||||
}
|
||||
|
||||
void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60)
|
||||
|
|
@ -119,8 +137,8 @@ void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60)
|
|||
{
|
||||
yield();
|
||||
}
|
||||
tut::ensure(STRINGIZE("process took longer than " << timeout << " seconds to terminate"),
|
||||
i < timeout);
|
||||
std::string msg = "process took longer than " + std::to_string(timeout) + " seconds to terminate";
|
||||
tut::ensure(msg, i < timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -153,7 +171,8 @@ struct PythonProcessLauncher
|
|||
try
|
||||
{
|
||||
mPy = LLProcess::create(mParams);
|
||||
tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), bool(mPy));
|
||||
std::string msg = "Couldn't launch " + mDesc + " script";
|
||||
tut::ensure(msg, bool(mPy));
|
||||
}
|
||||
catch (const tut::failure&)
|
||||
{
|
||||
|
|
@ -214,7 +233,8 @@ struct PythonProcessLauncher
|
|||
mParams.args.add(out.getName());
|
||||
run();
|
||||
// assuming the script wrote to that file, read it
|
||||
return readfile(out.getName(), STRINGIZE("from " << mDesc << " script"));
|
||||
std::string desc = "from " + mDesc + " script";
|
||||
return readfile(out.getName(), desc);
|
||||
}
|
||||
|
||||
LLProcess::Params mParams;
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ const std::string HTTP_IN_HEADER_LOCATION("location");
|
|||
const std::string HTTP_IN_HEADER_RETRY_AFTER("retry-after");
|
||||
const std::string HTTP_IN_HEADER_SET_COOKIE("set-cookie");
|
||||
const std::string HTTP_IN_HEADER_USER_AGENT("user-agent");
|
||||
const std::string HTTP_IN_HEADER_X_CONTENT_TYPE_OPTIONS("x-content-type-options");
|
||||
const std::string HTTP_IN_HEADER_X_FORWARDED_FOR("x-forwarded-for");
|
||||
|
||||
const std::string HTTP_CONTENT_LLSD_XML("application/llsd+xml");
|
||||
|
|
@ -122,6 +123,7 @@ const std::string HTTP_CONTENT_IMAGE_BMP("image/bmp");
|
|||
|
||||
const std::string HTTP_NO_CACHE("no-cache");
|
||||
const std::string HTTP_NO_CACHE_CONTROL("no-cache, max-age=0");
|
||||
const std::string HTTP_NOSNIFF("nosniff");
|
||||
|
||||
const std::string HTTP_VERB_INVALID("(invalid)");
|
||||
const std::string HTTP_VERB_HEAD("HEAD");
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@ extern const std::string HTTP_IN_HEADER_LOCATION;
|
|||
extern const std::string HTTP_IN_HEADER_RETRY_AFTER;
|
||||
extern const std::string HTTP_IN_HEADER_SET_COOKIE;
|
||||
extern const std::string HTTP_IN_HEADER_USER_AGENT;
|
||||
extern const std::string HTTP_IN_HEADER_X_CONTENT_TYPE_OPTIONS;
|
||||
extern const std::string HTTP_IN_HEADER_X_FORWARDED_FOR;
|
||||
|
||||
//// HTTP Content Types ////
|
||||
|
|
@ -215,5 +216,6 @@ extern const std::string HTTP_CONTENT_IMAGE_BMP;
|
|||
//// HTTP Cache Settings ////
|
||||
extern const std::string HTTP_NO_CACHE;
|
||||
extern const std::string HTTP_NO_CACHE_CONTROL;
|
||||
extern const std::string HTTP_NOSNIFF;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -576,10 +576,6 @@ bool LLCrashLogger::init()
|
|||
std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log.old");
|
||||
std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "crashreport.log");
|
||||
|
||||
#if LL_WINDOWS
|
||||
LLAPRFile::remove(old_log_file);
|
||||
#endif
|
||||
|
||||
LLFile::rename(log_file.c_str(), old_log_file.c_str());
|
||||
|
||||
// Set the log file to crashreport.log
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
/**
|
||||
/**
|
||||
* @file lldir_utils_objc.mm
|
||||
* @brief Cocoa implementation of directory utilities for macOS
|
||||
*
|
||||
* $LicenseInfo:firstyear=2020&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2020, 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$
|
||||
*/
|
||||
*/
|
||||
#if LL_DARWIN
|
||||
|
||||
//WARNING: This file CANNOT use standard linden includes due to conflicts between definitions of BOOL
|
||||
|
|
@ -39,18 +39,18 @@ std::string getSystemTempFolder()
|
|||
tempDir = @"/tmp";
|
||||
result = std::string([tempDir UTF8String]);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//findSystemDirectory scoped exclusively to this file.
|
||||
//findSystemDirectory scoped exclusively to this file.
|
||||
std::string findSystemDirectory(NSSearchPathDirectory searchPathDirectory,
|
||||
NSSearchPathDomainMask domainMask)
|
||||
{
|
||||
std::string result;
|
||||
@autoreleasepool {
|
||||
NSString *path = nil;
|
||||
|
||||
|
||||
// Search for the path
|
||||
NSArray* paths = NSSearchPathForDirectoriesInDomains(searchPathDirectory,
|
||||
domainMask,
|
||||
|
|
@ -60,10 +60,10 @@ std::string findSystemDirectory(NSSearchPathDirectory searchPathDirectory,
|
|||
path = [paths objectAtIndex:0];
|
||||
//HACK: Always attempt to create directory, ignore errors.
|
||||
NSError *error = nil;
|
||||
|
||||
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
|
||||
|
||||
|
||||
|
||||
|
||||
result = std::string([path UTF8String]);
|
||||
}
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ std::string getSystemResourceFolder()
|
|||
NSString *bundlePath = [[NSBundle mainBundle] resourcePath];
|
||||
result = std::string([bundlePath UTF8String]);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ std::string getSystemApplicationSupportFolder()
|
|||
{
|
||||
return findSystemDirectory (NSApplicationSupportDirectory,
|
||||
NSUserDomainMask);
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // LL_DARWIN
|
||||
|
|
|
|||
|
|
@ -103,9 +103,6 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp
|
|||
const std::string old_filename = LLDiskCache::metaDataToFilepath(old_file_id, old_file_type);
|
||||
const std::string new_filename = LLDiskCache::metaDataToFilepath(new_file_id, new_file_type);
|
||||
|
||||
// Rename needs the new file to not exist.
|
||||
LLFileSystem::removeFile(new_file_id, new_file_type, ENOENT);
|
||||
|
||||
if (LLFile::rename(old_filename, new_filename) != 0)
|
||||
{
|
||||
// We would like to return false here indicating the operation
|
||||
|
|
|
|||
|
|
@ -554,11 +554,6 @@ public:
|
|||
|
||||
}
|
||||
|
||||
if (!opj_setup_encoder(encoder, ¶meters, image))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
U32 width_tiles = (rawImageIn.getWidth() >> 6);
|
||||
U32 height_tiles = (rawImageIn.getHeight() >> 6);
|
||||
|
||||
|
|
@ -572,6 +567,19 @@ public:
|
|||
height_tiles = 1;
|
||||
}
|
||||
|
||||
if (width_tiles == 1 || height_tiles == 1)
|
||||
{
|
||||
// Images with either dimension less than 32 need less number of resolutions otherwise they error
|
||||
int min_dim = rawImageIn.getWidth() < rawImageIn.getHeight() ? rawImageIn.getWidth() : rawImageIn.getHeight();
|
||||
int max_res = 1 + (int)floor(log2(min_dim));
|
||||
parameters.numresolution = max_res;
|
||||
}
|
||||
|
||||
if (!opj_setup_encoder(encoder, ¶meters, image))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
U32 tile_count = width_tiles * height_tiles;
|
||||
U32 data_size_guess = tile_count * TILE_SIZE;
|
||||
|
||||
|
|
|
|||
|
|
@ -774,6 +774,7 @@ void LLPermissions::importLLSD(const LLSD& sd_perm)
|
|||
}
|
||||
}
|
||||
|
||||
fixOwnership();
|
||||
fix();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5713,6 +5713,8 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
|
|||
{
|
||||
try
|
||||
{
|
||||
// providing mIndices should help avoid unused vertices
|
||||
// but those should have been filtered out on upload
|
||||
vert_count = static_cast<S32>(meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count));
|
||||
}
|
||||
catch (std::bad_alloc&)
|
||||
|
|
@ -5722,10 +5724,16 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
|
|||
}
|
||||
}
|
||||
|
||||
if (vert_count < 65535 && vert_count != 0)
|
||||
// Probably should be using meshopt_remapVertexBuffer instead of remaping manually
|
||||
if (vert_count < 65535 && vert_count > 0)
|
||||
{
|
||||
//copy results back into volume
|
||||
resizeVertices(vert_count);
|
||||
if (mNumVertices == 0)
|
||||
{
|
||||
LLError::LLUserWarningMsg::showOutOfMemory();
|
||||
LL_ERRS("LLCoros") << "Failed to allocate memory for resizeVertices(" << vert_count << ")" << LL_ENDL;
|
||||
}
|
||||
|
||||
if (!data.w.empty())
|
||||
{
|
||||
|
|
@ -5738,13 +5746,27 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
|
|||
{
|
||||
U32 src_idx = i;
|
||||
U32 dst_idx = remap[i];
|
||||
if (dst_idx >= (U32)mNumVertices)
|
||||
if (dst_idx == U32_MAX)
|
||||
{
|
||||
// Unused indices? Probably need to resize mIndices
|
||||
dst_idx = mNumVertices - 1;
|
||||
llassert(false);
|
||||
LL_DEBUGS_ONCE("LLVOLUME") << "U32_MAX destination index, substituting" << LL_ENDL;
|
||||
}
|
||||
else if (dst_idx >= (U32)mNumVertices)
|
||||
{
|
||||
dst_idx = mNumVertices - 1;
|
||||
// Shouldn't happen, figure out what gets returned in remap and why.
|
||||
llassert(false);
|
||||
LL_DEBUGS_ONCE("LLVOLUME") << "Invalid destination index, substituting" << LL_ENDL;
|
||||
}
|
||||
if (src_idx >= (U32)data.p.size())
|
||||
{
|
||||
// data.p.size() is supposed to be equal to mNumIndices
|
||||
src_idx = (U32)(data.p.size() - 1);
|
||||
llassert(false);
|
||||
LL_DEBUGS_ONCE("LLVOLUME") << "Invalid source index, substituting" << LL_ENDL;
|
||||
}
|
||||
mIndices[i] = dst_idx;
|
||||
|
||||
mPositions[dst_idx].load3(data.p[src_idx].mV);
|
||||
|
|
@ -5778,7 +5800,7 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (vert_count == 0)
|
||||
if (vert_count <= 0)
|
||||
{
|
||||
LL_WARNS_ONCE("LLVOLUME") << "meshopt_generateVertexRemapMulti failed to process a model or model was invalid" << LL_ENDL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,22 @@ LLCoprocedureManager::LLCoprocedureManager()
|
|||
|
||||
LLCoprocedureManager::~LLCoprocedureManager()
|
||||
{
|
||||
close();
|
||||
try
|
||||
{
|
||||
close();
|
||||
}
|
||||
catch (const boost::fibers::fiber_error&)
|
||||
{
|
||||
LL_WARNS() << "Fiber error during ~LLCoprocedureManager()" << LL_ENDL;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// Shutting down, just log it
|
||||
LL_WARNS() << "Exception during ~LLCoprocedureManager(): " << e.what() << LL_ENDL;
|
||||
}
|
||||
mPropertyQueryFn.clear();
|
||||
mPropertyDefineFn.clear();
|
||||
mPoolMap.clear();
|
||||
}
|
||||
|
||||
void LLCoprocedureManager::initializePool(const std::string &poolName, size_t queue_size)
|
||||
|
|
@ -365,6 +380,22 @@ LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size, s
|
|||
|
||||
LLCoprocedurePool::~LLCoprocedurePool()
|
||||
{
|
||||
try
|
||||
{
|
||||
close(); // should have been closed already, but shouldn't hurt
|
||||
mStatusListener.disconnect();
|
||||
mPendingCoprocs.reset();
|
||||
mCoroMapping.clear();
|
||||
}
|
||||
catch (const boost::fibers::fiber_error&)
|
||||
{
|
||||
LL_WARNS() << "Fiber error during ~LLCoprocedurePool() " << mPoolName << LL_ENDL;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// Shutting down, just log it
|
||||
LL_WARNS() << "Exception " << e.what() << " during ~LLCoprocedurePool() in " << mPoolName << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
# -*- cmake -*-
|
||||
|
||||
project(llphysicsextensionsos)
|
||||
|
||||
include(00-Common)
|
||||
include(LLCommon)
|
||||
include(LLMath)
|
||||
include(VHACD)
|
||||
|
||||
set(llphysicsextensionsos_SOURCE_FILES
|
||||
llconvexdecomposition.cpp
|
||||
llconvexdecompositionvhacd.cpp
|
||||
llpathinglib.cpp
|
||||
LLPathingLibStubImpl.cpp
|
||||
llphysicsextensions.cpp
|
||||
LLPhysicsExtensionsStubImpl.cpp
|
||||
)
|
||||
|
||||
set(llphysicsextensionsos_HEADER_FILES
|
||||
CMakeLists.txt
|
||||
llconvexdecomposition.h
|
||||
llconvexdecompositionvhacd.h
|
||||
llpathinglib.h
|
||||
LLPathingLibStubImpl.h
|
||||
llphysicsextensions.h
|
||||
LLPhysicsExtensionsStubImpl.h
|
||||
)
|
||||
|
||||
set_source_files_properties(${llphysicsextensionsos_HEADER_FILES}
|
||||
PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
|
||||
list(APPEND llphysicsextensionsos_SOURCE_FILES ${llphysicsextensionsos_HEADER_FILES})
|
||||
|
||||
add_library (llphysicsextensionsos ${llphysicsextensionsos_SOURCE_FILES})
|
||||
target_include_directories(llphysicsextensionsos INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
target_link_libraries(llphysicsextensionsos
|
||||
llcommon
|
||||
llmath
|
||||
ll::vhacd)
|
||||
|
||||
if(WINDOWS)
|
||||
target_compile_options(llphysicsextensionsos PRIVATE /bigobj)
|
||||
endif()
|
||||
|
||||
# Add tests
|
||||
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* @file LLPathingLibStubImpl.cpp
|
||||
* @author prep@lindenlab.com
|
||||
* @brief A stubbed implementation of LLPathingLib
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 20112010, 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 "llpathinglib.h"
|
||||
#include "LLPathingLibStubImpl.h"
|
||||
|
||||
#include "llsd.h"
|
||||
|
||||
//=============================================================================
|
||||
LLPathingLibImpl::LLPathingLibImpl()
|
||||
{
|
||||
}
|
||||
|
||||
LLPathingLibImpl::~LLPathingLibImpl()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
LLPathingLib* LLPathingLibImpl::getInstance()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
LLPathingLib::LLPLResult LLPathingLibImpl::initSystem()
|
||||
{
|
||||
return LLPL_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
LLPathingLib::LLPLResult LLPathingLibImpl::quitSystem()
|
||||
{
|
||||
return LLPL_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
LLPathingLib::LLPLResult LLPathingLibImpl::extractNavMeshSrcFromLLSD( const LLSD::Binary& dataBlock, int dir )
|
||||
{
|
||||
return LLPL_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
void LLPathingLibImpl::processNavMeshData()
|
||||
{
|
||||
}
|
||||
|
||||
LLPathingLibImpl::LLPLResult LLPathingLibImpl::generatePath( const PathingPacket& pathingPacket )
|
||||
{
|
||||
return LLPL_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
void LLPathingLibImpl::setNavMeshMaterialType( LLPLCharacterType materialType )
|
||||
{
|
||||
}
|
||||
|
||||
void LLPathingLibImpl::setNavMeshColors( const NavMeshColors& color )
|
||||
{
|
||||
}
|
||||
|
||||
void LLPathingLibImpl::renderNavMesh()
|
||||
{
|
||||
}
|
||||
|
||||
void LLPathingLibImpl::renderNavMeshEdges()
|
||||
{
|
||||
}
|
||||
|
||||
void LLPathingLibImpl::renderNavMeshShapesVBO( U32 shapeRenderFlags )
|
||||
{
|
||||
}
|
||||
|
||||
void LLPathingLibImpl::renderPath()
|
||||
{
|
||||
}
|
||||
|
||||
void LLPathingLibImpl::renderPathBookend( LLRender& gl, LLPathingLib::LLPLPathBookEnd type )
|
||||
{
|
||||
}
|
||||
|
||||
void LLPathingLibImpl::cleanupVBOManager()
|
||||
{
|
||||
}
|
||||
|
||||
void LLPathingLibImpl::cleanupResidual()
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* @file LLPathingLibSubImpl.h
|
||||
* @author prep@lindenlab.com
|
||||
* @brief A stubbed implementation of LLPathingLib
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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_PATHING_LIB_H
|
||||
#define LL_PATHING_LIB_H
|
||||
|
||||
#include "llpathinglib.h"
|
||||
|
||||
class LLSD;
|
||||
|
||||
//=============================================================================
|
||||
class LLPathingLibImpl : public LLPathingLib
|
||||
{
|
||||
public:
|
||||
LLPathingLibImpl();
|
||||
virtual ~LLPathingLibImpl();
|
||||
|
||||
// Obtain a pointer to the actual implementation
|
||||
static LLPathingLib* getInstance();
|
||||
static LLPathingLib::LLPLResult initSystem();
|
||||
static LLPathingLib::LLPLResult quitSystem();
|
||||
|
||||
//Extract and store navmesh data from the llsd datablock sent down by the server
|
||||
virtual LLPLResult extractNavMeshSrcFromLLSD( const LLSD::Binary& dataBlock, int dir );
|
||||
//Stitch any stored navmeshes together
|
||||
virtual void processNavMeshData();
|
||||
|
||||
//Method used to generate and visualize a path on the viewers navmesh
|
||||
virtual LLPLResult generatePath( const PathingPacket& pathingPacket );
|
||||
|
||||
//Set the material type for the heatmap type
|
||||
virtual void setNavMeshMaterialType( LLPLCharacterType materialType );
|
||||
//Set the various navmesh colors
|
||||
virtual void setNavMeshColors( const NavMeshColors& color );
|
||||
|
||||
//The entry method to rendering the client side navmesh
|
||||
virtual void renderNavMesh();
|
||||
//The entry method to rendering the client side navmesh edges
|
||||
virtual void renderNavMeshEdges();
|
||||
//The entry method to render the client navmesh shapes VBO
|
||||
virtual void renderNavMeshShapesVBO( U32 shapeRenderFlags );
|
||||
//The entry method to render the clients designated path
|
||||
virtual void renderPath();
|
||||
//The entry method to render the capsule bookends for the clients designated path
|
||||
virtual void renderPathBookend( LLRender& gl, LLPathingLib::LLPLPathBookEnd type );
|
||||
|
||||
//Method to delete any vbo's that are currently being managed by the pathing library
|
||||
virtual void cleanupVBOManager();
|
||||
//Method to cleanup any allocations within the implementation
|
||||
virtual void cleanupResidual();
|
||||
};
|
||||
|
||||
#endif //LL_PATHING_LIB_H
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* @file LLPhysicsExtensionsStubImpl.cpp
|
||||
* @author prep@lindenlab.com
|
||||
* @brief A stubbed implementation of LLPhysicsExtensions
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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 "llphysicsextensions.h"
|
||||
#include "LLPhysicsExtensionsStubImpl.h"
|
||||
|
||||
//=============================================================================
|
||||
LLPhysicsExtensionsImpl::LLPhysicsExtensionsImpl()
|
||||
{
|
||||
}
|
||||
|
||||
LLPhysicsExtensionsImpl::~LLPhysicsExtensionsImpl()
|
||||
{
|
||||
}
|
||||
|
||||
bool LLPhysicsExtensionsImpl::initSystem()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LLPhysicsExtensionsImpl::quitSystem()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* @file LLPhysicsExtensionsSubImpl.h
|
||||
* @author prep@lindenlab.com
|
||||
* @brief A stubbed implementation of LLPhysicsExtensions
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2012, 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_PHYSICS_EXTENSIONS_STUB_IMPL_H
|
||||
#define LL_PHYSICS_EXTENSIONS_STUB_IMPL_H
|
||||
|
||||
#include "llphysicsextensions.h"
|
||||
|
||||
//=============================================================================
|
||||
class LLPhysicsExtensionsImpl : public LLPhysicsExtensions
|
||||
{
|
||||
public:
|
||||
|
||||
LLPhysicsExtensionsImpl();
|
||||
virtual ~LLPhysicsExtensionsImpl();
|
||||
|
||||
static bool initSystem();
|
||||
static bool quitSystem();
|
||||
};
|
||||
|
||||
#endif //LL_PHYSICS_EXTENSIONS_STUB_IMPL_H
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* @file llpathinglib.cpp
|
||||
* @author prep@lindenlab.com
|
||||
* @brief LLPathingLib core creation methods
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&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 "LLPathingLibStubImpl.h"
|
||||
|
||||
#include "llpathinglib.h"
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*static */bool LLPathingLib::s_isInitialized = false;
|
||||
|
||||
//=============================================================================
|
||||
|
||||
|
||||
/*static*/bool LLPathingLib::isFunctional()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*static*/LLPathingLib* LLPathingLib::getInstance()
|
||||
{
|
||||
if ( !s_isInitialized )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return LLPathingLibImpl::getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*static */LLPathingLib::LLPLResult LLPathingLib::initSystem()
|
||||
{
|
||||
if ( LLPathingLibImpl::initSystem() == LLPL_OK )
|
||||
{
|
||||
s_isInitialized = true;
|
||||
return LLPL_OK;
|
||||
}
|
||||
return LLPL_UNKOWN_ERROR;
|
||||
}
|
||||
//=============================================================================
|
||||
/*static */LLPathingLib::LLPLResult LLPathingLib::quitSystem()
|
||||
{
|
||||
LLPLResult quitResult = LLPL_UNKOWN_ERROR;
|
||||
|
||||
if (s_isInitialized)
|
||||
{
|
||||
quitResult = LLPathingLibImpl::quitSystem();
|
||||
s_isInitialized = false;
|
||||
}
|
||||
|
||||
return quitResult;
|
||||
}
|
||||
//=============================================================================
|
||||
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
/**
|
||||
* @file llpathinglib.cpp
|
||||
* @author prep@lindenlab.com
|
||||
* @brief LLPathingLib interface definition
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&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_PATHING_LIBRARY
|
||||
#define LL_PATHING_LIBRARY
|
||||
|
||||
#include "llpreprocessor.h"
|
||||
#include "llsd.h"
|
||||
#include "v3dmath.h"
|
||||
#include "v4math.h"
|
||||
|
||||
#include "v4color.h"
|
||||
#include "v4coloru.h"
|
||||
#include "llphysicsextensions.h"
|
||||
|
||||
typedef int bool32;
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define LLCD_CALL __cdecl
|
||||
#else
|
||||
#define LLCD_CALL
|
||||
#endif
|
||||
|
||||
class LLRender;
|
||||
|
||||
//=============================================================================
|
||||
class LLPathingLib
|
||||
{
|
||||
|
||||
public:
|
||||
enum LLShapeType
|
||||
{
|
||||
LLST_WalkableObjects = 0,
|
||||
LLST_ObstacleObjects,
|
||||
LLST_MaterialPhantoms,
|
||||
LLST_ExclusionPhantoms,
|
||||
LLST_MaxShapeTypes = LLST_ExclusionPhantoms+1,
|
||||
LLST_None = LLST_MaxShapeTypes+2,
|
||||
LLST_SimpleBox = LLST_None+1,
|
||||
LLST_SimpleCapsule = LLST_SimpleBox+1,
|
||||
};
|
||||
|
||||
enum LLShapeTypeFlag
|
||||
{
|
||||
LLSTB_WalkableObjects = 0x1 << 1,
|
||||
LLSTB_ObstacleObjects = 0x1 << 2,
|
||||
LLSTB_MaterialPhantoms = 0x1 << 3,
|
||||
LLSTB_ExclusionPhantoms = 0x1 << 4,
|
||||
LLSTB_None = 0x1 << 5
|
||||
};
|
||||
|
||||
enum LLPLPathBookEnd
|
||||
{
|
||||
LLPL_START = 0,
|
||||
LLPL_END,
|
||||
};
|
||||
|
||||
enum LLPLResult
|
||||
{
|
||||
LLPL_OK = 0,
|
||||
LLPL_NOTSET,
|
||||
LLPL_ERROR,
|
||||
LLPL_NO_NAVMESH,
|
||||
LLPL_UNKOWN_ERROR,
|
||||
LLPL_NO_PATH,
|
||||
LLPL_PATH_GENERATED_OK,
|
||||
LLPL_NOT_IMPLEMENTED,
|
||||
};
|
||||
|
||||
enum LLPLCharacterType
|
||||
{
|
||||
LLPL_CHARACTER_TYPE_A = 4,
|
||||
LLPL_CHARACTER_TYPE_B = 3,
|
||||
LLPL_CHARACTER_TYPE_C = 2,
|
||||
LLPL_CHARACTER_TYPE_D = 1,
|
||||
LLPL_CHARACTER_TYPE_NONE = 0
|
||||
};
|
||||
|
||||
struct PathingPacket
|
||||
{
|
||||
PathingPacket() : mHasPointA(false), mHasPointB(false), mCharacterWidth(0.0f), mCharacterType(LLPL_CHARACTER_TYPE_NONE) {}
|
||||
bool mHasPointA;
|
||||
LLVector3 mStartPointA;
|
||||
LLVector3 mEndPointA;
|
||||
bool mHasPointB;
|
||||
LLVector3 mStartPointB;
|
||||
LLVector3 mEndPointB;
|
||||
F32 mCharacterWidth;
|
||||
LLPLCharacterType mCharacterType;
|
||||
};
|
||||
|
||||
struct NavMeshColors
|
||||
{
|
||||
LLColor4U mWalkable;
|
||||
LLColor4U mObstacle;
|
||||
LLColor4U mMaterial;
|
||||
LLColor4U mExclusion;
|
||||
LLColor4U mConnectedEdge;
|
||||
LLColor4U mBoundaryEdge;
|
||||
LLColor4 mHeatColorBase;
|
||||
LLColor4 mHeatColorMax;
|
||||
LLColor4U mFaceColor;
|
||||
LLColor4U mStarValid;
|
||||
LLColor4U mStarInvalid;
|
||||
LLColor4U mTestPath;
|
||||
LLColor4U mWaterColor;
|
||||
};
|
||||
|
||||
public:
|
||||
//Ctor
|
||||
LLPathingLib() {}
|
||||
virtual ~LLPathingLib() {}
|
||||
|
||||
/// @returns false if this is the stub
|
||||
static bool isFunctional();
|
||||
|
||||
// Obtain a pointer to the actual implementation
|
||||
static LLPathingLib* getInstance();
|
||||
static LLPathingLib::LLPLResult initSystem();
|
||||
static LLPathingLib::LLPLResult quitSystem();
|
||||
|
||||
//Extract and store navmesh data from the llsd datablock sent down by the server
|
||||
virtual LLPLResult extractNavMeshSrcFromLLSD( const LLSD::Binary& dataBlock, int dir ) = 0;
|
||||
//Stitch any stored navmeshes together
|
||||
virtual void processNavMeshData( ) = 0;
|
||||
|
||||
//Method used to generate and visualize a path on the viewers navmesh
|
||||
virtual LLPLResult generatePath( const PathingPacket& pathingPacket ) = 0;
|
||||
|
||||
//Set the material type for the heatmap type
|
||||
virtual void setNavMeshMaterialType( LLPLCharacterType materialType ) = 0;
|
||||
//Set the various navmesh colors
|
||||
virtual void setNavMeshColors( const NavMeshColors& color ) = 0;
|
||||
|
||||
//The entry method to rendering the client side navmesh
|
||||
virtual void renderNavMesh() = 0;
|
||||
//The entry method to rendering the client side navmesh edges
|
||||
virtual void renderNavMeshEdges() = 0;
|
||||
//The entry method to render the client navmesh shapes VBO
|
||||
virtual void renderNavMeshShapesVBO( U32 shapeRenderFlags ) = 0;
|
||||
//The entry method to render the clients designated path
|
||||
virtual void renderPath() = 0;
|
||||
//The entry method to render the capsule bookends for the clients designated path
|
||||
virtual void renderPathBookend( LLRender& gl, LLPathingLib::LLPLPathBookEnd type ) = 0;
|
||||
//Renders all of the generated simple shapes (using their default transforms)
|
||||
virtual void renderSimpleShapes( LLRender& gl, F32 regionsWaterHeight ) = 0;
|
||||
|
||||
//Method called from second life to create a capsule from properties of a character
|
||||
virtual void createPhysicsCapsuleRep( F32 length, F32 radius, BOOL horizontal, const LLUUID& id ) = 0;
|
||||
//Removes any cached physics capsule using a list of cached uuids
|
||||
virtual void cleanupPhysicsCapsuleRepResiduals() = 0;
|
||||
//Renders a selected uuids physics rep
|
||||
virtual void renderSimpleShapeCapsuleID( LLRender& gl, const LLUUID& id, const LLVector3& pos, const LLQuaternion& rot ) = 0;
|
||||
|
||||
//Method to delete any vbo's that are currently being managed by the pathing library
|
||||
virtual void cleanupVBOManager( ) = 0;
|
||||
//Method to cleanup any allocations within the implementation
|
||||
virtual void cleanupResidual( ) = 0;
|
||||
private:
|
||||
static bool s_isInitialized;
|
||||
};
|
||||
|
||||
#endif //LL_PATHING_LIBRARY
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* @file llphysicsextensions.cpp
|
||||
* @author nyx@lindenlab.com
|
||||
* @brief LLPhysicsExtensions core initialization methods
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&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 "llphysicsextensions.h"
|
||||
#include "LLPhysicsExtensionsStubImpl.h"
|
||||
|
||||
|
||||
//disable the undefined symbol optimization
|
||||
//#pragma warning (disable : 4221)
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*static */bool LLPhysicsExtensions::s_isInitialized = false;
|
||||
|
||||
|
||||
/*static*/bool LLPhysicsExtensions::isFunctional()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*static*/LLPhysicsExtensions* LLPhysicsExtensions::getInstance()
|
||||
{
|
||||
if ( !s_isInitialized )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return LLPhysicsExtensionsImpl::getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*static */bool LLPhysicsExtensions::initSystem()
|
||||
{
|
||||
bool result = LLPhysicsExtensionsImpl::initSystem();
|
||||
if ( result )
|
||||
{
|
||||
s_isInitialized = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
//=============================================================================
|
||||
/*static */bool LLPhysicsExtensions::quitSystem()
|
||||
{
|
||||
return LLPhysicsExtensionsImpl::quitSystem();
|
||||
}
|
||||
//=============================================================================
|
||||
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* @file llphysicsextensions.h
|
||||
* @author nyx@lindenlab.com
|
||||
* @brief LLPhysicsExtensions core shared initialization
|
||||
* routines
|
||||
*
|
||||
* $LicenseInfo:firstyear=2012&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_PHYSICS_EXTENSIONS
|
||||
#define LL_PHYSICS_EXTENSIONS
|
||||
|
||||
#include "llpreprocessor.h"
|
||||
#include "llsd.h"
|
||||
#include "v3dmath.h"
|
||||
|
||||
#define LLPHYSICSEXTENSIONS_VERSION "1.0"
|
||||
|
||||
typedef int bool32;
|
||||
|
||||
class LLPhysicsExtensions
|
||||
{
|
||||
|
||||
public:
|
||||
// Obtain a pointer to the actual implementation
|
||||
static LLPhysicsExtensions* getInstance();
|
||||
|
||||
/// @returns false if this is the stub
|
||||
static bool isFunctional();
|
||||
|
||||
static bool initSystem();
|
||||
static bool quitSystem();
|
||||
|
||||
private:
|
||||
static bool s_isInitialized;
|
||||
};
|
||||
|
||||
#endif //LL_PATHING_LIBRARY
|
||||
|
||||
|
||||
|
|
@ -982,14 +982,18 @@ void LLPluginProcessParent::poll(F64 timeout)
|
|||
}
|
||||
}
|
||||
|
||||
// Remove instances in the done state from the sInstances map.
|
||||
mapInstances_t::iterator itClean = sInstances.begin();
|
||||
while (itClean != sInstances.end())
|
||||
if (sInstancesMutex)
|
||||
{
|
||||
if ((*itClean).second->isDone())
|
||||
itClean = sInstances.erase(itClean);
|
||||
else
|
||||
++itClean;
|
||||
// Remove instances in the done state from the sInstances map.
|
||||
LLCoros::LockType lock(*sInstancesMutex);
|
||||
mapInstances_t::iterator itClean = sInstances.begin();
|
||||
while (itClean != sInstances.end())
|
||||
{
|
||||
if ((*itClean).second->isDone())
|
||||
itClean = sInstances.erase(itClean);
|
||||
else
|
||||
++itClean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,21 +7,21 @@
|
|||
* $LicenseInfo:firstyear=2010&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, 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$
|
||||
*
|
||||
|
|
@ -38,51 +38,51 @@
|
|||
|
||||
void LLCocoaPlugin::setupCocoa()
|
||||
{
|
||||
static bool inited = false;
|
||||
|
||||
if(!inited)
|
||||
{
|
||||
createAutoReleasePool();
|
||||
|
||||
// The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents.
|
||||
// ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr'
|
||||
// when init'ing the Cocoa App window.
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];
|
||||
|
||||
// This is a bit of voodoo taken from the Apple sample code "CarbonCocoa_PictureCursor":
|
||||
// http://developer.apple.com/samplecode/CarbonCocoa_PictureCursor/index.html
|
||||
|
||||
// Needed for Carbon based applications which call into Cocoa
|
||||
NSApplicationLoad();
|
||||
static bool inited = false;
|
||||
|
||||
if(!inited)
|
||||
{
|
||||
createAutoReleasePool();
|
||||
|
||||
// The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents.
|
||||
// ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr'
|
||||
// when init'ing the Cocoa App window.
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];
|
||||
|
||||
// This is a bit of voodoo taken from the Apple sample code "CarbonCocoa_PictureCursor":
|
||||
// http://developer.apple.com/samplecode/CarbonCocoa_PictureCursor/index.html
|
||||
|
||||
// Needed for Carbon based applications which call into Cocoa
|
||||
NSApplicationLoad();
|
||||
|
||||
// Must first call [[[NSWindow alloc] init] release] to get the NSWindow machinery set up so that NSCursor can use a window to cache the cursor image
|
||||
[[[NSWindow alloc] init] release];
|
||||
|
||||
// Must first call [[[NSWindow alloc] init] release] to get the NSWindow machinery set up so that NSCursor can use a window to cache the cursor image
|
||||
[[[NSWindow alloc] init] release];
|
||||
|
||||
mPluginWindow = [NSApp mainWindow];
|
||||
|
||||
deleteAutoReleasePool();
|
||||
|
||||
inited = true;
|
||||
}
|
||||
|
||||
deleteAutoReleasePool();
|
||||
|
||||
inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
static NSAutoreleasePool *sPool = NULL;
|
||||
|
||||
void LLCocoaPlugin::createAutoReleasePool()
|
||||
{
|
||||
if(!sPool)
|
||||
{
|
||||
sPool = [[NSAutoreleasePool alloc] init];
|
||||
}
|
||||
if(!sPool)
|
||||
{
|
||||
sPool = [[NSAutoreleasePool alloc] init];
|
||||
}
|
||||
}
|
||||
|
||||
void LLCocoaPlugin::deleteAutoReleasePool()
|
||||
{
|
||||
if(sPool)
|
||||
{
|
||||
[sPool release];
|
||||
sPool = NULL;
|
||||
}
|
||||
if(sPool)
|
||||
{
|
||||
[sPool release];
|
||||
sPool = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LLCocoaPlugin::LLCocoaPlugin():mHackState(0)
|
||||
|
|
@ -110,12 +110,12 @@ void LLCocoaPlugin::setupGroup()
|
|||
// {
|
||||
// // Start out with a window layer that's way out in front (fixes the problem with the menubar not getting hidden on first switch to fullscreen youtube)
|
||||
// SetWindowGroupName(layer_group, CFSTR("SLPlugin Layer"));
|
||||
// SetWindowGroupLevel(layer_group, kCGOverlayWindowLevel);
|
||||
// SetWindowGroupLevel(layer_group, kCGOverlayWindowLevel);
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
void LLCocoaPlugin::updateWindows()
|
||||
void LLCocoaPlugin::updateWindows()
|
||||
{
|
||||
// NSArray* window_list = [NSApp orderedWindows];
|
||||
// NSWindow* current_window = [window_list objectAtIndex:0];
|
||||
|
|
@ -123,38 +123,38 @@ void LLCocoaPlugin::updateWindows()
|
|||
// bool this_is_front_process = false;
|
||||
// bool parent_is_front_process = false;
|
||||
//
|
||||
//
|
||||
//
|
||||
// // Check for a change in this process's frontmost window.
|
||||
// if ( current_window != mFrontWindow )
|
||||
// {
|
||||
// // and figure out whether this process or its parent are currently frontmost
|
||||
// if ( current_window == parent_window ) parent_is_front_process = true;
|
||||
// if ( current_window == mPluginWindow ) this_is_front_process = true;
|
||||
//
|
||||
//
|
||||
// if (current_window != NULL && mFrontWindow == NULL)
|
||||
// {
|
||||
// // Opening the first window
|
||||
//
|
||||
//
|
||||
// if(mHackState == 0)
|
||||
// {
|
||||
// // Next time through the event loop, lower the window group layer
|
||||
// mHackState = 1;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// if(parent_is_front_process)
|
||||
// {
|
||||
// // Bring this process's windows to the front.
|
||||
// [mPluginWindow makeKeyAndOrderFront:NSApp];
|
||||
// [mPluginWindow setOrderedIndex:0];
|
||||
// }
|
||||
//
|
||||
//
|
||||
// [NSApp activateIgnoringOtherApps:YES];
|
||||
// }
|
||||
//
|
||||
//
|
||||
// else if (( current_window == NULL) && (mFrontWindow != NULL))
|
||||
// {
|
||||
// // Closing the last window
|
||||
//
|
||||
//
|
||||
// if(this_is_front_process)
|
||||
// {
|
||||
// // Try to bring this process's parent to the front
|
||||
|
|
@ -171,7 +171,7 @@ void LLCocoaPlugin::updateWindows()
|
|||
//// }
|
||||
// mHackState = 2;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// mFrontWindow = [window_list objectAtIndex:0];
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,11 +65,20 @@ target_link_libraries(llprimitive
|
|||
llxml
|
||||
llcharacter
|
||||
llrender
|
||||
llphysicsextensions_impl
|
||||
ll::colladadom
|
||||
ll::glm
|
||||
)
|
||||
|
||||
if (HAVOK OR HAVOK_TPV)
|
||||
target_link_libraries(llprimitive
|
||||
llphysicsextensions_impl
|
||||
)
|
||||
else()
|
||||
target_link_libraries(llprimitive
|
||||
llphysicsextensionsos
|
||||
)
|
||||
endif ()
|
||||
|
||||
#add unit tests
|
||||
if (LL_TESTS)
|
||||
INCLUDE(LLAddBuildTest)
|
||||
|
|
|
|||
|
|
@ -923,3 +923,34 @@ void LLGLTFMaterial::updateTextureTracking()
|
|||
// setTEGLTFMaterialOverride is responsible for tracking
|
||||
// for material overrides editor will set it
|
||||
}
|
||||
|
||||
void LLGLTFMaterial::convertTextureTransformToPBR(
|
||||
F32 tex_scale_s,
|
||||
F32 tex_scale_t,
|
||||
F32 tex_offset_s,
|
||||
F32 tex_offset_t,
|
||||
F32 tex_rotation,
|
||||
LLVector2& pbr_scale,
|
||||
LLVector2& pbr_offset,
|
||||
F32& pbr_rotation)
|
||||
{
|
||||
pbr_scale.set(tex_scale_s, tex_scale_t);
|
||||
pbr_rotation = -(tex_rotation) / 2.f;
|
||||
const F32 adjusted_offset_s = tex_offset_s;
|
||||
const F32 adjusted_offset_t = -tex_offset_t;
|
||||
F32 center_adjust_s = 0.5f * (1.0f - tex_scale_s);
|
||||
F32 center_adjust_t = 0.5f * (1.0f - tex_scale_t);
|
||||
|
||||
if (pbr_rotation != 0.0f)
|
||||
{
|
||||
const F32 c = cosf(pbr_rotation);
|
||||
const F32 s = sinf(pbr_rotation);
|
||||
const F32 tmp_s = center_adjust_s * c - center_adjust_t * s;
|
||||
const F32 tmp_t = center_adjust_s * s + center_adjust_t * c;
|
||||
center_adjust_s = tmp_s;
|
||||
center_adjust_t = tmp_t;
|
||||
}
|
||||
|
||||
pbr_offset.set(adjusted_offset_s + center_adjust_s,
|
||||
adjusted_offset_t + center_adjust_t);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -214,6 +214,14 @@ public:
|
|||
bool hasLocalTextures() { return !mTrackingIdToLocalTexture.empty(); }
|
||||
virtual bool replaceLocalTexture(const LLUUID& tracking_id, const LLUUID &old_id, const LLUUID& new_id);
|
||||
virtual void updateTextureTracking();
|
||||
|
||||
// Convert legacy TE transform values to PBR transform values.
|
||||
static void convertTextureTransformToPBR(F32 tex_scale_s, F32 tex_scale_t,
|
||||
F32 tex_offset_s, F32 tex_offset_t,
|
||||
F32 tex_rotation,
|
||||
LLVector2& pbr_scale,
|
||||
LLVector2& pbr_offset,
|
||||
F32& pbr_rotation);
|
||||
protected:
|
||||
static LLVector2 vec2FromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const LLVector2& default_value);
|
||||
static F32 floatFromJson(const std::map<std::string, tinygltf::Value>& object, const char* key, const F32 default_value);
|
||||
|
|
|
|||
|
|
@ -1296,10 +1296,10 @@ LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos)
|
|||
}
|
||||
|
||||
void LLModel::setConvexHullDecomposition(
|
||||
const LLModel::convex_hull_decomposition& decomp)
|
||||
const LLModel::convex_hull_decomposition& decomp, const std::vector<LLModel::PhysicsMesh>& decomp_mesh)
|
||||
{
|
||||
mPhysics.mHull = decomp;
|
||||
mPhysics.mMesh.clear();
|
||||
mPhysics.mMesh = decomp_mesh;
|
||||
updateHullCenters();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -305,7 +305,8 @@ public:
|
|||
S32 mDecompID;
|
||||
|
||||
void setConvexHullDecomposition(
|
||||
const convex_hull_decomposition& decomp);
|
||||
const convex_hull_decomposition& decomp,
|
||||
const std::vector<LLModel::PhysicsMesh>& decomp_mesh);
|
||||
void updateHullCenters();
|
||||
|
||||
LLVector3 mCenterOfHullCenters;
|
||||
|
|
|
|||
|
|
@ -86,7 +86,10 @@ void LLCubeMap::initGL()
|
|||
#endif
|
||||
mImages[i]->setTarget(mTargets[i], LLTexUnit::TT_CUBE_MAP);
|
||||
mRawImages[i] = new LLImageRaw(RESOLUTION, RESOLUTION, 4);
|
||||
mImages[i]->createGLTexture(0, mRawImages[i], texname);
|
||||
if (!mImages[i]->createGLTexture(0, mRawImages[i], texname))
|
||||
{
|
||||
LL_WARNS() << "Failed to create GL texture for environment cubemap face " << i << LL_ENDL;
|
||||
}
|
||||
|
||||
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname);
|
||||
mImages[i]->setAddressMode(LLTexUnit::TAM_CLAMP);
|
||||
|
|
@ -203,7 +206,10 @@ void LLCubeMap::initEnvironmentMap(const std::vector<LLPointer<LLImageRaw> >& ra
|
|||
mImages[i] = new LLImageGL(resolution, resolution, components, true);
|
||||
mImages[i]->setTarget(mTargets[i], LLTexUnit::TT_CUBE_MAP);
|
||||
mRawImages[i] = rawimages[i];
|
||||
mImages[i]->createGLTexture(0, mRawImages[i], texname);
|
||||
if (!mImages[i]->createGLTexture(0, mRawImages[i], texname))
|
||||
{
|
||||
LL_WARNS() << "Failed to create GL texture for environment cubemap face " << i << LL_ENDL;
|
||||
}
|
||||
|
||||
gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_CUBE_MAP, texname);
|
||||
mImages[i]->setAddressMode(LLTexUnit::TAM_CLAMP);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ S32 LLGLSLShader::sIndexedTextureChannels = 0;
|
|||
U32 LLGLSLShader::sMaxGLTFMaterials = 0;
|
||||
U32 LLGLSLShader::sMaxGLTFNodes = 0;
|
||||
bool LLGLSLShader::sProfileEnabled = false;
|
||||
bool LLGLSLShader::sCanProfile = true;
|
||||
std::set<LLGLSLShader*> LLGLSLShader::sInstances;
|
||||
LLGLSLShader::defines_map_t LLGLSLShader::sGlobalDefines;
|
||||
U64 LLGLSLShader::sTotalTimeElapsed = 0;
|
||||
|
|
@ -267,7 +268,7 @@ void LLGLSLShader::placeProfileQuery(bool for_runtime)
|
|||
|
||||
bool LLGLSLShader::readProfileQuery(bool for_runtime, bool force_read)
|
||||
{
|
||||
if (sProfileEnabled || for_runtime)
|
||||
if ((sProfileEnabled || for_runtime) && sCanProfile)
|
||||
{
|
||||
if (!mProfilePending)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -160,6 +160,7 @@ public:
|
|||
|
||||
static std::set<LLGLSLShader*> sInstances;
|
||||
static bool sProfileEnabled;
|
||||
static bool sCanProfile;
|
||||
|
||||
LLGLSLShader();
|
||||
~LLGLSLShader();
|
||||
|
|
|
|||
|
|
@ -1097,6 +1097,8 @@ void sub_image_lines(U32 target, S32 miplevel, S32 x_offset, S32 y_offset, S32 w
|
|||
// full width texture, do 32 lines at a time
|
||||
for (U32 y_pos = y_offset; y_pos < y_offset_end; y_pos += batch_size)
|
||||
{
|
||||
// If this keeps crashing, pass down data_size, looks like it is using
|
||||
// imageraw->getData(); for data, but goes way over allocated size limit
|
||||
glTexSubImage2D(target, miplevel, x_offset, y_pos, width, batch_size, pixformat, pixtype, src);
|
||||
src += line_width * batch_size;
|
||||
}
|
||||
|
|
@ -1106,6 +1108,8 @@ void sub_image_lines(U32 target, S32 miplevel, S32 x_offset, S32 y_offset, S32 w
|
|||
// partial width or strange height
|
||||
for (U32 y_pos = y_offset; y_pos < y_offset_end; y_pos += 1)
|
||||
{
|
||||
// If this keeps crashing, pass down data_size, looks like it is using
|
||||
// imageraw->getData(); for data, but goes way over allocated size limit
|
||||
glTexSubImage2D(target, miplevel, x_offset, y_pos, width, 1, pixformat, pixtype, src);
|
||||
src += line_width;
|
||||
}
|
||||
|
|
@ -1546,6 +1550,7 @@ bool LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S
|
|||
llassert(mCurrentDiscardLevel >= 0);
|
||||
discard_level = mCurrentDiscardLevel;
|
||||
}
|
||||
discard_level = llmin(discard_level, MAX_DISCARD_LEVEL);
|
||||
|
||||
// Actual image width/height = raw image width/height * 2^discard_level
|
||||
S32 raw_w = imageraw->getWidth() ;
|
||||
|
|
@ -1644,6 +1649,7 @@ bool LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, bool data_
|
|||
discard_level = mCurrentDiscardLevel;
|
||||
}
|
||||
discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
|
||||
discard_level = llmin(discard_level, MAX_DISCARD_LEVEL);
|
||||
|
||||
if (main_thread // <--- always force creation of new_texname when not on main thread ...
|
||||
&& !defer_copy // <--- ... or defer copy is set
|
||||
|
|
|
|||
|
|
@ -1048,6 +1048,7 @@ void LLShaderMgr::clearShaderCache()
|
|||
LL_INFOS("ShaderMgr") << "Removing shader cache at " << shader_cache << LL_ENDL;
|
||||
const std::string mask = "*";
|
||||
gDirUtilp->deleteFilesInDir(shader_cache, mask);
|
||||
LLFile::rmdir(shader_cache);
|
||||
mShaderBinaryCache.clear();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -494,6 +494,7 @@ void LLAccordionCtrl::arrangeMultiple()
|
|||
|
||||
void LLAccordionCtrl::arrange()
|
||||
{
|
||||
LL_PROFILE_ZONE_SCOPED;
|
||||
updateNoTabsHelpTextVisibility();
|
||||
|
||||
if (mAccordionTabs.empty())
|
||||
|
|
|
|||
|
|
@ -248,10 +248,22 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw()
|
|||
void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::reshape(S32 width, S32 height, bool called_from_parent /* = true */)
|
||||
{
|
||||
S32 header_height = mHeaderTextbox->getTextPixelHeight();
|
||||
LLRect old_header_rect = mHeaderTextbox->getRect();
|
||||
|
||||
LLRect textboxRect(HEADER_TEXT_LEFT_OFFSET, (height + header_height) / 2, width, (height - header_height) / 2);
|
||||
mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight());
|
||||
mHeaderTextbox->setRect(textboxRect);
|
||||
if (old_header_rect.getHeight() != textboxRect.getHeight()
|
||||
|| old_header_rect.mLeft != textboxRect.mLeft
|
||||
|| old_header_rect.mTop != textboxRect.mTop
|
||||
|| old_header_rect.getWidth() > textboxRect.getWidth() // reducing header's width
|
||||
|| (old_header_rect.getWidth() < textboxRect.getWidth() && old_header_rect.getWidth() < mHeaderTextbox->getTextPixelWidth()))
|
||||
{
|
||||
// Expensive text reflow
|
||||
// Update if position or height changes
|
||||
// Update if width reduces
|
||||
// But do not update if text already fits and width increases (arguably LLTextBox::reshape should be smarter, not Accordion)
|
||||
mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight());
|
||||
mHeaderTextbox->setRect(textboxRect);
|
||||
}
|
||||
|
||||
if (mHeaderTextbox->getTextPixelWidth() > mHeaderTextbox->getRect().getWidth())
|
||||
{
|
||||
|
|
@ -416,8 +428,11 @@ void LLAccordionCtrlTab::reshape(S32 width, S32 height, bool called_from_parent
|
|||
LLRect headerRect;
|
||||
|
||||
headerRect.setLeftTopAndSize(0, height, width, HEADER_HEIGHT);
|
||||
mHeader->setRect(headerRect);
|
||||
mHeader->reshape(headerRect.getWidth(), headerRect.getHeight());
|
||||
if (mHeader->getRect() != headerRect)
|
||||
{
|
||||
mHeader->setRect(headerRect);
|
||||
mHeader->reshape(headerRect.getWidth(), headerRect.getHeight());
|
||||
}
|
||||
|
||||
if (!mDisplayChildren)
|
||||
return;
|
||||
|
|
@ -464,7 +479,34 @@ void LLAccordionCtrlTab::onUpdateScrollToChild(const LLUICtrl *cntrl)
|
|||
// Translate to parent coordinatess to check if we are in visible rectangle
|
||||
rect.translate(getRect().mLeft, getRect().mBottom);
|
||||
|
||||
if (!getRect().contains(rect))
|
||||
bool needs_to_scroll = false;
|
||||
const LLRect &acc_rect = getRect();
|
||||
if (!acc_rect.contains(rect))
|
||||
{
|
||||
if (acc_rect.mTop < rect.mBottom || acc_rect.mBottom > rect.mTop)
|
||||
{
|
||||
// Content fully not in view
|
||||
needs_to_scroll = true;
|
||||
}
|
||||
else if (acc_rect.getHeight() >= rect.getHeight())
|
||||
{
|
||||
// Content can be displayed fully, but only partially in view
|
||||
needs_to_scroll = true;
|
||||
}
|
||||
else if (acc_rect.mTop <= rect.mTop || acc_rect.mBottom >= rect.mBottom)
|
||||
{
|
||||
// Intersects, but too big to be displayed fully
|
||||
S32 covered_height = acc_rect.mTop > rect.mTop ? rect.mTop - acc_rect.mBottom : acc_rect.mTop - rect.mBottom;
|
||||
constexpr F32 covered_ratio = 0.7f;
|
||||
if (covered_height < covered_ratio * acc_rect.getHeight())
|
||||
{
|
||||
// Try to show bigger portion of the content
|
||||
needs_to_scroll = true;
|
||||
}
|
||||
}
|
||||
// else too big and in the middle of the view as is
|
||||
}
|
||||
if (needs_to_scroll)
|
||||
{
|
||||
// for accordition's scroll, height is in pixels
|
||||
// Back to local coords and calculate position for scroller
|
||||
|
|
@ -932,7 +974,7 @@ void LLAccordionCtrlTab::adjustContainerPanel(const LLRect& child_rect)
|
|||
show_hide_scrollbar(child_rect);
|
||||
updateLayout(child_rect);
|
||||
}
|
||||
else
|
||||
else if (mContainerPanel->getRect() != child_rect)
|
||||
{
|
||||
mContainerPanel->reshape(child_rect.getWidth(), child_rect.getHeight());
|
||||
mContainerPanel->setRect(child_rect);
|
||||
|
|
|
|||
|
|
@ -390,14 +390,17 @@ void LLEmojiDictionary::loadEmojis()
|
|||
continue;
|
||||
}
|
||||
|
||||
std::string category;
|
||||
std::list<std::string> categories = loadCategories(sd);
|
||||
if (categories.empty())
|
||||
{
|
||||
LL_WARNS() << "Skipping invalid emoji descriptor (no categories)" << LL_ENDL;
|
||||
continue;
|
||||
// Should already have a localization for "other symbols"
|
||||
category = "other symbols";
|
||||
}
|
||||
else
|
||||
{
|
||||
category = categories.front();
|
||||
}
|
||||
|
||||
std::string category = categories.front();
|
||||
|
||||
if (std::find(mSkipCategories.begin(), mSkipCategories.end(), category) != mSkipCategories.end())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -117,7 +117,17 @@ void LLEmojiHelper::showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, c
|
|||
S32 top = floater_y - HELPER_FLOATER_OFFSET_Y + rect.getHeight();
|
||||
rect.setLeftTopAndSize(left, top, rect.getWidth(), rect.getHeight());
|
||||
pHelperFloater->setRect(rect);
|
||||
|
||||
// Hack: Trying to open floater, search for a match,
|
||||
// and hide floater immediately if no match found,
|
||||
// instead of checking prior to opening
|
||||
//
|
||||
// Supress sounds in case floater won't be shown.
|
||||
// Todo: add some kind of shouldShow(short_code)
|
||||
U8 sound_flags = pHelperFloater->getSoundFlags();
|
||||
pHelperFloater->setSoundFlags(LLView::SILENT);
|
||||
pHelperFloater->openFloater(LLSD().with("hint", short_code));
|
||||
pHelperFloater->setSoundFlags(sound_flags);
|
||||
}
|
||||
|
||||
void LLEmojiHelper::hideHelper(const LLUICtrl* ctrl_p, bool strict)
|
||||
|
|
|
|||
|
|
@ -2274,7 +2274,7 @@ void LLFloater::drawConeToOwner(F32 &context_cone_opacity,
|
|||
LLRect local_rect = getLocalRect();
|
||||
|
||||
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
|
||||
LLGLEnable(GL_CULL_FACE);
|
||||
LLGLEnable cull_face(GL_CULL_FACE);
|
||||
gGL.begin(LLRender::TRIANGLE_STRIP);
|
||||
{
|
||||
gGL.color4f(0.f, 0.f, 0.f, contex_cone_in_alpha * context_cone_opacity);
|
||||
|
|
|
|||
|
|
@ -1510,6 +1510,7 @@ bool LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
|
|||
&& ( count > 0 && (hasVisibleChildren()) ))) && // show menu only if selected items are visible
|
||||
!hide_folder_menu)
|
||||
{
|
||||
LL_INFOS("Inventory") << "Opening inventory menu from path: " << getPathname() << LL_ENDL;
|
||||
if (mCallbackRegistrar)
|
||||
{
|
||||
mCallbackRegistrar->pushScope();
|
||||
|
|
|
|||
|
|
@ -1884,10 +1884,18 @@ void LLFolderViewFolder::updateHasFavorites(bool new_childs_value)
|
|||
void LLFolderViewFolder::onIdleUpdateFavorites(void* data)
|
||||
{
|
||||
LLFolderViewFolder* self = reinterpret_cast<LLFolderViewFolder*>(data);
|
||||
if (self->mFavoritesDirtyFlags == FAVORITE_CLEANUP)
|
||||
{
|
||||
// parent or child already processed the update, clean the callback
|
||||
self->mFavoritesDirtyFlags = 0;
|
||||
gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->mFavoritesDirtyFlags == 0)
|
||||
{
|
||||
// already processed either on previous run or by a different callback
|
||||
gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, self);
|
||||
llassert(false); // should not happen, everything that sets to 0 should clean callback
|
||||
gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, data);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1915,7 +1923,7 @@ void LLFolderViewFolder::onIdleUpdateFavorites(void* data)
|
|||
// Parent will remove onIdleUpdateFavorites later, don't remove now,
|
||||
// We are inside gIdleCallbacks. Removing 'self' callback is safe,
|
||||
// but removing 'parent' can invalidate following iterator
|
||||
parent->mFavoritesDirtyFlags = 0;
|
||||
parent->mFavoritesDirtyFlags = FAVORITE_CLEANUP;
|
||||
}
|
||||
parent = parent->getParentFolder();
|
||||
}
|
||||
|
|
@ -1981,7 +1989,7 @@ void LLFolderViewFolder::onIdleUpdateFavorites(void* data)
|
|||
// Parent will remove onIdleUpdateFavorites later, don't remove now,
|
||||
// We are inside gIdleCallbacks. Removing 'self' callback is safe,
|
||||
// but removing 'parent' can invalidate following iterator
|
||||
parent->mFavoritesDirtyFlags = 0;
|
||||
parent->mFavoritesDirtyFlags = FAVORITE_CLEANUP;
|
||||
}
|
||||
parent = parent->getParentFolder();
|
||||
}
|
||||
|
|
@ -1992,7 +2000,7 @@ void LLFolderViewFolder::onIdleUpdateFavorites(void* data)
|
|||
// Parent will remove onIdleUpdateFavorites later, don't remove now.
|
||||
// We are inside gIdleCallbacks. Removing 'self' callback is safe,
|
||||
// but removing 'parent' can invalidate following iterator
|
||||
parent->mFavoritesDirtyFlags = 0;
|
||||
parent->mFavoritesDirtyFlags = FAVORITE_CLEANUP;
|
||||
}
|
||||
parent = parent->getParentFolder();
|
||||
}
|
||||
|
|
@ -2106,10 +2114,14 @@ void LLFolderViewFolder::setOpen(bool openitem)
|
|||
{
|
||||
// navigateToFolder can destroy this view
|
||||
// delay it in case setOpen was called from click or key processing
|
||||
doOnIdleOneTime([this]()
|
||||
{
|
||||
getViewModelItem()->navigateToFolder();
|
||||
});
|
||||
LLPointer<LLFolderViewModelItem> view_model_item = mViewModelItem;
|
||||
doOnIdleOneTime([view_model_item]()
|
||||
{
|
||||
if (view_model_item.notNull())
|
||||
{
|
||||
view_model_item.get()->navigateToFolder();
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -421,6 +421,7 @@ private:
|
|||
|
||||
constexpr static S32 FAVORITE_ADDED = 1;
|
||||
constexpr static S32 FAVORITE_REMOVED = 2;
|
||||
constexpr static S32 FAVORITE_CLEANUP = 4;
|
||||
S32 mFavoritesDirtyFlags { 0 };
|
||||
public:
|
||||
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p)
|
|||
mURLClickSignal(NULL),
|
||||
mIsFriendSignal(NULL),
|
||||
mIsObjectBlockedSignal(NULL),
|
||||
mIsObjectReachableSignal(NULL),
|
||||
mMaxTextByteLength( p.max_text_length ),
|
||||
mFont(p.font),
|
||||
mFontShadow(p.font_shadow),
|
||||
|
|
@ -290,6 +291,7 @@ LLTextBase::~LLTextBase()
|
|||
delete mURLClickSignal;
|
||||
delete mIsFriendSignal;
|
||||
delete mIsObjectBlockedSignal;
|
||||
delete mIsObjectReachableSignal;
|
||||
}
|
||||
|
||||
void LLTextBase::initFromParams(const LLTextBase::Params& p)
|
||||
|
|
@ -1036,8 +1038,37 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
|
|||
{
|
||||
LLStyleSP emoji_style;
|
||||
LLEmojiDictionary* ed = LLEmojiDictionary::instanceExists() ? LLEmojiDictionary::getInstance() : NULL;
|
||||
LLTextSegment* segmentp = nullptr;
|
||||
segment_vec_t::iterator seg_iter;
|
||||
if (segments && segments->size() > 0)
|
||||
{
|
||||
seg_iter = segments->begin();
|
||||
segmentp = *seg_iter;
|
||||
}
|
||||
for (S32 text_kitty = 0, text_len = static_cast<S32>(wstr.size()); text_kitty < text_len; text_kitty++)
|
||||
{
|
||||
if (segmentp)
|
||||
{
|
||||
if (segmentp->getEnd() <= pos + text_kitty)
|
||||
{
|
||||
seg_iter++;
|
||||
if (seg_iter != segments->end())
|
||||
{
|
||||
segmentp = *seg_iter;
|
||||
}
|
||||
else
|
||||
{
|
||||
segmentp = nullptr;
|
||||
}
|
||||
}
|
||||
if (segmentp && !segmentp->getPermitsEmoji())
|
||||
{
|
||||
// Some segments, like LLInlineViewSegment do not permit splitting
|
||||
// and should not be interrupted by emoji segments
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
llwchar code = wstr[text_kitty];
|
||||
bool isEmoji = ed ? ed->isEmoji(code) : LLStringOps::isEmoji(code);
|
||||
if (isEmoji)
|
||||
|
|
@ -1446,6 +1477,8 @@ void LLTextBase::reshape(S32 width, S32 height, bool called_from_parent)
|
|||
// up-to-date mVisibleTextRect
|
||||
updateRects();
|
||||
|
||||
// Todo: This might be wrong. updateRects already sets needsReflow conditionaly.
|
||||
// Reflow is expensive and doing it at any twith can be too much.
|
||||
needsReflow();
|
||||
}
|
||||
}
|
||||
|
|
@ -2281,6 +2314,15 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
|
|||
unblockButton->setVisible(is_blocked);
|
||||
}
|
||||
}
|
||||
|
||||
if (mIsObjectReachableSignal)
|
||||
{
|
||||
bool is_reachable = *(*mIsObjectReachableSignal)(LLUUID(LLUrlAction::getObjectId(url)));
|
||||
if (LLView* zoom_btn = menu->getChild<LLView>("zoom_in"))
|
||||
{
|
||||
zoom_btn->setEnabled(is_reachable);
|
||||
}
|
||||
}
|
||||
menu->show(x, y);
|
||||
LLMenuGL::showPopup(this, menu, x, y);
|
||||
}
|
||||
|
|
@ -3387,6 +3429,15 @@ boost::signals2::connection LLTextBase::setIsObjectBlockedCallback(const is_bloc
|
|||
return mIsObjectBlockedSignal->connect(cb);
|
||||
}
|
||||
|
||||
boost::signals2::connection LLTextBase::setIsObjectReachableCallback(const is_obj_reachable_signal_t::slot_type& cb)
|
||||
{
|
||||
if (!mIsObjectReachableSignal)
|
||||
{
|
||||
mIsObjectReachableSignal = new is_obj_reachable_signal_t();
|
||||
}
|
||||
return mIsObjectReachableSignal->connect(cb);
|
||||
}
|
||||
|
||||
//
|
||||
// LLTextSegment
|
||||
//
|
||||
|
|
@ -3426,6 +3477,7 @@ S32 LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offs
|
|||
void LLTextSegment::updateLayout(const LLTextBase& editor) {}
|
||||
F32 LLTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect) { return draw_rect.mLeft; }
|
||||
bool LLTextSegment::canEdit() const { return false; }
|
||||
bool LLTextSegment::getPermitsEmoji() const { return true; }
|
||||
void LLTextSegment::unlinkFromDocument(LLTextBase*) {}
|
||||
void LLTextSegment::linkToDocument(LLTextBase*) {}
|
||||
const LLUIColor& LLTextSegment::getColor() const { static const LLUIColor white = LLUIColorTable::instance().getColor("White", LLColor4::white); return white; }
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ public:
|
|||
virtual void updateLayout(const class LLTextBase& editor);
|
||||
virtual F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
|
||||
virtual bool canEdit() const;
|
||||
virtual bool getPermitsEmoji() const;
|
||||
virtual void unlinkFromDocument(class LLTextBase* editor);
|
||||
virtual void linkToDocument(class LLTextBase* editor);
|
||||
|
||||
|
|
@ -255,6 +256,7 @@ public:
|
|||
/*virtual*/ void updateLayout(const class LLTextBase& editor);
|
||||
/*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRectf& draw_rect);
|
||||
/*virtual*/ bool canEdit() const { return false; }
|
||||
/*virtual*/ bool getPermitsEmoji() const { return false; }
|
||||
/*virtual*/ void unlinkFromDocument(class LLTextBase* editor);
|
||||
/*virtual*/ void linkToDocument(class LLTextBase* editor);
|
||||
|
||||
|
|
@ -325,6 +327,7 @@ public:
|
|||
|
||||
typedef boost::signals2::signal<bool (const LLUUID& user_id)> is_friend_signal_t;
|
||||
typedef boost::signals2::signal<bool (const LLUUID& blocked_id, const std::string from)> is_blocked_signal_t;
|
||||
typedef boost::signals2::signal<bool (const LLUUID& obj_id)> is_obj_reachable_signal_t;
|
||||
|
||||
struct LineSpacingParams : public LLInitParam::ChoiceBlock<LineSpacingParams>
|
||||
{
|
||||
|
|
@ -535,6 +538,7 @@ public:
|
|||
boost::signals2::connection setURLClickedCallback(const commit_signal_t::slot_type& cb);
|
||||
boost::signals2::connection setIsFriendCallback(const is_friend_signal_t::slot_type& cb);
|
||||
boost::signals2::connection setIsObjectBlockedCallback(const is_blocked_signal_t::slot_type& cb);
|
||||
boost::signals2::connection setIsObjectReachableCallback(const is_obj_reachable_signal_t::slot_type& cb);
|
||||
|
||||
void setWordWrap(bool wrap);
|
||||
LLScrollContainer* getScrollContainer() const { return mScroller; }
|
||||
|
|
@ -783,6 +787,7 @@ protected:
|
|||
// Used to check if user with given ID is avatar's friend
|
||||
is_friend_signal_t* mIsFriendSignal;
|
||||
is_blocked_signal_t* mIsObjectBlockedSignal;
|
||||
is_obj_reachable_signal_t* mIsObjectReachableSignal;
|
||||
|
||||
LLUIString mLabel; // text label that is visible when no user text provided
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1054,7 +1054,7 @@ bool LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
|
|||
// if drop is set, it's time to call the callback to get the operation done
|
||||
if (handled && drop)
|
||||
{
|
||||
handled = mHandleDropCallback(cargo_data, x, y, this);
|
||||
handled = mHandleDropCallback(cargo_data, cargo_type, x, y, this);
|
||||
}
|
||||
|
||||
// We accept only single tool drop on toolbars
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class LLIconCtrl;
|
|||
|
||||
typedef boost::function<void (S32 x, S32 y, LLToolBarButton* button)> tool_startdrag_callback_t;
|
||||
typedef boost::function<bool (S32 x, S32 y, const LLUUID& uuid, LLAssetType::EType type)> tool_handledrag_callback_t;
|
||||
typedef boost::function<bool (void* data, S32 x, S32 y, LLToolBar* toolbar)> tool_handledrop_callback_t;
|
||||
typedef boost::function<bool (void* data, EDragAndDropType cargo_type, S32 x, S32 y, LLToolBar* toolbar)> tool_handledrop_callback_t;
|
||||
|
||||
class LLToolBarButton : public LLButton
|
||||
{
|
||||
|
|
|
|||
|
|
@ -34,8 +34,6 @@ LLViewerEventRecorder::LLViewerEventRecorder() {
|
|||
logEvents = false;
|
||||
// Remove any previous event log file
|
||||
std::string old_log_ui_events_to_llsd_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.old");
|
||||
LLFile::remove(old_log_ui_events_to_llsd_file, ENOENT);
|
||||
|
||||
|
||||
mLogFilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLife_Events_log.llsd");
|
||||
LLFile::rename(mLogFilename, old_log_ui_events_to_llsd_file, ENOENT);
|
||||
|
|
|
|||
|
|
@ -374,8 +374,6 @@ void LLWebRTCImpl::terminate()
|
|||
|
||||
mSignalingThread->BlockingCall([this]() { mPeerConnectionFactory = nullptr; });
|
||||
|
||||
mPeerConnections.clear();
|
||||
|
||||
mWorkerThread->BlockingCall(
|
||||
[this]()
|
||||
{
|
||||
|
|
@ -386,6 +384,14 @@ void LLWebRTCImpl::terminate()
|
|||
mDeviceModule = nullptr;
|
||||
mTaskQueueFactory = nullptr;
|
||||
});
|
||||
|
||||
// In case peer connections still somehow have jobs in workers,
|
||||
// only clear connections up after clearing workers.
|
||||
mNetworkThread = nullptr;
|
||||
mWorkerThread = nullptr;
|
||||
mSignalingThread = nullptr;
|
||||
|
||||
mPeerConnections.clear();
|
||||
webrtc::LogMessage::RemoveLogToStream(mLogSink);
|
||||
}
|
||||
|
||||
|
|
@ -566,14 +572,20 @@ void LLWebRTCImpl::workerDeployDevices()
|
|||
void LLWebRTCImpl::setCaptureDevice(const std::string &id)
|
||||
{
|
||||
|
||||
mRecordingDevice = id;
|
||||
deployDevices();
|
||||
if (mRecordingDevice != id)
|
||||
{
|
||||
mRecordingDevice = id;
|
||||
deployDevices();
|
||||
}
|
||||
}
|
||||
|
||||
void LLWebRTCImpl::setRenderDevice(const std::string &id)
|
||||
{
|
||||
mPlayoutDevice = id;
|
||||
deployDevices();
|
||||
if (mPlayoutDevice != id)
|
||||
{
|
||||
mPlayoutDevice = id;
|
||||
deployDevices();
|
||||
}
|
||||
}
|
||||
|
||||
// updateDevices needs to happen on the worker thread.
|
||||
|
|
@ -635,6 +647,13 @@ void LLWebRTCImpl::OnDevicesUpdated()
|
|||
void LLWebRTCImpl::setTuningMode(bool enable)
|
||||
{
|
||||
mTuningMode = enable;
|
||||
if (!mTuningMode
|
||||
&& !mMute
|
||||
&& mPeerCustomProcessor
|
||||
&& mPeerCustomProcessor->getGain() != mGain)
|
||||
{
|
||||
mPeerCustomProcessor->setGain(mGain);
|
||||
}
|
||||
mWorkerThread->PostTask(
|
||||
[this]
|
||||
{
|
||||
|
|
@ -766,6 +785,7 @@ void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnectionInterface* peer_conn
|
|||
std::find(mPeerConnections.begin(), mPeerConnections.end(), peer_connection);
|
||||
if (it != mPeerConnections.end())
|
||||
{
|
||||
// Todo: make sure conection had no jobs in workers
|
||||
mPeerConnections.erase(it);
|
||||
if (mPeerConnections.empty())
|
||||
{
|
||||
|
|
@ -785,7 +805,8 @@ LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl() :
|
|||
mWebRTCImpl(nullptr),
|
||||
mPeerConnection(nullptr),
|
||||
mMute(MUTE_INITIAL),
|
||||
mAnswerReceived(false)
|
||||
mAnswerReceived(false),
|
||||
mPendingJobs(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -793,6 +814,10 @@ LLWebRTCPeerConnectionImpl::~LLWebRTCPeerConnectionImpl()
|
|||
{
|
||||
mSignalingObserverList.clear();
|
||||
mDataObserverList.clear();
|
||||
if (mPendingJobs > 0)
|
||||
{
|
||||
RTC_LOG(LS_ERROR) << __FUNCTION__ << "Destroying a connection that has " << std::to_string(mPendingJobs) << " unfinished jobs that might cause workers to crash";
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -804,8 +829,10 @@ void LLWebRTCPeerConnectionImpl::init(LLWebRTCImpl * webrtc_impl)
|
|||
mWebRTCImpl = webrtc_impl;
|
||||
mPeerConnectionFactory = mWebRTCImpl->getPeerConnectionFactory();
|
||||
}
|
||||
|
||||
void LLWebRTCPeerConnectionImpl::terminate()
|
||||
{
|
||||
mPendingJobs++;
|
||||
mWebRTCImpl->PostSignalingTask(
|
||||
[this]()
|
||||
{
|
||||
|
|
@ -848,7 +875,9 @@ void LLWebRTCPeerConnectionImpl::terminate()
|
|||
observer->OnPeerConnectionClosed();
|
||||
}
|
||||
}
|
||||
mPendingJobs--;
|
||||
});
|
||||
mPeerConnectionFactory.release();
|
||||
}
|
||||
|
||||
void LLWebRTCPeerConnectionImpl::setSignalingObserver(LLWebRTCSignalingObserver *observer) { mSignalingObserverList.emplace_back(observer); }
|
||||
|
|
@ -869,6 +898,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
|
|||
RTC_DCHECK(!mPeerConnection);
|
||||
mAnswerReceived = false;
|
||||
|
||||
mPendingJobs++;
|
||||
mWebRTCImpl->PostSignalingTask(
|
||||
[this,options]()
|
||||
{
|
||||
|
|
@ -890,6 +920,13 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
|
|||
config.set_max_port(60100);
|
||||
|
||||
webrtc::PeerConnectionDependencies pc_dependencies(this);
|
||||
if (mPeerConnectionFactory == nullptr)
|
||||
{
|
||||
RTC_LOG(LS_ERROR) << __FUNCTION__ << "Error creating peer connection, factory doesn't exist";
|
||||
// Too early?
|
||||
mPendingJobs--;
|
||||
return;
|
||||
}
|
||||
auto error_or_peer_connection = mPeerConnectionFactory->CreatePeerConnectionOrError(config, std::move(pc_dependencies));
|
||||
if (error_or_peer_connection.ok())
|
||||
{
|
||||
|
|
@ -902,6 +939,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
|
|||
{
|
||||
observer->OnRenegotiationNeeded();
|
||||
}
|
||||
mPendingJobs--;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -964,6 +1002,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
|
|||
|
||||
webrtc::PeerConnectionInterface::RTCOfferAnswerOptions offerOptions;
|
||||
mPeerConnection->CreateOffer(this, offerOptions);
|
||||
mPendingJobs--;
|
||||
});
|
||||
|
||||
return true;
|
||||
|
|
@ -1006,6 +1045,7 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp)
|
|||
{
|
||||
RTC_LOG(LS_INFO) << __FUNCTION__ << " Remote SDP: " << sdp;
|
||||
|
||||
mPendingJobs++;
|
||||
mWebRTCImpl->PostSignalingTask(
|
||||
[this, sdp]()
|
||||
{
|
||||
|
|
@ -1015,6 +1055,7 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp)
|
|||
mPeerConnection->SetRemoteDescription(webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, sdp),
|
||||
webrtc::scoped_refptr<webrtc::SetRemoteDescriptionObserverInterface>(this));
|
||||
}
|
||||
mPendingJobs--;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1037,6 +1078,7 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute)
|
|||
mMute = new_state;
|
||||
|
||||
|
||||
mPendingJobs++;
|
||||
mWebRTCImpl->PostSignalingTask(
|
||||
[this, force_reset, enable]()
|
||||
{
|
||||
|
|
@ -1060,6 +1102,7 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute)
|
|||
track->set_enabled(enable);
|
||||
}
|
||||
}
|
||||
mPendingJobs--;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1081,6 +1124,7 @@ void LLWebRTCPeerConnectionImpl::resetMute()
|
|||
|
||||
void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume)
|
||||
{
|
||||
mPendingJobs++;
|
||||
mWebRTCImpl->PostSignalingTask(
|
||||
[this, volume]()
|
||||
{
|
||||
|
|
@ -1099,11 +1143,13 @@ void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume)
|
|||
}
|
||||
}
|
||||
}
|
||||
mPendingJobs--;
|
||||
});
|
||||
}
|
||||
|
||||
void LLWebRTCPeerConnectionImpl::setSendVolume(float volume)
|
||||
{
|
||||
mPendingJobs++;
|
||||
mWebRTCImpl->PostSignalingTask(
|
||||
[this, volume]()
|
||||
{
|
||||
|
|
@ -1114,6 +1160,7 @@ void LLWebRTCPeerConnectionImpl::setSendVolume(float volume)
|
|||
track->GetSource()->SetVolume(volume*5.0);
|
||||
}
|
||||
}
|
||||
mPendingJobs--;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1190,11 +1237,13 @@ void LLWebRTCPeerConnectionImpl::OnConnectionChange(webrtc::PeerConnectionInterf
|
|||
{
|
||||
case webrtc::PeerConnectionInterface::PeerConnectionState::kConnected:
|
||||
{
|
||||
mPendingJobs++;
|
||||
mWebRTCImpl->PostWorkerTask([this]() {
|
||||
for (auto &observer : mSignalingObserverList)
|
||||
{
|
||||
observer->OnAudioEstablished(this);
|
||||
}
|
||||
mPendingJobs--;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
|
@ -1452,11 +1501,13 @@ void LLWebRTCPeerConnectionImpl::sendData(const std::string& data, bool binary)
|
|||
{
|
||||
webrtc::CopyOnWriteBuffer cowBuffer(data.data(), data.length());
|
||||
webrtc::DataBuffer buffer(cowBuffer, binary);
|
||||
mPendingJobs++;
|
||||
mWebRTCImpl->PostNetworkTask([this, buffer]() {
|
||||
if (mDataChannel)
|
||||
{
|
||||
mDataChannel->Send(buffer);
|
||||
}
|
||||
mPendingJobs--;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -422,6 +422,9 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceO
|
|||
~LLWebRTCImpl()
|
||||
{
|
||||
delete mLogSink;
|
||||
|
||||
// Explicit cleanup for the sake of debugging and crash stacks
|
||||
mPeerCustomProcessor = nullptr;
|
||||
}
|
||||
|
||||
void init();
|
||||
|
|
@ -669,6 +672,8 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface,
|
|||
// data
|
||||
std::vector<LLWebRTCDataObserver *> mDataObserverList;
|
||||
webrtc::scoped_refptr<webrtc::DataChannelInterface> mDataChannel;
|
||||
|
||||
std::atomic<int> mPendingJobs;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
- (unsigned long)getVramSize
|
||||
{
|
||||
CGLRendererInfoObj info = 0;
|
||||
GLint vram_megabytes = 0;
|
||||
GLint vram_megabytes = 0;
|
||||
int num_renderers = 0;
|
||||
CGLError the_err = CGLQueryRendererInfo (CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), &info, &num_renderers);
|
||||
if(0 == the_err)
|
||||
|
|
@ -132,29 +132,29 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
vram_megabytes = 256;
|
||||
}
|
||||
|
||||
return (unsigned long)vram_megabytes; // return value is in megabytes.
|
||||
return (unsigned long)vram_megabytes; // return value is in megabytes.
|
||||
}
|
||||
|
||||
- (void)viewDidMoveToWindow
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(windowResized:) name:NSWindowDidResizeNotification
|
||||
object:[self window]];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(windowResized:) name:NSWindowDidResizeNotification
|
||||
object:[self window]];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(windowWillMiniaturize:) name:NSWindowWillMiniaturizeNotification
|
||||
object:[self window]];
|
||||
selector:@selector(windowWillMiniaturize:) name:NSWindowWillMiniaturizeNotification
|
||||
object:[self window]];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification
|
||||
object:[self window]];
|
||||
selector:@selector(windowDidDeminiaturize:) name:NSWindowDidDeminiaturizeNotification
|
||||
object:[self window]];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification
|
||||
object:[self window]];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification
|
||||
object:[self window]];
|
||||
selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification
|
||||
object:[self window]];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(windowDidChangeScreen:) name:NSWindowDidChangeScreenNotification
|
||||
object:[self window]];
|
||||
|
||||
|
||||
NSRect wnd_rect = [[self window] frame];
|
||||
|
|
@ -188,28 +188,28 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
|
||||
-(void)windowDidChangeScreen:(NSNotification *)notification;
|
||||
{
|
||||
callWindowDidChangeScreen();
|
||||
callWindowDidChangeScreen();
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[super dealloc];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
return [self initWithFrame:[self bounds] withSamples:2 andVsync:TRUE];
|
||||
return [self initWithFrame:[self bounds] withSamples:2 andVsync:TRUE];
|
||||
}
|
||||
|
||||
- (id) initWithSamples:(NSUInteger)samples
|
||||
{
|
||||
return [self initWithFrame:[self bounds] withSamples:samples andVsync:TRUE];
|
||||
return [self initWithFrame:[self bounds] withSamples:samples andVsync:TRUE];
|
||||
}
|
||||
|
||||
- (id) initWithSamples:(NSUInteger)samples andVsync:(BOOL)vsync
|
||||
{
|
||||
return [self initWithFrame:[self bounds] withSamples:samples andVsync:vsync];
|
||||
return [self initWithFrame:[self bounds] withSamples:samples andVsync:vsync];
|
||||
}
|
||||
|
||||
#if LL_DARWIN
|
||||
|
|
@ -221,90 +221,90 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
- (id) initWithFrame:(NSRect)frame withSamples:(NSUInteger)samples andVsync:(BOOL)vsync
|
||||
{
|
||||
[self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeURL]];
|
||||
[self initWithFrame:frame];
|
||||
[self initWithFrame:frame];
|
||||
|
||||
// Initialize with a default "safe" pixel format that will work with versions dating back to OS X 10.6.
|
||||
// Any specialized pixel formats, i.e. a core profile pixel format, should be initialized through rebuildContextWithFormat.
|
||||
// 10.7 and 10.8 don't really care if we're defining a profile or not. If we don't explicitly request a core or legacy profile, it'll always assume a legacy profile (for compatibility reasons).
|
||||
NSOpenGLPixelFormatAttribute attrs[] = {
|
||||
// Initialize with a default "safe" pixel format that will work with versions dating back to OS X 10.6.
|
||||
// Any specialized pixel formats, i.e. a core profile pixel format, should be initialized through rebuildContextWithFormat.
|
||||
// 10.7 and 10.8 don't really care if we're defining a profile or not. If we don't explicitly request a core or legacy profile, it'll always assume a legacy profile (for compatibility reasons).
|
||||
NSOpenGLPixelFormatAttribute attrs[] = {
|
||||
NSOpenGLPFANoRecovery,
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
NSOpenGLPFAClosestPolicy,
|
||||
NSOpenGLPFAAccelerated,
|
||||
NSOpenGLPFASampleBuffers, 0,
|
||||
NSOpenGLPFASamples, 0,
|
||||
NSOpenGLPFAStencilSize, 8,
|
||||
NSOpenGLPFADepthSize, 24,
|
||||
NSOpenGLPFAAlphaSize, 8,
|
||||
NSOpenGLPFAColorSize, 24,
|
||||
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core,
|
||||
0
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
NSOpenGLPFAClosestPolicy,
|
||||
NSOpenGLPFAAccelerated,
|
||||
NSOpenGLPFASampleBuffers, 0,
|
||||
NSOpenGLPFASamples, 0,
|
||||
NSOpenGLPFAStencilSize, 8,
|
||||
NSOpenGLPFADepthSize, 24,
|
||||
NSOpenGLPFAAlphaSize, 8,
|
||||
NSOpenGLPFAColorSize, 24,
|
||||
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core,
|
||||
0
|
||||
};
|
||||
|
||||
NSOpenGLPixelFormat *pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease];
|
||||
NSOpenGLPixelFormat *pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease];
|
||||
|
||||
if (pixelFormat == nil)
|
||||
{
|
||||
NSLog(@"Failed to create pixel format!", nil);
|
||||
return nil;
|
||||
}
|
||||
if (pixelFormat == nil)
|
||||
{
|
||||
NSLog(@"Failed to create pixel format!", nil);
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSOpenGLContext *glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
|
||||
NSOpenGLContext *glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
|
||||
|
||||
if (glContext == nil)
|
||||
{
|
||||
NSLog(@"Failed to create OpenGL context!", nil);
|
||||
return nil;
|
||||
}
|
||||
if (glContext == nil)
|
||||
{
|
||||
NSLog(@"Failed to create OpenGL context!", nil);
|
||||
return nil;
|
||||
}
|
||||
|
||||
[self setPixelFormat:pixelFormat];
|
||||
[self setPixelFormat:pixelFormat];
|
||||
|
||||
//for retina support
|
||||
[self setWantsBestResolutionOpenGLSurface:gHiDPISupport];
|
||||
//for retina support
|
||||
[self setWantsBestResolutionOpenGLSurface:gHiDPISupport];
|
||||
|
||||
[self setOpenGLContext:glContext];
|
||||
[self setOpenGLContext:glContext];
|
||||
|
||||
[glContext setView:self];
|
||||
[glContext setView:self];
|
||||
|
||||
[glContext makeCurrentContext];
|
||||
[glContext makeCurrentContext];
|
||||
|
||||
if (vsync)
|
||||
{
|
||||
GLint value = 1;
|
||||
if (vsync)
|
||||
{
|
||||
GLint value = 1;
|
||||
[glContext setValues:&value forParameter:NSOpenGLContextParameterSwapInterval];
|
||||
} else {
|
||||
// supress this error after move to Xcode 7:
|
||||
// error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull]
|
||||
// Tried using ObjC 'nonnull' keyword as per SO article but didn't build
|
||||
GLint swapInterval=0;
|
||||
} else {
|
||||
// supress this error after move to Xcode 7:
|
||||
// error: null passed to a callee that requires a non-null argument [-Werror,-Wnonnull]
|
||||
// Tried using ObjC 'nonnull' keyword as per SO article but didn't build
|
||||
GLint swapInterval=0;
|
||||
[glContext setValues:&swapInterval forParameter:NSOpenGLContextParameterSwapInterval];
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL) rebuildContext
|
||||
{
|
||||
return [self rebuildContextWithFormat:[self pixelFormat]];
|
||||
return [self rebuildContextWithFormat:[self pixelFormat]];
|
||||
}
|
||||
|
||||
- (BOOL) rebuildContextWithFormat:(NSOpenGLPixelFormat *)format
|
||||
{
|
||||
NSOpenGLContext *ctx = [self openGLContext];
|
||||
NSOpenGLContext *ctx = [self openGLContext];
|
||||
|
||||
[ctx clearDrawable];
|
||||
[ctx initWithFormat:format shareContext:nil];
|
||||
[ctx clearDrawable];
|
||||
[ctx initWithFormat:format shareContext:nil];
|
||||
|
||||
if (ctx == nil)
|
||||
{
|
||||
NSLog(@"Failed to create OpenGL context!", nil);
|
||||
return false;
|
||||
}
|
||||
if (ctx == nil)
|
||||
{
|
||||
NSLog(@"Failed to create OpenGL context!", nil);
|
||||
return false;
|
||||
}
|
||||
|
||||
[self setOpenGLContext:ctx];
|
||||
[ctx setView:self];
|
||||
[ctx makeCurrentContext];
|
||||
return true;
|
||||
[self setOpenGLContext:ctx];
|
||||
[ctx setView:self];
|
||||
[ctx makeCurrentContext];
|
||||
return true;
|
||||
}
|
||||
|
||||
#if LL_DARWIN
|
||||
|
|
@ -313,14 +313,14 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
|
||||
- (CGLContextObj)getCGLContextObj
|
||||
{
|
||||
NSOpenGLContext *ctx = [self openGLContext];
|
||||
return (CGLContextObj)[ctx CGLContextObj];
|
||||
NSOpenGLContext *ctx = [self openGLContext];
|
||||
return (CGLContextObj)[ctx CGLContextObj];
|
||||
}
|
||||
|
||||
- (CGLPixelFormatObj*)getCGLPixelFormatObj
|
||||
{
|
||||
NSOpenGLPixelFormat *fmt = [self pixelFormat];
|
||||
return (CGLPixelFormatObj*)[fmt CGLPixelFormatObj];
|
||||
NSOpenGLPixelFormat *fmt = [self pixelFormat];
|
||||
return (CGLPixelFormatObj*)[fmt CGLPixelFormatObj];
|
||||
}
|
||||
|
||||
// Various events can be intercepted by our view, thus not reaching our window.
|
||||
|
|
@ -365,29 +365,29 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
|
||||
- (void) rightMouseDown:(NSEvent *)theEvent
|
||||
{
|
||||
callRightMouseDown(mMousePos, [theEvent modifierFlags]);
|
||||
callRightMouseDown(mMousePos, [theEvent modifierFlags]);
|
||||
}
|
||||
|
||||
- (void) rightMouseUp:(NSEvent *)theEvent
|
||||
{
|
||||
callRightMouseUp(mMousePos, [theEvent modifierFlags]);
|
||||
callRightMouseUp(mMousePos, [theEvent modifierFlags]);
|
||||
}
|
||||
|
||||
- (void)mouseMoved:(NSEvent *)theEvent
|
||||
{
|
||||
NSPoint dev_delta = [self convertPointToBacking:NSMakePoint([theEvent deltaX], [theEvent deltaY])];
|
||||
|
||||
float mouseDeltas[] = {
|
||||
float(dev_delta.x),
|
||||
float(dev_delta.y)
|
||||
};
|
||||
float mouseDeltas[] = {
|
||||
float(dev_delta.x),
|
||||
float(dev_delta.y)
|
||||
};
|
||||
|
||||
callDeltaUpdate(mouseDeltas, 0);
|
||||
callDeltaUpdate(mouseDeltas, 0);
|
||||
|
||||
NSPoint mPoint = [self convertPointToBacking:[theEvent locationInWindow]];
|
||||
mMousePos[0] = mPoint.x;
|
||||
mMousePos[1] = mPoint.y;
|
||||
callMouseMoved(mMousePos, 0);
|
||||
mMousePos[0] = mPoint.x;
|
||||
mMousePos[1] = mPoint.y;
|
||||
callMouseMoved(mMousePos, 0);
|
||||
}
|
||||
|
||||
// NSWindow doesn't trigger mouseMoved when the mouse is being clicked and dragged.
|
||||
|
|
@ -395,23 +395,23 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
|
||||
- (void) mouseDragged:(NSEvent *)theEvent
|
||||
{
|
||||
// Trust the deltas supplied by NSEvent.
|
||||
// The old CoreGraphics APIs we previously relied on are now flagged as obsolete.
|
||||
// NSEvent isn't obsolete, and provides us with the correct deltas.
|
||||
// Trust the deltas supplied by NSEvent.
|
||||
// The old CoreGraphics APIs we previously relied on are now flagged as obsolete.
|
||||
// NSEvent isn't obsolete, and provides us with the correct deltas.
|
||||
|
||||
NSPoint dev_delta = [self convertPointToBacking:NSMakePoint([theEvent deltaX], [theEvent deltaY])];
|
||||
|
||||
float mouseDeltas[] = {
|
||||
float(dev_delta.x),
|
||||
float(dev_delta.y)
|
||||
};
|
||||
float mouseDeltas[] = {
|
||||
float(dev_delta.x),
|
||||
float(dev_delta.y)
|
||||
};
|
||||
|
||||
callDeltaUpdate(mouseDeltas, 0);
|
||||
callDeltaUpdate(mouseDeltas, 0);
|
||||
|
||||
NSPoint mPoint = [self convertPointToBacking:[theEvent locationInWindow]];
|
||||
mMousePos[0] = mPoint.x;
|
||||
mMousePos[1] = mPoint.y;
|
||||
callMouseDragged(mMousePos, 0);
|
||||
NSPoint mPoint = [self convertPointToBacking:[theEvent locationInWindow]];
|
||||
mMousePos[0] = mPoint.x;
|
||||
mMousePos[1] = mPoint.y;
|
||||
callMouseDragged(mMousePos, 0);
|
||||
}
|
||||
|
||||
- (void) otherMouseDown:(NSEvent *)theEvent
|
||||
|
|
@ -426,29 +426,29 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
|
||||
- (void) rightMouseDragged:(NSEvent *)theEvent
|
||||
{
|
||||
[self mouseDragged:theEvent];
|
||||
[self mouseDragged:theEvent];
|
||||
}
|
||||
|
||||
- (void) otherMouseDragged:(NSEvent *)theEvent
|
||||
{
|
||||
[self mouseDragged:theEvent];
|
||||
[self mouseDragged:theEvent];
|
||||
}
|
||||
|
||||
- (void) scrollWheel:(NSEvent *)theEvent
|
||||
{
|
||||
callScrollMoved(-[theEvent deltaX], -[theEvent deltaY]);
|
||||
callScrollMoved(-[theEvent deltaX], -[theEvent deltaY]);
|
||||
}
|
||||
|
||||
- (void) mouseExited:(NSEvent *)theEvent
|
||||
{
|
||||
callMouseExit();
|
||||
callMouseExit();
|
||||
}
|
||||
|
||||
- (void) keyUp:(NSEvent *)theEvent
|
||||
{
|
||||
NativeKeyEventData eventData = extractKeyDataFromKeyEvent(theEvent);
|
||||
eventData.mKeyEvent = NativeKeyEventData::KEYUP;
|
||||
callKeyUp(&eventData, [theEvent keyCode], [theEvent modifierFlags]);
|
||||
callKeyUp(&eventData, [theEvent keyCode], [theEvent modifierFlags]);
|
||||
}
|
||||
|
||||
- (void) keyDown:(NSEvent *)theEvent
|
||||
|
|
@ -462,7 +462,7 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
// Because flagsChange event handler misses event when other window is activated,
|
||||
// e.g. OS Window for upload something or Input Window...
|
||||
// mModifiers instance variable is for insertText: or insertText:replacementRange: (by Pell Smit)
|
||||
mModifiers = [theEvent modifierFlags];
|
||||
mModifiers = [theEvent modifierFlags];
|
||||
NSString *str_no_modifiers = [theEvent charactersIgnoringModifiers];
|
||||
unichar ch = 0;
|
||||
if (str_no_modifiers.length)
|
||||
|
|
@ -490,8 +490,8 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
{
|
||||
NativeKeyEventData eventData = extractKeyDataFromModifierEvent(theEvent);
|
||||
|
||||
mModifiers = [theEvent modifierFlags];
|
||||
callModifier([theEvent modifierFlags]);
|
||||
mModifiers = [theEvent modifierFlags];
|
||||
callModifier([theEvent modifierFlags]);
|
||||
|
||||
NSInteger mask = 0;
|
||||
switch([theEvent keyCode])
|
||||
|
|
@ -532,69 +532,69 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
|
||||
- (BOOL) acceptsFirstResponder
|
||||
{
|
||||
return YES;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSDragOperation) draggingEntered:(id<NSDraggingInfo>)sender
|
||||
{
|
||||
NSPasteboard *pboard;
|
||||
NSPasteboard *pboard;
|
||||
NSDragOperation sourceDragMask;
|
||||
|
||||
sourceDragMask = [sender draggingSourceOperationMask];
|
||||
sourceDragMask = [sender draggingSourceOperationMask];
|
||||
|
||||
pboard = [sender draggingPasteboard];
|
||||
pboard = [sender draggingPasteboard];
|
||||
|
||||
if ([[pboard types] containsObject:NSPasteboardTypeURL])
|
||||
{
|
||||
if (sourceDragMask & NSDragOperationLink) {
|
||||
NSURL *fileUrl = [[pboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:[NSDictionary dictionary]] objectAtIndex:0];
|
||||
mLastDraggedUrl = [[fileUrl absoluteString] UTF8String];
|
||||
{
|
||||
if (sourceDragMask & NSDragOperationLink) {
|
||||
NSURL *fileUrl = [[pboard readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]] options:[NSDictionary dictionary]] objectAtIndex:0];
|
||||
mLastDraggedUrl = [[fileUrl absoluteString] UTF8String];
|
||||
return NSDragOperationLink;
|
||||
}
|
||||
}
|
||||
return NSDragOperationNone;
|
||||
}
|
||||
return NSDragOperationNone;
|
||||
}
|
||||
|
||||
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
|
||||
{
|
||||
callHandleDragUpdated(mLastDraggedUrl);
|
||||
callHandleDragUpdated(mLastDraggedUrl);
|
||||
|
||||
return NSDragOperationLink;
|
||||
return NSDragOperationLink;
|
||||
}
|
||||
|
||||
- (void) draggingExited:(id<NSDraggingInfo>)sender
|
||||
{
|
||||
callHandleDragExited(mLastDraggedUrl);
|
||||
callHandleDragExited(mLastDraggedUrl);
|
||||
}
|
||||
|
||||
- (BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender
|
||||
{
|
||||
return YES;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL) performDragOperation:(id<NSDraggingInfo>)sender
|
||||
{
|
||||
callHandleDragDropped(mLastDraggedUrl);
|
||||
return true;
|
||||
callHandleDragDropped(mLastDraggedUrl);
|
||||
return true;
|
||||
}
|
||||
|
||||
- (BOOL)hasMarkedText
|
||||
{
|
||||
return mHasMarkedText;
|
||||
return mHasMarkedText;
|
||||
}
|
||||
|
||||
- (NSRange)markedRange
|
||||
{
|
||||
int range[2];
|
||||
getPreeditMarkedRange(&range[0], &range[1]);
|
||||
return NSMakeRange(range[0], range[1]);
|
||||
int range[2];
|
||||
getPreeditMarkedRange(&range[0], &range[1]);
|
||||
return NSMakeRange(range[0], range[1]);
|
||||
}
|
||||
|
||||
- (NSRange)selectedRange
|
||||
{
|
||||
int range[2];
|
||||
getPreeditSelectionRange(&range[0], &range[1]);
|
||||
return NSMakeRange(range[0], range[1]);
|
||||
int range[2];
|
||||
getPreeditSelectionRange(&range[0], &range[1]);
|
||||
return NSMakeRange(range[0], range[1]);
|
||||
}
|
||||
|
||||
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
|
||||
|
|
@ -680,21 +680,21 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
|
||||
- (void)unmarkText
|
||||
{
|
||||
[[self inputContext] discardMarkedText];
|
||||
resetPreedit();
|
||||
mHasMarkedText = FALSE;
|
||||
[[self inputContext] discardMarkedText];
|
||||
resetPreedit();
|
||||
mHasMarkedText = FALSE;
|
||||
}
|
||||
|
||||
// We don't support attributed strings.
|
||||
- (NSArray *)validAttributesForMarkedText
|
||||
{
|
||||
return [NSArray array];
|
||||
return [NSArray array];
|
||||
}
|
||||
|
||||
// See above.
|
||||
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
|
||||
{
|
||||
return nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)insertText:(id)insertString
|
||||
|
|
@ -707,9 +707,9 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
|
||||
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
|
||||
{
|
||||
// SL-19801 Special workaround for system emoji picker
|
||||
if ([aString length] == 2)
|
||||
{
|
||||
// SL-19801 Special workaround for system emoji picker
|
||||
if ([aString length] == 2)
|
||||
{
|
||||
@try
|
||||
{
|
||||
uint32_t b0 = [aString characterAtIndex:0];
|
||||
|
|
@ -727,7 +727,7 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
NSLog(@"Encountered an unsupported attributed character. Exception: %@ String: %@", e.name, aString);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@try
|
||||
{
|
||||
|
|
@ -760,36 +760,36 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
if (!(mModifiers & NSEventModifierFlagCommand) &&
|
||||
!(mModifiers & NSEventModifierFlagShift) &&
|
||||
!(mModifiers & NSEventModifierFlagOption))
|
||||
{
|
||||
callUnicodeCallback(13, 0);
|
||||
} else {
|
||||
callUnicodeCallback(13, mModifiers);
|
||||
}
|
||||
{
|
||||
callUnicodeCallback(13, 0);
|
||||
} else {
|
||||
callUnicodeCallback(13, mModifiers);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
|
||||
{
|
||||
return NSNotFound;
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
|
||||
{
|
||||
float pos[4] = {0, 0, 0, 0};
|
||||
getPreeditLocation(pos, mMarkedTextLength);
|
||||
return NSMakeRect(pos[0], pos[1], pos[2], pos[3]);
|
||||
float pos[4] = {0, 0, 0, 0};
|
||||
getPreeditLocation(pos, mMarkedTextLength);
|
||||
return NSMakeRect(pos[0], pos[1], pos[2], pos[3]);
|
||||
}
|
||||
|
||||
- (void)doCommandBySelector:(SEL)aSelector
|
||||
{
|
||||
if (aSelector == @selector(insertNewline:))
|
||||
{
|
||||
[self insertNewline:self];
|
||||
}
|
||||
if (aSelector == @selector(insertNewline:))
|
||||
{
|
||||
[self insertNewline:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)drawsVerticallyForCharacterAtIndex:(NSUInteger)charIndex
|
||||
{
|
||||
return NO;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) allowMarkedTextInput:(bool)allowed
|
||||
|
|
@ -824,7 +824,7 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
|
||||
- (void) setGLView:(LLOpenGLView *)view
|
||||
{
|
||||
glview = view;
|
||||
glview = view;
|
||||
}
|
||||
|
||||
- (void)keyDown:(NSEvent *)theEvent
|
||||
|
|
@ -875,24 +875,24 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
|
||||
- (id) init
|
||||
{
|
||||
return self;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL) becomeFirstResponder
|
||||
{
|
||||
callFocus();
|
||||
return true;
|
||||
callFocus();
|
||||
return true;
|
||||
}
|
||||
|
||||
- (BOOL) resignFirstResponder
|
||||
{
|
||||
callFocusLost();
|
||||
return true;
|
||||
callFocusLost();
|
||||
return true;
|
||||
}
|
||||
|
||||
- (void) close
|
||||
{
|
||||
callQuitHandler();
|
||||
callQuitHandler();
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ public:
|
|||
#if LL_WINDOWS
|
||||
virtual bool getCursorDelta(LLCoordCommon* delta) = 0;
|
||||
#endif
|
||||
virtual bool isWrapMouse() const = 0;
|
||||
virtual void showCursor() = 0;
|
||||
virtual void hideCursor() = 0;
|
||||
virtual bool isCursorHidden() = 0;
|
||||
|
|
|
|||
|
|
@ -68,7 +68,13 @@ void LLWindowCallbacks::handleMouseLeave(LLWindow *window)
|
|||
return;
|
||||
}
|
||||
|
||||
bool LLWindowCallbacks::handleCloseRequest(LLWindow *window)
|
||||
bool LLWindowCallbacks::handleCloseRequest(LLWindow *window, bool from_user)
|
||||
{
|
||||
//allow the window to close
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LLWindowCallbacks::handleSessionExit(LLWindow* window)
|
||||
{
|
||||
//allow the window to close
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ public:
|
|||
virtual bool handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
|
||||
virtual void handleMouseLeave(LLWindow *window);
|
||||
// return true to allow window to close, which will then cause handleQuit to be called
|
||||
virtual bool handleCloseRequest(LLWindow *window);
|
||||
virtual bool handleCloseRequest(LLWindow *window, bool from_user);
|
||||
virtual bool handleSessionExit(LLWindow* window);
|
||||
// window is about to be destroyed, clean up your business
|
||||
virtual void handleQuit(LLWindow *window);
|
||||
virtual bool handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ public:
|
|||
#if LL_WINDOWS
|
||||
/*virtual*/ bool getCursorDelta(LLCoordCommon* delta) override { return false; }
|
||||
#endif
|
||||
/*virtual*/ bool isWrapMouse() const override { return true; }
|
||||
/*virtual*/ void showCursor() override {}
|
||||
/*virtual*/ void hideCursor() override {}
|
||||
/*virtual*/ void showCursorFromMouseMove() override {}
|
||||
|
|
|
|||
|
|
@ -41,15 +41,15 @@
|
|||
|
||||
int createNSApp(int argc, const char *argv[])
|
||||
{
|
||||
return NSApplicationMain(argc, argv);
|
||||
return NSApplicationMain(argc, argv);
|
||||
}
|
||||
|
||||
void setupCocoa()
|
||||
{
|
||||
static bool inited = false;
|
||||
|
||||
if(!inited)
|
||||
{
|
||||
static bool inited = false;
|
||||
|
||||
if(!inited)
|
||||
{
|
||||
@autoreleasepool {
|
||||
// The following prevents the Cocoa command line parser from trying to open 'unknown' arguements as documents.
|
||||
// ie. running './secondlife -set Language fr' would cause a pop-up saying can't open document 'fr'
|
||||
|
|
@ -57,8 +57,8 @@ void setupCocoa()
|
|||
[[NSUserDefaults standardUserDefaults] setObject:@"NO" forKey:@"NSTreatUnknownArgumentsAsOpen"];
|
||||
}
|
||||
|
||||
inited = true;
|
||||
}
|
||||
inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool copyToPBoard(const unsigned short *str, unsigned int len)
|
||||
|
|
@ -66,7 +66,7 @@ bool copyToPBoard(const unsigned short *str, unsigned int len)
|
|||
@autoreleasepool {
|
||||
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
|
||||
[pboard clearContents];
|
||||
|
||||
|
||||
NSArray *contentsToPaste = [[[NSArray alloc] initWithObjects:[NSString stringWithCharacters:str length:len], nil] autorelease];
|
||||
return [pboard writeObjects:contentsToPaste];
|
||||
}
|
||||
|
|
@ -74,8 +74,8 @@ bool copyToPBoard(const unsigned short *str, unsigned int len)
|
|||
|
||||
bool pasteBoardAvailable()
|
||||
{
|
||||
NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
|
||||
return [[NSPasteboard generalPasteboard] canReadObjectForClasses:classArray options:[NSDictionary dictionary]];
|
||||
NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
|
||||
return [[NSPasteboard generalPasteboard] canReadObjectForClasses:classArray options:[NSDictionary dictionary]];
|
||||
}
|
||||
|
||||
unsigned short *copyFromPBoard()
|
||||
|
|
@ -111,55 +111,55 @@ CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY)
|
|||
hotSpot:NSMakePoint(hotspotX, hotspotY)
|
||||
] retain];
|
||||
}
|
||||
|
||||
return (CursorRef)cursor;
|
||||
|
||||
return (CursorRef)cursor;
|
||||
}
|
||||
|
||||
void setArrowCursor()
|
||||
{
|
||||
NSCursor *cursor = [NSCursor arrowCursor];
|
||||
[NSCursor unhide];
|
||||
[cursor set];
|
||||
NSCursor *cursor = [NSCursor arrowCursor];
|
||||
[NSCursor unhide];
|
||||
[cursor set];
|
||||
}
|
||||
|
||||
void setIBeamCursor()
|
||||
{
|
||||
NSCursor *cursor = [NSCursor IBeamCursor];
|
||||
[cursor set];
|
||||
NSCursor *cursor = [NSCursor IBeamCursor];
|
||||
[cursor set];
|
||||
}
|
||||
|
||||
void setPointingHandCursor()
|
||||
{
|
||||
NSCursor *cursor = [NSCursor pointingHandCursor];
|
||||
[cursor set];
|
||||
NSCursor *cursor = [NSCursor pointingHandCursor];
|
||||
[cursor set];
|
||||
}
|
||||
|
||||
void setCopyCursor()
|
||||
{
|
||||
NSCursor *cursor = [NSCursor dragCopyCursor];
|
||||
[cursor set];
|
||||
NSCursor *cursor = [NSCursor dragCopyCursor];
|
||||
[cursor set];
|
||||
}
|
||||
|
||||
void setCrossCursor()
|
||||
{
|
||||
NSCursor *cursor = [NSCursor crosshairCursor];
|
||||
[cursor set];
|
||||
NSCursor *cursor = [NSCursor crosshairCursor];
|
||||
[cursor set];
|
||||
}
|
||||
|
||||
void setNotAllowedCursor()
|
||||
{
|
||||
NSCursor *cursor = [NSCursor operationNotAllowedCursor];
|
||||
[cursor set];
|
||||
NSCursor *cursor = [NSCursor operationNotAllowedCursor];
|
||||
[cursor set];
|
||||
}
|
||||
|
||||
void hideNSCursor()
|
||||
{
|
||||
[NSCursor hide];
|
||||
[NSCursor hide];
|
||||
}
|
||||
|
||||
void showNSCursor()
|
||||
{
|
||||
[NSCursor unhide];
|
||||
[NSCursor unhide];
|
||||
}
|
||||
|
||||
#if LL_DARWIN
|
||||
|
|
@ -179,42 +179,42 @@ bool isCGCursorVisible()
|
|||
|
||||
void hideNSCursorTillMove(bool hide)
|
||||
{
|
||||
[NSCursor setHiddenUntilMouseMoves:hide];
|
||||
[NSCursor setHiddenUntilMouseMoves:hide];
|
||||
}
|
||||
|
||||
// This is currently unused, since we want all our cursors to persist for the life of the app, but I've included it for completeness.
|
||||
OSErr releaseImageCursor(CursorRef ref)
|
||||
{
|
||||
if( ref != NULL )
|
||||
{
|
||||
if( ref != NULL )
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSCursor *cursor = (NSCursor*)ref;
|
||||
[cursor autorelease];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return paramErr;
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return paramErr;
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
OSErr setImageCursor(CursorRef ref)
|
||||
{
|
||||
if( ref != NULL )
|
||||
{
|
||||
if( ref != NULL )
|
||||
{
|
||||
@autoreleasepool {
|
||||
NSCursor *cursor = (NSCursor*)ref;
|
||||
[cursor set];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return paramErr;
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return paramErr;
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
// Now for some unholy juggling between generic pointers and casting them to Obj-C objects!
|
||||
|
|
@ -222,46 +222,46 @@ OSErr setImageCursor(CursorRef ref)
|
|||
|
||||
NSWindowRef createNSWindow(int x, int y, int width, int height)
|
||||
{
|
||||
LLNSWindow *window = [[LLNSWindow alloc]initWithContentRect:NSMakeRect(x, y, width, height)
|
||||
LLNSWindow *window = [[LLNSWindow alloc]initWithContentRect:NSMakeRect(x, y, width, height)
|
||||
styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable
|
||||
backing:NSBackingStoreBuffered defer:NO];
|
||||
[window makeKeyAndOrderFront:nil];
|
||||
[window setAcceptsMouseMovedEvents:TRUE];
|
||||
[window makeKeyAndOrderFront:nil];
|
||||
[window setAcceptsMouseMovedEvents:TRUE];
|
||||
[window setRestorable:FALSE]; // Viewer manages state from own settings
|
||||
return window;
|
||||
return window;
|
||||
}
|
||||
|
||||
GLViewRef createOpenGLView(NSWindowRef window, unsigned int samples, bool vsync)
|
||||
{
|
||||
LLOpenGLView *glview = [[LLOpenGLView alloc]initWithFrame:[(LLNSWindow*)window frame] withSamples:samples andVsync:vsync];
|
||||
[(LLNSWindow*)window setContentView:glview];
|
||||
return glview;
|
||||
LLOpenGLView *glview = [[LLOpenGLView alloc]initWithFrame:[(LLNSWindow*)window frame] withSamples:samples andVsync:vsync];
|
||||
[(LLNSWindow*)window setContentView:glview];
|
||||
return glview;
|
||||
}
|
||||
|
||||
void glSwapBuffers(void* context)
|
||||
{
|
||||
[(NSOpenGLContext*)context flushBuffer];
|
||||
[(NSOpenGLContext*)context flushBuffer];
|
||||
}
|
||||
|
||||
CGLContextObj getCGLContextObj(GLViewRef view)
|
||||
{
|
||||
return [(LLOpenGLView *)view getCGLContextObj];
|
||||
return [(LLOpenGLView *)view getCGLContextObj];
|
||||
}
|
||||
|
||||
CGLPixelFormatObj* getCGLPixelFormatObj(NSWindowRef window)
|
||||
{
|
||||
LLOpenGLView *glview = [(LLNSWindow*)window contentView];
|
||||
return [glview getCGLPixelFormatObj];
|
||||
LLOpenGLView *glview = [(LLNSWindow*)window contentView];
|
||||
return [glview getCGLPixelFormatObj];
|
||||
}
|
||||
|
||||
unsigned long getVramSize(GLViewRef view)
|
||||
{
|
||||
return [(LLOpenGLView *)view getVramSize];
|
||||
return [(LLOpenGLView *)view getVramSize];
|
||||
}
|
||||
|
||||
float getDeviceUnitSize(GLViewRef view)
|
||||
{
|
||||
return [(LLOpenGLView*)view convertSizeToBacking:NSMakeSize(1, 1)].width;
|
||||
return [(LLOpenGLView*)view convertSizeToBacking:NSMakeSize(1, 1)].width;
|
||||
}
|
||||
|
||||
CGRect getContentViewRect(NSWindowRef window)
|
||||
|
|
@ -276,48 +276,48 @@ CGRect getBackingViewRect(NSWindowRef window, GLViewRef view)
|
|||
|
||||
void getWindowSize(NSWindowRef window, float* size)
|
||||
{
|
||||
NSRect frame = [(LLNSWindow*)window frame];
|
||||
size[0] = frame.origin.x;
|
||||
size[1] = frame.origin.y;
|
||||
size[2] = frame.size.width;
|
||||
size[3] = frame.size.height;
|
||||
NSRect frame = [(LLNSWindow*)window frame];
|
||||
size[0] = frame.origin.x;
|
||||
size[1] = frame.origin.y;
|
||||
size[2] = frame.size.width;
|
||||
size[3] = frame.size.height;
|
||||
}
|
||||
|
||||
void setWindowSize(NSWindowRef window, int width, int height)
|
||||
{
|
||||
NSRect frame = [(LLNSWindow*)window frame];
|
||||
frame.size.width = width;
|
||||
frame.size.height = height;
|
||||
[(LLNSWindow*)window setFrame:frame display:TRUE];
|
||||
NSRect frame = [(LLNSWindow*)window frame];
|
||||
frame.size.width = width;
|
||||
frame.size.height = height;
|
||||
[(LLNSWindow*)window setFrame:frame display:TRUE];
|
||||
}
|
||||
|
||||
void setWindowPos(NSWindowRef window, float* pos)
|
||||
{
|
||||
NSPoint point;
|
||||
point.x = pos[0];
|
||||
point.y = pos[1];
|
||||
[(LLNSWindow*)window setFrameOrigin:point];
|
||||
NSPoint point;
|
||||
point.x = pos[0];
|
||||
point.y = pos[1];
|
||||
[(LLNSWindow*)window setFrameOrigin:point];
|
||||
}
|
||||
|
||||
void getCursorPos(NSWindowRef window, float* pos)
|
||||
{
|
||||
NSPoint mLoc;
|
||||
mLoc = [(LLNSWindow*)window mouseLocationOutsideOfEventStream];
|
||||
pos[0] = mLoc.x;
|
||||
pos[1] = mLoc.y;
|
||||
NSPoint mLoc;
|
||||
mLoc = [(LLNSWindow*)window mouseLocationOutsideOfEventStream];
|
||||
pos[0] = mLoc.x;
|
||||
pos[1] = mLoc.y;
|
||||
}
|
||||
|
||||
void makeWindowOrderFront(NSWindowRef window)
|
||||
{
|
||||
[(LLNSWindow*)window makeKeyAndOrderFront:nil];
|
||||
[(LLNSWindow*)window makeKeyAndOrderFront:nil];
|
||||
}
|
||||
|
||||
void convertScreenToWindow(NSWindowRef window, float *coord)
|
||||
{
|
||||
NSRect point = NSMakeRect(coord[0], coord[1], 0,0);
|
||||
point = [(LLNSWindow*)window convertRectFromScreen:point];
|
||||
coord[0] = point.origin.x;
|
||||
coord[1] = point.origin.y;
|
||||
point = [(LLNSWindow*)window convertRectFromScreen:point];
|
||||
coord[0] = point.origin.x;
|
||||
coord[1] = point.origin.y;
|
||||
}
|
||||
|
||||
void convertRectToScreen(NSWindowRef window, float *coord)
|
||||
|
|
@ -325,21 +325,21 @@ void convertRectToScreen(NSWindowRef window, float *coord)
|
|||
NSRect rect = NSMakeRect(coord[0], coord[1], coord[2], coord[3]);;
|
||||
rect = [(LLNSWindow*)window convertRectToScreen:rect];
|
||||
|
||||
coord[0] = rect.origin.x;
|
||||
coord[1] = rect.origin.y;
|
||||
coord[2] = rect.size.width;
|
||||
coord[3] = rect.size.height;
|
||||
coord[0] = rect.origin.x;
|
||||
coord[1] = rect.origin.y;
|
||||
coord[2] = rect.size.width;
|
||||
coord[3] = rect.size.height;
|
||||
}
|
||||
|
||||
void convertRectFromScreen(NSWindowRef window, float *coord)
|
||||
{
|
||||
NSRect point = NSMakeRect(coord[0], coord[1], coord[2], coord[3]);
|
||||
point = [(LLNSWindow*)window convertRectFromScreen:point];
|
||||
|
||||
coord[0] = point.origin.x;
|
||||
coord[1] = point.origin.y;
|
||||
coord[2] = point.size.width;
|
||||
coord[3] = point.size.height;
|
||||
NSRect point = NSMakeRect(coord[0], coord[1], coord[2], coord[3]);
|
||||
point = [(LLNSWindow*)window convertRectFromScreen:point];
|
||||
|
||||
coord[0] = point.origin.x;
|
||||
coord[1] = point.origin.y;
|
||||
coord[2] = point.size.width;
|
||||
coord[3] = point.size.height;
|
||||
}
|
||||
|
||||
void convertWindowToScreen(NSWindowRef window, float *coord)
|
||||
|
|
@ -353,24 +353,24 @@ void convertWindowToScreen(NSWindowRef window, float *coord)
|
|||
|
||||
void closeWindow(NSWindowRef window)
|
||||
{
|
||||
[(LLNSWindow*)window close];
|
||||
[(LLNSWindow*)window release];
|
||||
[(LLNSWindow*)window close];
|
||||
[(LLNSWindow*)window release];
|
||||
}
|
||||
|
||||
void removeGLView(GLViewRef view)
|
||||
{
|
||||
[(LLOpenGLView*)view clearGLContext];
|
||||
[(LLOpenGLView*)view removeFromSuperview];
|
||||
[(LLOpenGLView*)view clearGLContext];
|
||||
[(LLOpenGLView*)view removeFromSuperview];
|
||||
}
|
||||
|
||||
void setupInputWindow(NSWindowRef window, GLViewRef glview)
|
||||
{
|
||||
[[(LLAppDelegate*)[NSApp delegate] inputView] setGLView:(LLOpenGLView*)glview];
|
||||
[[(LLAppDelegate*)[NSApp delegate] inputView] setGLView:(LLOpenGLView*)glview];
|
||||
}
|
||||
|
||||
void commitCurrentPreedit(GLViewRef glView)
|
||||
{
|
||||
[(LLOpenGLView*)glView commitCurrentPreedit];
|
||||
[(LLOpenGLView*)glView commitCurrentPreedit];
|
||||
}
|
||||
|
||||
void allowDirectMarkedTextInput(bool allow, GLViewRef glView)
|
||||
|
|
@ -380,20 +380,20 @@ void allowDirectMarkedTextInput(bool allow, GLViewRef glView)
|
|||
|
||||
NSWindowRef getMainAppWindow()
|
||||
{
|
||||
LLNSWindow *winRef = [(LLAppDelegate*)[[NSApplication sharedApplication] delegate] window];
|
||||
|
||||
[winRef setAcceptsMouseMovedEvents:TRUE];
|
||||
return winRef;
|
||||
LLNSWindow *winRef = [(LLAppDelegate*)[[NSApplication sharedApplication] delegate] window];
|
||||
|
||||
[winRef setAcceptsMouseMovedEvents:TRUE];
|
||||
return winRef;
|
||||
}
|
||||
|
||||
void makeFirstResponder(NSWindowRef window, GLViewRef view)
|
||||
{
|
||||
[(LLNSWindow*)window makeFirstResponder:(LLOpenGLView*)view];
|
||||
[(LLNSWindow*)window makeFirstResponder:(LLOpenGLView*)view];
|
||||
}
|
||||
|
||||
void requestUserAttention()
|
||||
{
|
||||
[[NSApplication sharedApplication] requestUserAttention:NSInformationalRequest];
|
||||
[[NSApplication sharedApplication] requestUserAttention:NSInformationalRequest];
|
||||
}
|
||||
|
||||
long showAlert(std::string text, std::string title, int type)
|
||||
|
|
@ -401,7 +401,7 @@ long showAlert(std::string text, std::string title, int type)
|
|||
long ret = 0;
|
||||
@autoreleasepool {
|
||||
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
|
||||
|
||||
|
||||
[alert setMessageText:[NSString stringWithCString:title.c_str() encoding:[NSString defaultCStringEncoding]]];
|
||||
[alert setInformativeText:[NSString stringWithCString:text.c_str() encoding:[NSString defaultCStringEncoding]]];
|
||||
if (type == 0)
|
||||
|
|
@ -418,7 +418,7 @@ long showAlert(std::string text, std::string title, int type)
|
|||
}
|
||||
ret = [alert runModal];
|
||||
}
|
||||
|
||||
|
||||
if (ret == NSAlertFirstButtonReturn)
|
||||
{
|
||||
if (type == 1)
|
||||
|
|
@ -438,7 +438,7 @@ long showAlert(std::string text, std::string title, int type)
|
|||
ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -451,5 +451,5 @@ long showAlert(std::string text, std::string title, int type)
|
|||
|
||||
unsigned int getModifiers()
|
||||
{
|
||||
return [NSEvent modifierFlags];
|
||||
return [NSEvent modifierFlags];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -610,7 +610,7 @@ void callQuitHandler()
|
|||
{
|
||||
if (gWindowImplementation && gWindowImplementation->getCallbacks())
|
||||
{
|
||||
if(gWindowImplementation->getCallbacks()->handleCloseRequest(gWindowImplementation))
|
||||
if(gWindowImplementation->getCallbacks()->handleCloseRequest(gWindowImplementation, true))
|
||||
{
|
||||
gWindowImplementation->getCallbacks()->handleQuit(gWindowImplementation);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ public:
|
|||
bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override;
|
||||
bool setCursorPosition(LLCoordWindow position) override;
|
||||
bool getCursorPosition(LLCoordWindow *position) override;
|
||||
bool isWrapMouse() const override { return !mCursorDecoupled; };
|
||||
void showCursor() override;
|
||||
void hideCursor() override;
|
||||
void showCursorFromMouseMove() override;
|
||||
|
|
|
|||
|
|
@ -1881,7 +1881,7 @@ void LLWindowSDL::gatherInput()
|
|||
{
|
||||
// *FIX: More informative dialog?
|
||||
LL_INFOS() << "Could not recreate context after resize! Quitting..." << LL_ENDL;
|
||||
if(mCallbacks->handleCloseRequest(this))
|
||||
if(mCallbacks->handleCloseRequest(this, false))
|
||||
{
|
||||
// Get the app to initiate cleanup.
|
||||
mCallbacks->handleQuit(this);
|
||||
|
|
@ -1931,7 +1931,7 @@ void LLWindowSDL::gatherInput()
|
|||
break;
|
||||
|
||||
case SDL_QUIT:
|
||||
if(mCallbacks->handleCloseRequest(this))
|
||||
if(mCallbacks->handleCloseRequest(this, true))
|
||||
{
|
||||
// Get the app to initiate cleanup.
|
||||
mCallbacks->handleQuit(this);
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ public:
|
|||
/*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp = NULL);
|
||||
/*virtual*/ bool setCursorPosition(LLCoordWindow position);
|
||||
/*virtual*/ bool getCursorPosition(LLCoordWindow *position);
|
||||
/*virtual*/ bool isWrapMouse() const override { return true; }
|
||||
/*virtual*/ void showCursor();
|
||||
/*virtual*/ void hideCursor();
|
||||
/*virtual*/ void showCursorFromMouseMove();
|
||||
|
|
|
|||
|
|
@ -450,6 +450,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
|
|||
F32 max_gl_version)
|
||||
:
|
||||
LLWindow(callbacks, fullscreen, flags),
|
||||
mAbsoluteCursorPosition(false),
|
||||
mMaxGLVersion(max_gl_version),
|
||||
mMaxCores(max_cores)
|
||||
{
|
||||
|
|
@ -2464,10 +2465,13 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
|
|||
case WM_CLOSE:
|
||||
{
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_CLOSE");
|
||||
// todo: WM_CLOSE can be caused by user and by task manager,
|
||||
// distinguish these cases.
|
||||
// For now assume it is always user.
|
||||
window_imp->post([=]()
|
||||
{
|
||||
// Will the app allow the window to close?
|
||||
if (window_imp->mCallbacks->handleCloseRequest(window_imp))
|
||||
if (window_imp->mCallbacks->handleCloseRequest(window_imp, true))
|
||||
{
|
||||
// Get the app to initiate cleanup.
|
||||
window_imp->mCallbacks->handleQuit(window_imp);
|
||||
|
|
@ -2485,6 +2489,50 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
case WM_QUERYENDSESSION:
|
||||
{
|
||||
// Generally means that OS is going to shut down or user is going to log off.
|
||||
// Can use ShutdownBlockReasonCreate here.
|
||||
LL_INFOS("Window") << "Received WM_QUERYENDSESSION with wParam: " << (U32)w_param << " lParam: " << (U32)l_param << LL_ENDL;
|
||||
return TRUE; // 1 = ok to end session. 0 no longer works by itself, use ShutdownBlockReasonCreate
|
||||
}
|
||||
case WM_ENDSESSION:
|
||||
{
|
||||
// OS session is shutting down, initiate cleanup.
|
||||
// Comes after WM_QUERYENDSESSION
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_ENDSESSION");
|
||||
LL_INFOS("Window") << "Received WM_ENDSESSION with wParam: " << (U32)w_param << " lParam: " << (U32)l_param << LL_ENDL;
|
||||
unsigned int end_session_flags = (U32)w_param;
|
||||
if (end_session_flags == 0)
|
||||
{
|
||||
// session is not actually ending
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((end_session_flags & ENDSESSION_CLOSEAPP)
|
||||
|| (end_session_flags & ENDSESSION_CRITICAL)
|
||||
|| (end_session_flags & ENDSESSION_LOGOFF))
|
||||
{
|
||||
window_imp->post([=]()
|
||||
{
|
||||
// Check if app needs cleanup or can be closed immediately.
|
||||
if (window_imp->mCallbacks->handleSessionExit(window_imp))
|
||||
{
|
||||
// Get the app to initiate cleanup.
|
||||
window_imp->mCallbacks->handleQuit(window_imp);
|
||||
// The app is responsible for calling destroyWindow when done with GL
|
||||
}
|
||||
});
|
||||
// Give app a second to finish up. That's not enough for a clean exit,
|
||||
// but better than nothing.
|
||||
// Todo: sync this better, some kind of waitForResult? Can't wait forever,
|
||||
// but can potentially use ShutdownBlockReasonCreate for a bigger delay.
|
||||
ms_sleep(1000);
|
||||
}
|
||||
// Don't need to post quit or destroy window,
|
||||
// if session is ending OS is going to take care of it.
|
||||
return 0;
|
||||
}
|
||||
case WM_COMMAND:
|
||||
{
|
||||
LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_COMMAND");
|
||||
|
|
@ -3109,6 +3157,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
|
|||
|
||||
prev_absolute_x = absolute_x;
|
||||
prev_absolute_y = absolute_y;
|
||||
window_imp->mAbsoluteCursorPosition = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -3125,6 +3174,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
|
|||
window_imp->mRawMouseDelta.mX += (S32)round((F32)raw->data.mouse.lLastX * (F32)speed / DEFAULT_SPEED);
|
||||
window_imp->mRawMouseDelta.mY -= (S32)round((F32)raw->data.mouse.lLastY * (F32)speed / DEFAULT_SPEED);
|
||||
}
|
||||
window_imp->mAbsoluteCursorPosition = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ public:
|
|||
/*virtual*/ bool setCursorPosition(LLCoordWindow position);
|
||||
/*virtual*/ bool getCursorPosition(LLCoordWindow *position);
|
||||
/*virtual*/ bool getCursorDelta(LLCoordCommon* delta);
|
||||
/*virtual*/ bool isWrapMouse() const override { return !mAbsoluteCursorPosition; };
|
||||
/*virtual*/ void showCursor();
|
||||
/*virtual*/ void hideCursor();
|
||||
/*virtual*/ void showCursorFromMouseMove();
|
||||
|
|
@ -195,6 +196,7 @@ protected:
|
|||
|
||||
HCURSOR mCursor[ UI_CURSOR_COUNT ]; // Array of all mouse cursors
|
||||
LLCoordWindow mCursorPosition; // mouse cursor position, should only be mutated on main thread
|
||||
bool mAbsoluteCursorPosition; // true if last position was received in absolute coordinates.
|
||||
LLMutex mRawMouseMutex;
|
||||
RAWINPUTDEVICE mRawMouse;
|
||||
LLCoordWindow mLastCursorPosition; // mouse cursor position from previous frame
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ include(VulkanGltf)
|
|||
include(ZLIBNG)
|
||||
include(LLPrimitive)
|
||||
|
||||
if (NOT HAVOK_TPV)
|
||||
if (HAVOK)
|
||||
# When using HAVOK_TPV, the library is precompiled, so no need for this
|
||||
|
||||
# Stub and probably havok lib itself is a hack, autobuild loads a 3p that really is a source tarball
|
||||
|
|
@ -76,7 +76,7 @@ if (NOT HAVOK_TPV)
|
|||
target_compile_options( llphysicsextensions PRIVATE -Wno-unused-local-typedef)
|
||||
endif (DARWIN)
|
||||
endif()
|
||||
endif (NOT HAVOK_TPV)
|
||||
endif ()
|
||||
|
||||
set(viewer_SOURCE_FILES
|
||||
gltfscenemanager.cpp
|
||||
|
|
@ -1714,10 +1714,6 @@ if (WINDOWS)
|
|||
list(APPEND viewer_SOURCE_FILES ${viewer_INSTALLER_FILES})
|
||||
endif (WINDOWS)
|
||||
|
||||
if (HAVOK OR HAVOK_TPV)
|
||||
set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_HAVOK")
|
||||
endif (HAVOK OR HAVOK_TPV)
|
||||
|
||||
if( DEFINED LLSTARTUP_COMPILE_FLAGS )
|
||||
# progress view disables/enables icons based on available packages
|
||||
set_source_files_properties(llprogressview.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}")
|
||||
|
|
@ -1956,13 +1952,30 @@ elseif (DARWIN)
|
|||
PROPERTIES
|
||||
RESOURCE SecondLife.xib
|
||||
LINK_FLAGS_RELEASE "${LINK_FLAGS_RELEASE} -Xlinker -dead_strip"
|
||||
# arch specific flags for universal builds: https://stackoverflow.com/a/77942065
|
||||
XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=x86_64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_FULL"
|
||||
XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=arm64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_STUB"
|
||||
# only generate the .MAP file for llphysicsextensions_tpv on x86_64
|
||||
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP"
|
||||
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensionsstub/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsstub"
|
||||
)
|
||||
if(HAVOK)
|
||||
set_target_properties(${VIEWER_BINARY_NAME}
|
||||
PROPERTIES
|
||||
# arch specific flags for universal builds: https://stackoverflow.com/a/77942065
|
||||
XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=x86_64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_FULL -DLL_HAVOK=1"
|
||||
XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=arm64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_STUB"
|
||||
# only generate the .MAP file for llphysicsextensions_tpv on x86_64
|
||||
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP"
|
||||
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_BINARY_DIR}/llphysicsextensionsos/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsos"
|
||||
)
|
||||
elseif(HAVOK_TPV)
|
||||
set_target_properties(${VIEWER_BINARY_NAME}
|
||||
PROPERTIES
|
||||
# arch specific flags for universal builds: https://stackoverflow.com/a/77942065
|
||||
XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=x86_64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_FULL -DLL_HAVOK=1"
|
||||
XCODE_ATTRIBUTE_OTHER_CFLAGS[arch=arm64] "$(inherited) -DLLPHYSICSEXTENSIONS_USE_STUB"
|
||||
# only generate the .MAP file for llphysicsextensions_tpv on x86_64
|
||||
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${ARCH_PREBUILT_DIRS}/ -lllphysicsextensions_tpv"
|
||||
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_BINARY_DIR}/llphysicsextensionsos/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsos"
|
||||
)
|
||||
else()
|
||||
target_link_libraries(${VIEWER_BINARY_NAME} llphysicsextensionsos)
|
||||
endif()
|
||||
else (WINDOWS)
|
||||
# Linux
|
||||
set_target_properties(${VIEWER_BINARY_NAME}
|
||||
|
|
@ -2039,6 +2052,10 @@ if( TARGET ll::nvapi )
|
|||
target_link_libraries(${VIEWER_BINARY_NAME} ll::nvapi )
|
||||
endif()
|
||||
|
||||
if ( TARGET llconvexdecomposition )
|
||||
target_link_libraries(${VIEWER_BINARY_NAME} llconvexdecomposition )
|
||||
endif ()
|
||||
|
||||
set(ARTWORK_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH
|
||||
"Path to artwork files.")
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
7.2.2
|
||||
7.2.3
|
||||
|
|
|
|||
|
|
@ -2469,16 +2469,16 @@
|
|||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>DisableMouseWarp</key>
|
||||
<key>MouseWarpMode</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Disable warping of the mouse to the center of the screen during alt-zoom and mouse look. Useful with certain input devices, mouse sharing programs like Synergy, or running under Parallels.</string>
|
||||
<string>Controls warping of the mouse to the center of the screen during alt-zoom and mouse look. Useful with certain input devices, mouse sharing programs like Synergy, or running under Parallels. 0 - automatic, 1 - on, 2 - off</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<string>S32</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
<key>DisableExternalBrowser</key>
|
||||
<map>
|
||||
|
|
@ -4380,7 +4380,7 @@
|
|||
<key>Type</key>
|
||||
<string>F32</string>
|
||||
<key>Value</key>
|
||||
<real>40.0</real>
|
||||
<real>90.0</real>
|
||||
</map>
|
||||
<key>LogMessages</key>
|
||||
<map>
|
||||
|
|
@ -7863,7 +7863,7 @@
|
|||
<key>RenderMaxOpenGLVersion</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Maximum OpenGL version to attempt use (minimum 3.1 maximum 4.6). Requires restart.</string>
|
||||
<string>Maximum OpenGL version to attempt use (minimum 3.1 maximum 4.6). Requires restart. Windows only.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
|
|
@ -9111,7 +9111,7 @@
|
|||
<key>RenderQualityPerformance</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Which graphics settings you've chosen</string>
|
||||
<string>Which graphics settings you've chosen. Don't use this setting to change quality directly from debug settings.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
|
|
@ -9119,7 +9119,18 @@
|
|||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
|
||||
<key>DebugQualityPerformance</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Allows to change performance quality directly from debug settings.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>U32</string>
|
||||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
|
||||
<key>RenderReflectionDetail</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
|
@ -10330,13 +10341,13 @@
|
|||
<key>SceneLoadRearMaxRadiusFraction</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>a percentage of draw distance beyond which all objects outside of view frustum will be unloaded, regardless of pixel threshold</string>
|
||||
<string>a fraction of draw distance beyond which all objects outside of view frustum will be unloaded, regardless of pixel threshold</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>F32</string>
|
||||
<key>Value</key>
|
||||
<real>75.0</real>
|
||||
<real>0.75</real>
|
||||
</map>
|
||||
<key>SceneLoadRearPixelThreshold</key>
|
||||
<map>
|
||||
|
|
@ -13036,9 +13047,9 @@
|
|||
<key>Use24HourClock</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>12 vs 24. At the moment only for region restart schedule floater</string>
|
||||
<string>12 vs 24. At the moment coverage is partial</string>
|
||||
<key>Persist</key>
|
||||
<integer>0</integer>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
|
|
|
|||
|
|
@ -158,6 +158,11 @@ bool Buffer::prep(Asset& asset)
|
|||
{
|
||||
std::string dir = gDirUtilp->getDirName(asset.mFilename);
|
||||
std::string bin_file = dir + gDirUtilp->getDirDelimiter() + mUri;
|
||||
if (!gDirUtilp->fileExists(bin_file))
|
||||
{
|
||||
// Characters might be escaped in the URI
|
||||
bin_file = dir + gDirUtilp->getDirDelimiter() + LLURI::unescape(mUri);
|
||||
}
|
||||
|
||||
llifstream file(bin_file.c_str(), std::ios::binary);
|
||||
if (!file.is_open())
|
||||
|
|
|
|||
|
|
@ -412,17 +412,14 @@ void LLGLTFLoader::processNodeHierarchy(S32 node_idx, std::map<std::string, S32>
|
|||
// Process this node's mesh if it has one
|
||||
if (node.mMesh >= 0 && node.mMesh < mGLTFAsset.mMeshes.size())
|
||||
{
|
||||
LLMatrix4 transformation;
|
||||
material_map mats;
|
||||
|
||||
LLModel* pModel = new LLModel(volume_params, 0.f);
|
||||
const LL::GLTF::Mesh& mesh = mGLTFAsset.mMeshes[node.mMesh];
|
||||
|
||||
// Get base mesh name and track usage
|
||||
std::string base_name = getLodlessLabel(mesh);
|
||||
// Get base node name and track usage
|
||||
// Potentially multiple nodes can reuse the same mesh and Collada used
|
||||
// node name instead of mesh name, so for consistency use node name if
|
||||
// avaliable, node index otherwise.
|
||||
std::string base_name = getLodlessLabel(node);
|
||||
if (base_name.empty())
|
||||
{
|
||||
base_name = "mesh_" + std::to_string(node.mMesh);
|
||||
base_name = "node_" + std::to_string(node_idx);
|
||||
}
|
||||
|
||||
S32 instance_count = mesh_name_counts[base_name]++;
|
||||
|
|
@ -433,6 +430,12 @@ void LLGLTFLoader::processNodeHierarchy(S32 node_idx, std::map<std::string, S32>
|
|||
base_name = base_name + "_copy_" + std::to_string(instance_count);
|
||||
}
|
||||
|
||||
LLMatrix4 transformation;
|
||||
material_map mats;
|
||||
|
||||
LLModel* pModel = new LLModel(volume_params, 0.f);
|
||||
const LL::GLTF::Mesh& mesh = mGLTFAsset.mMeshes[node.mMesh];
|
||||
|
||||
if (populateModelFromMesh(pModel, base_name, mesh, node, mats) &&
|
||||
(LLModel::NO_ERRORS == pModel->getStatus()) &&
|
||||
validate_model(pModel))
|
||||
|
|
@ -652,6 +655,14 @@ std::string LLGLTFLoader::processTexture(S32 texture_index, const std::string& t
|
|||
filename = filename.substr(pos + 1);
|
||||
}
|
||||
|
||||
std::string dir = gDirUtilp->getDirName(mFilename);
|
||||
std::string full_path = dir + gDirUtilp->getDirDelimiter() + filename;
|
||||
if (!gDirUtilp->fileExists(full_path) && filename.find("data:") == std::string::npos)
|
||||
{
|
||||
// Uri might be escaped
|
||||
filename = LLURI::unescape(filename);
|
||||
}
|
||||
|
||||
LL_INFOS("GLTF_IMPORT") << "Found texture: " << filename << " for material: " << material_name << LL_ENDL;
|
||||
|
||||
LLSD args;
|
||||
|
|
@ -1810,13 +1821,13 @@ size_t LLGLTFLoader::getSuffixPosition(const std::string &label)
|
|||
return -1;
|
||||
}
|
||||
|
||||
std::string LLGLTFLoader::getLodlessLabel(const LL::GLTF::Mesh& mesh)
|
||||
std::string LLGLTFLoader::getLodlessLabel(const LL::GLTF::Node& node)
|
||||
{
|
||||
size_t ext_pos = getSuffixPosition(mesh.mName);
|
||||
size_t ext_pos = getSuffixPosition(node.mName);
|
||||
if (ext_pos != -1)
|
||||
{
|
||||
return mesh.mName.substr(0, ext_pos);
|
||||
return node.mName.substr(0, ext_pos);
|
||||
}
|
||||
return mesh.mName;
|
||||
return node.mName;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ private:
|
|||
void notifyUnsupportedExtension(bool unsupported);
|
||||
|
||||
static size_t getSuffixPosition(const std::string& label);
|
||||
static std::string getLodlessLabel(const LL::GLTF::Mesh& mesh);
|
||||
static std::string getLodlessLabel(const LL::GLTF::Node& mesh);
|
||||
|
||||
// bool mPreprocessGLTF;
|
||||
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ std::map<S32, std::string> LLTeleportRequest::sTeleportStatusName = { { kPending
|
|||
class LLTeleportRequestViaLandmark : public LLTeleportRequest
|
||||
{
|
||||
public:
|
||||
LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId);
|
||||
LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId, bool log = true);
|
||||
virtual ~LLTeleportRequestViaLandmark();
|
||||
|
||||
virtual void toOstream(std::ostream& os) const;
|
||||
|
|
@ -179,6 +179,7 @@ public:
|
|||
|
||||
protected:
|
||||
inline const LLUUID &getLandmarkId() const {return mLandmarkId;};
|
||||
bool mLogOnDestruction = true;
|
||||
|
||||
private:
|
||||
LLUUID mLandmarkId;
|
||||
|
|
@ -5008,16 +5009,25 @@ void LLTeleportRequest::toOstream(std::ostream& os) const
|
|||
//-----------------------------------------------------------------------------
|
||||
// LLTeleportRequestViaLandmark
|
||||
//-----------------------------------------------------------------------------
|
||||
LLTeleportRequestViaLandmark::LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId)
|
||||
: LLTeleportRequest(),
|
||||
mLandmarkId(pLandmarkId)
|
||||
LLTeleportRequestViaLandmark::LLTeleportRequestViaLandmark(const LLUUID &pLandmarkId, bool log)
|
||||
: LLTeleportRequest()
|
||||
, mLandmarkId(pLandmarkId)
|
||||
, mLogOnDestruction(true)
|
||||
{
|
||||
LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark created, " << *this << LL_ENDL;
|
||||
if (log)
|
||||
{
|
||||
// Workaround to not log twice for LLTeleportRequestViaLure, besides this wouldn't have logged fully.
|
||||
LL_INFOS("Teleport") << "LLTeleportRequestViaLandmark created, " << *this << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
LLTeleportRequestViaLandmark::~LLTeleportRequestViaLandmark()
|
||||
{
|
||||
LL_INFOS("Teleport") << "~LLTeleportRequestViaLandmark, " << *this << LL_ENDL;
|
||||
if (mLogOnDestruction)
|
||||
{
|
||||
// Workaround to not crash on toOstream for derived classes and to not log twice.
|
||||
LL_INFOS("Teleport") << "~LLTeleportRequestViaLandmark, " << *this << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void LLTeleportRequestViaLandmark::toOstream(std::ostream& os) const
|
||||
|
|
@ -5047,16 +5057,20 @@ void LLTeleportRequestViaLandmark::restartTeleport()
|
|||
// LLTeleportRequestViaLure
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
LLTeleportRequestViaLure::LLTeleportRequestViaLure(const LLUUID &pLureId, bool pIsLureGodLike)
|
||||
: LLTeleportRequestViaLandmark(pLureId),
|
||||
LLTeleportRequestViaLure::LLTeleportRequestViaLure(const LLUUID& pLureId, bool pIsLureGodLike)
|
||||
: LLTeleportRequestViaLandmark(pLureId, false),
|
||||
mIsLureGodLike(pIsLureGodLike)
|
||||
{
|
||||
LL_INFOS("Teleport") << "LLTeleportRequestViaLure created" << LL_ENDL;
|
||||
LL_INFOS("Teleport") << "LLTeleportRequestViaLure created: " << *this << LL_ENDL;
|
||||
}
|
||||
|
||||
LLTeleportRequestViaLure::~LLTeleportRequestViaLure()
|
||||
{
|
||||
LL_INFOS("Teleport") << "~LLTeleportRequestViaLure" << LL_ENDL;
|
||||
if (mLogOnDestruction)
|
||||
{
|
||||
LL_INFOS("Teleport") << "~LLTeleportRequestViaLure: " << *this << LL_ENDL;
|
||||
mLogOnDestruction = false;
|
||||
}
|
||||
}
|
||||
|
||||
void LLTeleportRequestViaLure::toOstream(std::ostream& os) const
|
||||
|
|
|
|||
|
|
@ -538,6 +538,27 @@ LLInventoryItem* LLAgentWearables::getWearableInventoryItem(LLWearableType::ETyp
|
|||
return item;
|
||||
}
|
||||
|
||||
const S32 LLAgentWearables::getWearableIdxFromItem(const LLViewerInventoryItem* item) const
|
||||
{
|
||||
if (!item) return -1;
|
||||
if (!item->isWearableType()) return -1;
|
||||
|
||||
LLWearableType::EType type = item->getWearableType();
|
||||
U32 wearable_count = getWearableCount(type);
|
||||
if (0 == wearable_count) return -1;
|
||||
|
||||
const LLUUID& asset_id = item->getAssetUUID();
|
||||
|
||||
for (U32 i = 0; i < wearable_count; ++i)
|
||||
{
|
||||
const LLViewerWearable* wearable = getViewerWearable(type, i);
|
||||
if (!wearable) continue;
|
||||
if (wearable->getAssetID() != asset_id) continue;
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
const LLViewerWearable* LLAgentWearables::getWearableFromItemID(const LLUUID& item_id) const
|
||||
{
|
||||
const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
|
||||
|
|
@ -1471,7 +1492,7 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos
|
|||
|
||||
LLWearableType::EType type = item->getWearableType();
|
||||
U32 wearable_count = getWearableCount(type);
|
||||
if (0 == wearable_count) return false;
|
||||
if (wearable_count < 2) return false;
|
||||
|
||||
const LLUUID& asset_id = item->getAssetUUID();
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ public:
|
|||
public:
|
||||
const LLUUID getWearableItemID(LLWearableType::EType type, U32 index /*= 0*/) const;
|
||||
const LLUUID getWearableAssetID(LLWearableType::EType type, U32 index /*= 0*/) const;
|
||||
const S32 getWearableIdxFromItem(const LLViewerInventoryItem* item) const;
|
||||
const LLViewerWearable* getWearableFromItemID(const LLUUID& item_id) const;
|
||||
LLViewerWearable* getWearableFromItemID(const LLUUID& item_id);
|
||||
LLViewerWearable* getWearableFromAssetID(const LLUUID& asset_id);
|
||||
|
|
|
|||
|
|
@ -57,42 +57,42 @@
|
|||
|
||||
- (void) applicationDidFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
// Call constructViewer() first so our logging subsystem is in place. This
|
||||
// risks missing crashes in the LLAppViewerMacOSX constructor, but for
|
||||
// present purposes it's more important to get the startup sequence
|
||||
// properly logged.
|
||||
// Someday I would like to modify the logging system so that calls before
|
||||
// it's initialized are cached in a std::ostringstream and then, once it's
|
||||
// initialized, "played back" into whatever handlers have been set up.
|
||||
constructViewer();
|
||||
// Call constructViewer() first so our logging subsystem is in place. This
|
||||
// risks missing crashes in the LLAppViewerMacOSX constructor, but for
|
||||
// present purposes it's more important to get the startup sequence
|
||||
// properly logged.
|
||||
// Someday I would like to modify the logging system so that calls before
|
||||
// it's initialized are cached in a std::ostringstream and then, once it's
|
||||
// initialized, "played back" into whatever handlers have been set up.
|
||||
constructViewer();
|
||||
|
||||
#if defined(LL_BUGSPLAT)
|
||||
infos("bugsplat setup");
|
||||
// Engage BugsplatStartupManager *before* calling initViewer() to handle
|
||||
// any crashes during initialization.
|
||||
// https://www.bugsplat.com/docs/platforms/os-x#initialization
|
||||
[BugsplatStartupManager sharedManager].autoSubmitCrashReport = YES;
|
||||
[BugsplatStartupManager sharedManager].askUserDetails = NO;
|
||||
[BugsplatStartupManager sharedManager].delegate = self;
|
||||
[[BugsplatStartupManager sharedManager] start];
|
||||
// Engage BugsplatStartupManager *before* calling initViewer() to handle
|
||||
// any crashes during initialization.
|
||||
// https://www.bugsplat.com/docs/platforms/os-x#initialization
|
||||
[BugsplatStartupManager sharedManager].autoSubmitCrashReport = YES;
|
||||
[BugsplatStartupManager sharedManager].askUserDetails = NO;
|
||||
[BugsplatStartupManager sharedManager].delegate = self;
|
||||
[[BugsplatStartupManager sharedManager] start];
|
||||
#endif
|
||||
infos("post-bugsplat setup");
|
||||
|
||||
frameTimer = nil;
|
||||
frameTimer = nil;
|
||||
|
||||
[self languageUpdated];
|
||||
[self languageUpdated];
|
||||
|
||||
if (initViewer())
|
||||
{
|
||||
// Set up recurring calls to oneFrame (repeating timer with timeout 0)
|
||||
// until applicationShouldTerminate.
|
||||
frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self
|
||||
selector:@selector(oneFrame) userInfo:nil repeats:YES];
|
||||
} else {
|
||||
exit(0);
|
||||
}
|
||||
if (initViewer())
|
||||
{
|
||||
// Set up recurring calls to oneFrame (repeating timer with timeout 0)
|
||||
// until applicationShouldTerminate.
|
||||
frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self
|
||||
selector:@selector(oneFrame) userInfo:nil repeats:YES];
|
||||
} else {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languageUpdated) name:@"NSTextInputContextKeyboardSelectionDidChangeNotification" object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languageUpdated) name:@"NSTextInputContextKeyboardSelectionDidChangeNotification" object:nil];
|
||||
|
||||
// [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
|
||||
}
|
||||
|
|
@ -110,74 +110,74 @@
|
|||
|
||||
- (void) applicationDidBecomeActive:(NSNotification *)notification
|
||||
{
|
||||
callWindowFocus();
|
||||
callWindowFocus();
|
||||
}
|
||||
|
||||
- (void) applicationDidResignActive:(NSNotification *)notification
|
||||
{
|
||||
callWindowUnfocus();
|
||||
callWindowUnfocus();
|
||||
}
|
||||
|
||||
- (void) applicationDidHide:(NSNotification *)notification
|
||||
{
|
||||
callWindowHide();
|
||||
callWindowHide();
|
||||
}
|
||||
|
||||
- (void) applicationDidUnhide:(NSNotification *)notification
|
||||
{
|
||||
callWindowUnhide();
|
||||
callWindowUnhide();
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply) applicationShouldTerminate:(NSApplication *)sender
|
||||
{
|
||||
// run one frame to assess state
|
||||
if (!pumpMainLoop())
|
||||
{
|
||||
// pumpMainLoop() returns true when done, false if it wants to be
|
||||
// called again. Since it returned false, do not yet cancel
|
||||
// frameTimer.
|
||||
handleQuit();
|
||||
[[NSApplication sharedApplication] stopModal];
|
||||
return NSTerminateCancel;
|
||||
} else {
|
||||
// pumpMainLoop() returned true: it's done. Okay, done with frameTimer.
|
||||
[frameTimer release];
|
||||
cleanupViewer();
|
||||
return NSTerminateNow;
|
||||
}
|
||||
// run one frame to assess state
|
||||
if (!pumpMainLoop())
|
||||
{
|
||||
// pumpMainLoop() returns true when done, false if it wants to be
|
||||
// called again. Since it returned false, do not yet cancel
|
||||
// frameTimer.
|
||||
handleQuit();
|
||||
[[NSApplication sharedApplication] stopModal];
|
||||
return NSTerminateCancel;
|
||||
} else {
|
||||
// pumpMainLoop() returned true: it's done. Okay, done with frameTimer.
|
||||
[frameTimer release];
|
||||
cleanupViewer();
|
||||
return NSTerminateNow;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) oneFrame
|
||||
{
|
||||
bool appExiting = pumpMainLoop();
|
||||
if (appExiting)
|
||||
{
|
||||
// Once pumpMainLoop() reports that we're done, cancel frameTimer:
|
||||
// stop the repetitive calls.
|
||||
[frameTimer release];
|
||||
[[NSApplication sharedApplication] terminate:self];
|
||||
}
|
||||
bool appExiting = pumpMainLoop();
|
||||
if (appExiting)
|
||||
{
|
||||
// Once pumpMainLoop() reports that we're done, cancel frameTimer:
|
||||
// stop the repetitive calls.
|
||||
[frameTimer release];
|
||||
[[NSApplication sharedApplication] terminate:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) showInputWindow:(bool)show withEvent:(NSEvent*)textEvent
|
||||
{
|
||||
if (![self romanScript])
|
||||
{
|
||||
if (show)
|
||||
{
|
||||
NSLog(@"Showing input window.");
|
||||
[inputWindow makeKeyAndOrderFront:inputWindow];
|
||||
if (![self romanScript])
|
||||
{
|
||||
if (show)
|
||||
{
|
||||
NSLog(@"Showing input window.");
|
||||
[inputWindow makeKeyAndOrderFront:inputWindow];
|
||||
if (textEvent != nil)
|
||||
{
|
||||
[[inputView inputContext] discardMarkedText];
|
||||
[[inputView inputContext] handleEvent:textEvent];
|
||||
}
|
||||
} else {
|
||||
NSLog(@"Hiding input window.");
|
||||
[inputWindow orderOut:inputWindow];
|
||||
[window makeKeyAndOrderFront:window];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NSLog(@"Hiding input window.");
|
||||
[inputWindow orderOut:inputWindow];
|
||||
[window makeKeyAndOrderFront:window];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This will get called multiple times by NSNotificationCenter.
|
||||
|
|
@ -187,15 +187,15 @@
|
|||
|
||||
- (void) languageUpdated
|
||||
{
|
||||
TISInputSourceRef currentInput = TISCopyCurrentKeyboardInputSource();
|
||||
CFArrayRef languages = (CFArrayRef)TISGetInputSourceProperty(currentInput, kTISPropertyInputSourceLanguages);
|
||||
|
||||
TISInputSourceRef currentInput = TISCopyCurrentKeyboardInputSource();
|
||||
CFArrayRef languages = (CFArrayRef)TISGetInputSourceProperty(currentInput, kTISPropertyInputSourceLanguages);
|
||||
|
||||
#if 0 // In the event of ever needing to add new language sources, change this to 1 and watch the terminal for "languages:"
|
||||
NSLog(@"languages: %@", TISGetInputSourceProperty(currentInput, kTISPropertyInputSourceLanguages));
|
||||
NSLog(@"languages: %@", TISGetInputSourceProperty(currentInput, kTISPropertyInputSourceLanguages));
|
||||
#endif
|
||||
|
||||
// Typically the language we want is going to be the very first result in the array.
|
||||
currentInputLanguage = (NSString*)CFArrayGetValueAtIndex(languages, 0);
|
||||
|
||||
// Typically the language we want is going to be the very first result in the array.
|
||||
currentInputLanguage = (NSString*)CFArrayGetValueAtIndex(languages, 0);
|
||||
}
|
||||
|
||||
- (bool) romanScript
|
||||
|
|
@ -209,7 +209,7 @@
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -313,11 +313,11 @@ struct AttachmentInfo
|
|||
|
||||
// We "happen to know" that info[0].basename is "SecondLife.old" -- due to
|
||||
// the fact that BugsplatMac only notices a crash during the viewer run
|
||||
// following the crash.
|
||||
// following the crash.
|
||||
// The Bugsplat service doesn't respect the MIME type above when returning
|
||||
// the log data to a browser, so take this opportunity to rename the file
|
||||
// from <base>.old to <base>_log.txt
|
||||
info[0].basename =
|
||||
info[0].basename =
|
||||
boost::filesystem::path(info[0].pathname).stem().string() + "_log.txt";
|
||||
infos("attachmentsForBugsplatStartupManager attaching log " + info[0].basename);
|
||||
|
||||
|
|
@ -373,7 +373,7 @@ struct AttachmentInfo
|
|||
{
|
||||
[super sendEvent:event];
|
||||
if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand))
|
||||
{
|
||||
{
|
||||
[[self keyWindow] sendEvent:event];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1500,6 +1500,27 @@ void wear_on_avatar_cb(const LLUUID& inv_item, bool do_replace = false)
|
|||
}
|
||||
}
|
||||
|
||||
bool needs_to_replace(LLViewerInventoryItem* item_to_wear, bool & first_for_object, std::vector<bool>& first_for_type, bool replace)
|
||||
{
|
||||
bool res = false;
|
||||
LLAssetType::EType type = item_to_wear->getType();
|
||||
if (type == LLAssetType::AT_OBJECT)
|
||||
{
|
||||
res = first_for_object && replace;
|
||||
first_for_object = false;
|
||||
}
|
||||
else if (type == LLAssetType::AT_CLOTHING)
|
||||
{
|
||||
LLWearableType::EType wtype = item_to_wear->getWearableType();
|
||||
if (wtype >= 0 && wtype < LLWearableType::WT_COUNT)
|
||||
{
|
||||
res = first_for_type[wtype] && replace;
|
||||
first_for_type[wtype] = false;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
|
||||
bool do_update,
|
||||
bool replace,
|
||||
|
|
@ -1508,7 +1529,8 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
|
|||
LL_DEBUGS("UIUsage") << "wearItemsOnAvatar" << LL_ENDL;
|
||||
LLUIUsage::instance().logCommand("Avatar.WearItem");
|
||||
|
||||
bool first = true;
|
||||
bool first_for_object = true;
|
||||
std::vector<bool> first_for_type(LLWearableType::WT_COUNT, true);
|
||||
|
||||
LLInventoryObject::const_object_list_t items_to_link;
|
||||
|
||||
|
|
@ -1516,9 +1538,6 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
|
|||
it != item_ids_to_wear.end();
|
||||
++it)
|
||||
{
|
||||
replace = first && replace;
|
||||
first = false;
|
||||
|
||||
const LLUUID& item_id_to_wear = *it;
|
||||
|
||||
if (item_id_to_wear.isNull())
|
||||
|
|
@ -1537,8 +1556,9 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
|
|||
if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID()))
|
||||
{
|
||||
LL_DEBUGS("Avatar") << "inventory item in library, will copy and wear "
|
||||
<< item_to_wear->getName() << " id " << item_id_to_wear << LL_ENDL;
|
||||
LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb,_1,replace));
|
||||
<< item_to_wear->getName() << " id " << item_id_to_wear << LL_ENDL;
|
||||
bool replace_item = needs_to_replace(item_to_wear, first_for_object, first_for_type, replace);
|
||||
LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb, _1, replace_item));
|
||||
copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(),
|
||||
item_to_wear->getUUID(), LLUUID::null, std::string(), cb);
|
||||
continue;
|
||||
|
|
@ -1576,7 +1596,8 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
|
|||
}
|
||||
LLWearableType::EType type = item_to_wear->getWearableType();
|
||||
S32 wearable_count = gAgentWearables.getWearableCount(type);
|
||||
if ((replace && wearable_count != 0) || !gAgentWearables.canAddWearable(type))
|
||||
bool replace_item = needs_to_replace(item_to_wear, first_for_object, first_for_type, replace);
|
||||
if ((replace_item && wearable_count != 0) || !gAgentWearables.canAddWearable(type))
|
||||
{
|
||||
LLUUID item_id = gAgentWearables.getWearableItemID(item_to_wear->getWearableType(),
|
||||
wearable_count-1);
|
||||
|
|
@ -1605,7 +1626,13 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear,
|
|||
|
||||
case LLAssetType::AT_OBJECT:
|
||||
{
|
||||
rez_attachment(item_to_wear, NULL, replace);
|
||||
// Note that this will replace only first attachment regardless of attachment point,
|
||||
// so if user is wearing two items over other two on different attachment points,
|
||||
// only one will be replaced.
|
||||
// Unfortunately we have no way to determine attachment point from inventory item.
|
||||
// We might want to forbid wearing multiple objects with replace option in future.
|
||||
bool replace_item = needs_to_replace(item_to_wear, first_for_object, first_for_type, replace);
|
||||
rez_attachment(item_to_wear, NULL, replace_item);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -4218,37 +4245,54 @@ bool LLAppearanceMgr::moveWearable(LLViewerInventoryItem* item, bool closer_to_b
|
|||
if (item->getType() != LLAssetType::AT_CLOTHING) return false;
|
||||
if (!gInventory.isObjectDescendentOf(item->getUUID(), getCOF())) return false;
|
||||
|
||||
S32 pos = gAgentWearables.getWearableIdxFromItem(item);
|
||||
if (pos < 0) return false; // Not found
|
||||
|
||||
U32 count = gAgentWearables.getWearableCount(item->getWearableType());
|
||||
if (count < 2) return false; // Nothing to swap with
|
||||
if (closer_to_body)
|
||||
{
|
||||
if (pos == 0) return false; // already first
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pos == count - 1) return false; // already last
|
||||
}
|
||||
|
||||
U32 old_pos = (U32)pos;
|
||||
U32 swap_with = closer_to_body ? old_pos - 1 : old_pos + 1;
|
||||
LLUUID swap_item_id = gAgentWearables.getWearableItemID(item->getWearableType(), swap_with);
|
||||
|
||||
// Find link item from item id.
|
||||
LLInventoryModel::cat_array_t cats;
|
||||
LLInventoryModel::item_array_t items;
|
||||
LLFindWearablesOfType filter_wearables_of_type(item->getWearableType());
|
||||
gInventory.collectDescendentsIf(getCOF(), cats, items, true, filter_wearables_of_type);
|
||||
if (items.empty()) return false;
|
||||
|
||||
// We assume that the items have valid descriptions.
|
||||
std::sort(items.begin(), items.end(), WearablesOrderComparator(item->getWearableType()));
|
||||
LLViewerInventoryItem* swap_item = nullptr;
|
||||
for (auto iter : items)
|
||||
{
|
||||
if (iter->getLinkedUUID() == swap_item_id)
|
||||
{
|
||||
swap_item = iter.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!swap_item)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (closer_to_body && items.front() == item) return false;
|
||||
if (!closer_to_body && items.back() == item) return false;
|
||||
// Description is supposed to hold sort index, but user could have changed
|
||||
// order rapidly and there might be a state mismatch between description
|
||||
// and gAgentWearables, trust gAgentWearables over description.
|
||||
// Generate new description.
|
||||
std::string new_desc = build_order_string(item->getWearableType(), old_pos);
|
||||
swap_item->setDescription(new_desc);
|
||||
new_desc = build_order_string(item->getWearableType(), swap_with);
|
||||
item->setDescription(new_desc);
|
||||
|
||||
LLInventoryModel::item_array_t::iterator it = std::find(items.begin(), items.end(), item);
|
||||
if (items.end() == it) return false;
|
||||
|
||||
|
||||
//swapping descriptions
|
||||
closer_to_body ? --it : ++it;
|
||||
LLViewerInventoryItem* swap_item = *it;
|
||||
if (!swap_item) return false;
|
||||
std::string tmp = swap_item->getActualDescription();
|
||||
swap_item->setDescription(item->getActualDescription());
|
||||
item->setDescription(tmp);
|
||||
|
||||
// LL_DEBUGS("Inventory") << "swap, item "
|
||||
// << ll_pretty_print_sd(item->asLLSD())
|
||||
// << " swap_item "
|
||||
// << ll_pretty_print_sd(swap_item->asLLSD()) << LL_ENDL;
|
||||
|
||||
// FIXME switch to use AISv3 where supported.
|
||||
//items need to be updated on a dataserver
|
||||
item->setComplete(true);
|
||||
item->updateServer(false);
|
||||
gInventory.updateItem(item);
|
||||
|
|
|
|||
|
|
@ -1265,6 +1265,7 @@ bool LLAppViewer::init()
|
|||
LLViewerCamera::createInstance();
|
||||
LL::GLTFSceneManager::createInstance();
|
||||
|
||||
gSavedSettings.setU32("DebugQualityPerformance", gSavedSettings.getU32("RenderQualityPerformance"));
|
||||
|
||||
#if LL_WINDOWS
|
||||
if (!mSecondInstance)
|
||||
|
|
@ -2397,7 +2398,6 @@ void LLAppViewer::initLoggingAndGetLastDuration()
|
|||
if (gDirUtilp->fileExists(user_data_path_cef_log))
|
||||
{
|
||||
std::string user_data_path_cef_old = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "cef.old");
|
||||
LLFile::remove(user_data_path_cef_old, ENOENT);
|
||||
LLFile::rename(user_data_path_cef_log, user_data_path_cef_old);
|
||||
}
|
||||
}
|
||||
|
|
@ -3944,8 +3944,15 @@ void LLAppViewer::processMarkerFiles()
|
|||
else if (marker_is_same_version)
|
||||
{
|
||||
// the file existed, is ours, and matched our version, so we can report on what it says
|
||||
LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found; last exec crashed" << LL_ENDL;
|
||||
LL_INFOS("MarkerFile") << "Exec marker '"<< mMarkerFileName << "' found; last exec crashed or froze" << LL_ENDL;
|
||||
#if LL_WINDOWS && LL_BUGSPLAT
|
||||
// bugsplat will set correct state in bugsplatSendLog
|
||||
// Might be more accurate to rename this one into 'unknown'
|
||||
gLastExecEvent = LAST_EXEC_FROZE;
|
||||
#else
|
||||
gLastExecEvent = LAST_EXEC_OTHER_CRASH;
|
||||
#endif // LL_WINDOWS
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -4194,7 +4201,7 @@ void LLAppViewer::earlyExit(const std::string& name, const LLSD& substitutions)
|
|||
// case where we need the viewer to exit without any need for notifications
|
||||
void LLAppViewer::earlyExitNoNotify()
|
||||
{
|
||||
LL_WARNS() << "app_early_exit with no notification: " << LL_ENDL;
|
||||
LL_WARNS() << "app_early_exit with no notification." << LL_ENDL;
|
||||
gDoDisconnect = true;
|
||||
finish_early_exit( LLSD(), LLSD() );
|
||||
}
|
||||
|
|
@ -4473,6 +4480,7 @@ void LLAppViewer::purgeCache()
|
|||
LLAppViewer::getTextureCache()->purgeCache(LL_PATH_CACHE);
|
||||
LLVOCache::getInstance()->removeCache(LL_PATH_CACHE);
|
||||
LLViewerShaderMgr::instance()->clearShaderCache();
|
||||
purgeCefStaleCaches();
|
||||
gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), "*");
|
||||
}
|
||||
|
||||
|
|
@ -5407,6 +5415,12 @@ void LLAppViewer::createErrorMarker(eLastExecEvent error_code) const
|
|||
}
|
||||
}
|
||||
|
||||
bool LLAppViewer::errorMarkerExists() const
|
||||
{
|
||||
std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME);
|
||||
return LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB);
|
||||
}
|
||||
|
||||
void LLAppViewer::outOfMemorySoftQuit()
|
||||
{
|
||||
if (!mQuitRequested)
|
||||
|
|
@ -5538,7 +5552,10 @@ void LLAppViewer::idleNetwork()
|
|||
add(LLStatViewer::NUM_NEW_OBJECTS, gObjectList.mNumNewObjects);
|
||||
|
||||
// Retransmit unacknowledged packets.
|
||||
gXferManager->retransmitUnackedPackets();
|
||||
if (gXferManager)
|
||||
{
|
||||
gXferManager->retransmitUnackedPackets();
|
||||
}
|
||||
gAssetStorage->checkForTimeouts();
|
||||
gViewerThrottle.setBufferLoadRate(gMessageSystem->getBufferLoadRate());
|
||||
gViewerThrottle.updateDynamicThrottle();
|
||||
|
|
|
|||
|
|
@ -149,6 +149,12 @@ public:
|
|||
std::string getWindowTitle() const; // The window display name.
|
||||
|
||||
void forceDisconnect(const std::string& msg); // Force disconnection, with a message to the user.
|
||||
|
||||
// sendSimpleLogoutRequest does not create a marker file.
|
||||
// Meant for lost network case, and for forced shutdowns,
|
||||
// to at least attempt to remove the ghost from the world.
|
||||
void sendSimpleLogoutRequest();
|
||||
|
||||
void badNetworkHandler(); // Cause a crash state due to bad network packet.
|
||||
|
||||
bool hasSavedFinalSnapshot() { return mSavedFinalSnapshot; }
|
||||
|
|
@ -245,6 +251,7 @@ public:
|
|||
|
||||
// Writes an error code into the error_marker file for use on next startup.
|
||||
void createErrorMarker(eLastExecEvent error_code) const;
|
||||
bool errorMarkerExists() const;
|
||||
|
||||
// Attempt a 'soft' quit with disconnect and saving of settings/cache.
|
||||
// Intended to be thread safe.
|
||||
|
|
@ -311,10 +318,6 @@ private:
|
|||
void sendLogoutRequest();
|
||||
void disconnectViewer();
|
||||
|
||||
// Does not create a marker file. For lost network case,
|
||||
// to at least attempt to remove the ghost from the world.
|
||||
void sendSimpleLogoutRequest();
|
||||
|
||||
// *FIX: the app viewer class should be some sort of singleton, no?
|
||||
// Perhaps its child class is the singleton and this should be an abstract base.
|
||||
static LLAppViewer* sInstance;
|
||||
|
|
|
|||
|
|
@ -5,27 +5,27 @@
|
|||
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, 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$
|
||||
*/
|
||||
*/
|
||||
|
||||
#if !defined LL_DARWIN
|
||||
#error "Use only with macOS"
|
||||
#error "Use only with macOS"
|
||||
#endif
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
|
|
|||
|
|
@ -172,6 +172,22 @@ namespace
|
|||
<< '/' << loc.mV[1]
|
||||
<< '/' << loc.mV[2])));
|
||||
}
|
||||
|
||||
LLAppViewer* app = LLAppViewer::instance();
|
||||
if (!app->isSecondInstance() && !app->errorMarkerExists())
|
||||
{
|
||||
// If marker doesn't exist, create a marker with 'other' code for next launch
|
||||
// otherwise don't override existing file
|
||||
// Any unmarked crashes will be considered as freezes
|
||||
if (app->logoutRequestSent())
|
||||
{
|
||||
app->createErrorMarker(LAST_EXEC_LOGOUT_CRASH);
|
||||
}
|
||||
else
|
||||
{
|
||||
app->createErrorMarker(LAST_EXEC_OTHER_CRASH);
|
||||
}
|
||||
}
|
||||
} // MDSCB_EXCEPTIONCODE
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -215,7 +215,8 @@ public:
|
|||
LLUUID obj_id = mObjectData["object_id"];
|
||||
if (obj_id.notNull())
|
||||
{
|
||||
return nullptr != gObjectList.findObject(mAvatarID);
|
||||
LLViewerObject* object = gObjectList.findObject(obj_id);
|
||||
return object && object->isReachable();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -441,6 +442,7 @@ public:
|
|||
time_t current_time = time_corrected();
|
||||
time_t message_time = (time_t)(current_time - LLFrameTimer::getElapsedSeconds() + mTime);
|
||||
|
||||
// Report abuse shouldn't use AM/PM, use 24-hour time
|
||||
time_string = "[" + LLTrans::getString("TimeMonth") + "]/["
|
||||
+ LLTrans::getString("TimeDay") + "]/["
|
||||
+ LLTrans::getString("TimeYear") + "] ["
|
||||
|
|
@ -1117,7 +1119,11 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p)
|
|||
mEditor = LLUICtrlFactory::create<LLTextEditor>(editor_params, this);
|
||||
mEditor->setIsFriendCallback(LLAvatarActions::isFriend);
|
||||
mEditor->setIsObjectBlockedCallback(boost::bind(&LLMuteList::isMuted, LLMuteList::getInstance(), _1, _2, 0));
|
||||
|
||||
mEditor->setIsObjectReachableCallback([](const LLUUID& obj_id)
|
||||
{
|
||||
LLViewerObject* object = gObjectList.findObject(obj_id);
|
||||
return object && object->isReachable();
|
||||
});
|
||||
}
|
||||
|
||||
LLSD LLChatHistory::getValue() const
|
||||
|
|
|
|||
|
|
@ -118,11 +118,21 @@ const std::string LLConversation::createTimestamp(const U64Seconds& utc_time)
|
|||
LLSD substitution;
|
||||
substitution["datetime"] = (S32)utc_time.value();
|
||||
|
||||
timeStr = "["+LLTrans::getString ("TimeMonth")+"]/["
|
||||
+LLTrans::getString ("TimeDay")+"]/["
|
||||
+LLTrans::getString ("TimeYear")+"] ["
|
||||
+LLTrans::getString ("TimeHour")+"]:["
|
||||
+LLTrans::getString ("TimeMin")+"]";
|
||||
static bool use_24h = gSavedSettings.getBOOL("Use24HourClock");
|
||||
timeStr = "[" + LLTrans::getString("TimeMonth") + "]/["
|
||||
+ LLTrans::getString("TimeDay") + "]/["
|
||||
+ LLTrans::getString("TimeYear") + "] [";
|
||||
if (use_24h)
|
||||
{
|
||||
timeStr += LLTrans::getString("TimeHour") + "]:["
|
||||
+ LLTrans::getString("TimeMin") + "]";
|
||||
}
|
||||
else
|
||||
{
|
||||
timeStr += LLTrans::getString("TimeHour12") + "]:["
|
||||
+ LLTrans::getString("TimeMin") + "] ["
|
||||
+ LLTrans::getString("TimeAMPM") + "]";
|
||||
}
|
||||
|
||||
|
||||
LLStringUtil::format (timeStr, substitution);
|
||||
|
|
|
|||
|
|
@ -802,7 +802,10 @@ void LLBumpImageList::onSourceStandardLoaded( bool success, LLViewerFetchedTextu
|
|||
}
|
||||
src_vi->setExplicitFormat(GL_RGBA, GL_RGBA);
|
||||
{
|
||||
src_vi->createGLTexture(src_vi->getDiscardLevel(), nrm_image);
|
||||
if (!src_vi->createGLTexture(src_vi->getDiscardLevel(), nrm_image))
|
||||
{
|
||||
LL_WARNS() << "Failed to create bump image texture for image " << src_vi->getID() << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -896,7 +899,10 @@ void LLBumpImageList::onSourceUpdated(LLViewerTexture* src, EBumpEffect bump_cod
|
|||
|
||||
LLImageGL* src_img = src->getGLTexture();
|
||||
LLImageGL* dst_img = bump->getGLTexture();
|
||||
dst_img->setSize(src->getWidth(), src->getHeight(), 4, 0);
|
||||
if (!dst_img->setSize(src->getWidth(), src->getHeight(), 4, 0))
|
||||
{
|
||||
LL_WARNS() << "Failed to setSize for image " << bump->getID() << LL_ENDL;
|
||||
}
|
||||
dst_img->setUseMipMaps(true);
|
||||
dst_img->setDiscardLevel(0);
|
||||
dst_img->createGLTexture();
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue