Merge branch 'master' of https://github.com/FirestormViewer/phoenix-firestorm into texture-changes-reverted

# Conflicts:
#	indra/newview/llviewerobject.cpp
master
Ansariel 2025-08-31 12:18:41 +02:00
commit 8383b89390
343 changed files with 8867 additions and 2434 deletions

View File

@ -35,12 +35,9 @@ jobs:
build_matrix:
strategy:
matrix:
os: [macos-15,ubuntu-24.04,windows-2022]
os: [macos-15,ubuntu-22.04,windows-2022]
grid: [sl,os]
variant: [regular, avx]
include:
- os: ubuntu-24.04
container_image: ubuntu:22.04 # only Linux uses a container
runs-on: ${{ matrix.os }}
container: ${{ matrix.container_image }}
outputs:
@ -128,17 +125,16 @@ jobs:
with:
swap-storage: false
- name: Install GCC-14
- name: Install GCC-13
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y software-properties-common
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt-get update
sudo apt-get install -y gcc-14 g++-14
echo "CC=gcc-14" >> $GITHUB_ENV
echo "CXX=g++-14" >> $GITHUB_ENV
sudo apt-get install -y gcc-13 g++-13
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 130 \
--slave /usr/bin/g++ g++ /usr/bin/g++-13
echo "CC=gcc-13" >> $GITHUB_ENV
echo "CXX=g++-13" >> $GITHUB_ENV
- name: Setup rclone and download the folder
uses: beqjanus/setup-rclone@main
with:

View File

@ -46,11 +46,10 @@ jobs:
runner: qa-dan-asus
artifact: Windows-installer
install-path: 'C:\viewer-automation-main'
# Commented out until mac runner is available
# - os: mac
# runner: qa-mac-atlas
# artifact: Mac-installer
# install-path: '$HOME/Documents/viewer-automation'
- os: mac
runner: qa-mac-atlas
artifact: macOS-installer
install-path: '$HOME/Documents/viewer-automation'
fail-fast: false
runs-on: [self-hosted, "${{ matrix.runner }}"]
@ -498,11 +497,13 @@ jobs:
mkdir -p "$MOUNT_POINT"
# Mount the DMG
hdiutil attach "${{ env.INSTALLER_PATH }}" -mountpoint "$MOUNT_POINT" -nobrowse
hdiutil attach "$INSTALLER_PATH" -mountpoint "$MOUNT_POINT" -nobrowse
echo "✅ DMG mounted at $MOUNT_POINT"
# Find the app in the mounted DMG
echo "Installing application to default location from DMG..."
# Find the .app bundle in the DMG
APP_PATH=$(find "$MOUNT_POINT" -name "*.app" -type d | head -1)
if [ -z "$APP_PATH" ]; then
@ -510,18 +511,40 @@ jobs:
exit 1
fi
echo "Installing application to Applications folder..."
APP_NAME=$(basename "$APP_PATH")
DEST_PATH="/Applications/$APP_NAME"
# Copy the app to the Applications folder (or specified install path)
cp -R "$APP_PATH" "${{ matrix.install-path }}"
# Handle existing installation
if [ -d "$DEST_PATH" ]; then
echo "Found existing installation at: $DEST_PATH"
echo "Moving existing installation to trash..."
# Move to trash instead of force removing
TRASH_PATH="$HOME/.Trash/$(date +%Y%m%d_%H%M%S)_$APP_NAME"
mv "$DEST_PATH" "$TRASH_PATH" || {
echo "⚠️ Could not move to trash, trying direct removal..."
rm -rf "$DEST_PATH" || {
echo "❌ Could not remove existing installation"
echo "Please manually remove: $DEST_PATH"
exit 1
}
}
echo "✅ Existing installation handled successfully"
fi
# Copy the .app to /Applications
echo "Copying app from: $APP_PATH"
echo "To destination: /Applications/"
cp -R "$APP_PATH" /Applications/
# Verify the app was copied successfully
if [ ! -d "${{ matrix.install-path }}/$(basename "$APP_PATH")" ]; then
echo "❌ Error: Failed to install application to ${{ matrix.install-path }}!"
if [ ! -d "$DEST_PATH" ]; then
echo "❌ Error: Failed to install application to /Applications!"
exit 1
fi
echo "✅ Application installed successfully to ${{ matrix.install-path }}"
echo "✅ Application installed successfully to /Applications"
# Save mount point for cleanup
echo "MOUNT_POINT=$MOUNT_POINT" >> $GITHUB_ENV

View File

@ -2649,23 +2649,17 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
</map>
<key>threejs</key>
<map>
<key>copyright</key>
<string>Copyright © 2010-2021 three.js authors</string>
<key>license</key>
<string>MIT</string>
<key>license_file</key>
<string>LICENSES/THREEJS_LICENSE.txt</string>
<key>name</key>
<string>threejs</string>
<key>platforms</key>
<map>
<key>darwin64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>cfed00d8ea7265c035c2d86a234b28efb0b23756</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
<string>https://github.com/secondlife/3p-three_js/releases/download/v0.132.2-b8f6746/threejs-0.132.2-darwin64-b8f6746.tar.zst</string>
</map>
<key>name</key>
<string>darwin64</string>
</map>
<key>linux64</key>
<key>common</key>
<map>
<key>archive</key>
<map>
@ -2677,33 +2671,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<string>https://github.com/secondlife/3p-three_js/releases/download/v0.132.2-5da28d9/threejs-0.132.2-common-8454371083.tar.zst</string>
</map>
<key>name</key>
<string>linux64</string>
</map>
<key>windows64</key>
<map>
<key>archive</key>
<map>
<key>hash</key>
<string>4141710fccbd1ea2b3b53d00e189bdfa2ee9d441</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
<string>https://github.com/secondlife/3p-three_js/releases/download/v0.132.2-b8f6746/threejs-0.132.2-windows64-b8f6746.tar.zst</string>
</map>
<key>name</key>
<string>windows64</string>
<string>common</string>
</map>
</map>
<key>license</key>
<string>MIT</string>
<key>license_file</key>
<string>LICENSES/THREEJS_LICENSE.txt</string>
<key>copyright</key>
<string>Copyright © 2010-2021 three.js authors</string>
<key>version</key>
<string>0.132.2</string>
<key>name</key>
<string>threejs</string>
</map>
<key>tinygltf</key>
<map>
@ -3215,6 +3187,64 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>version</key>
<string>1.0.9-5e8947c</string>
</map>
<key>discord_sdk</key>
<map>
<key>platforms</key>
<map>
<key>windows64</key>
<map>
<key>archive</key>
<map>
<key>creds</key>
<string>github</string>
<key>hash</key>
<string>e11571bf76b27d15c244069988ae372eaa5afae9</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
<string>https://api.github.com/repos/secondlife/3p-discord-sdk/releases/assets/279333720</string>
</map>
<key>name</key>
<string>windows64</string>
</map>
<key>darwin64</key>
<map>
<key>archive</key>
<map>
<key>creds</key>
<string>github</string>
<key>hash</key>
<string>dc21df8b051c425163acf3eff8f06e32f407c9e0</string>
<key>hash_algorithm</key>
<string>sha1</string>
<key>url</key>
<string>https://api.github.com/repos/secondlife/3p-discord-sdk/releases/assets/279333706</string>
</map>
<key>name</key>
<string>darwin64</string>
</map>
</map>
<key>license</key>
<string>discord_sdk</string>
<key>license_file</key>
<string>LICENSES/discord_sdk.txt</string>
<key>copyright</key>
<string>Discord Inc.</string>
<key>version</key>
<string>1.4.9649.16733550144</string>
<key>name</key>
<string>discord_sdk</string>
<key>vcs_branch</key>
<string>main</string>
<key>vcs_revision</key>
<string>ef5c7c4a490ceac2df2b2f046788b1daf1bbb392</string>
<key>vcs_url</key>
<string>https://github.com/secondlife/3p-discord-sdk</string>
<key>canonical_repo</key>
<string>https://github.com/secondlife/3p-discord-sdk</string>
<key>description</key>
<string>Discord Social SDK</string>
</map>
</map>
<key>package_description</key>
<map>
@ -3236,6 +3266,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<string>-DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE</string>
<string>-DROOT_PROJECT_NAME:STRING=SecondLife</string>
<string>-DINSTALL_PROPRIETARY=TRUE</string>
<string>-DUSE_OPENAL:BOOL=ON</string>
</array>
</map>
<key>build</key>
@ -3277,6 +3308,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<string>-DADDRESS_SIZE:STRING=$AUTOBUILD_ADDRSIZE</string>
<string>-DROOT_PROJECT_NAME:STRING=SecondLife</string>
<string>-DINSTALL_PROPRIETARY=TRUE</string>
<string>-DUSE_OPENAL:BOOL=ON</string>
</array>
</map>
<key>build</key>

View File

@ -19,7 +19,7 @@ set(cmake_SOURCE_FILES
Copy3rdPartyLibs.cmake
DBusGlib.cmake
DeploySharedLibs.cmake
Discord.cmake # <FS:LO> Discord rich presence
Discord.cmake
DragDrop.cmake
EXPAT.cmake
FindAutobuild.cmake

View File

@ -6,6 +6,9 @@
include(CMakeCopyIfDifferent)
include(Linking)
if (USE_DISCORD)
include(Discord)
endif ()
include(OPENAL)
include(FMODSTUDIO)
@ -83,6 +86,10 @@ if(WINDOWS)
endif(ADDRESS_SIZE EQUAL 32)
endif (USE_BUGSPLAT)
if (TARGET ll::discord_sdk)
list(APPEND release_files discord_partner_sdk.dll)
endif ()
set(release_files ${release_files} growl++.dll growl.dll )
if (TARGET ll::fmodstudio)
# fmodL is included for logging, only one should be picked by manifest
@ -198,6 +205,10 @@ elseif(DARWIN)
)
endif()
if (TARGET ll::discord_sdk)
list(APPEND release_files libdiscord_partner_sdk.dylib)
endif ()
if (TARGET ll::fmodstudio)
set(debug_files ${debug_files} libfmodL.dylib)
set(release_files ${release_files} libfmod.dylib)

View File

@ -1,5 +1,19 @@
# -*- cmake -*-
# <FS:Ansariel> Prefer FS-specific Discord implementation
#include(Prebuilt)
#
#include_guard()
#
#add_library(ll::discord_sdk INTERFACE IMPORTED)
#target_compile_definitions(ll::discord_sdk INTERFACE LL_DISCORD=1)
#
#use_prebuilt_binary(discord_sdk)
#
#target_include_directories(ll::discord_sdk SYSTEM INTERFACE ${LIBS_PREBUILT_DIR}/include/discord_sdk)
#target_link_libraries(ll::discord_sdk INTERFACE discord_partner_sdk)
# </FS:Ansariel>
include_guard()
add_library(fs::discord INTERFACE IMPORTED)

View File

@ -119,6 +119,8 @@ public:
virtual void addDebugText( const std::string& text ) = 0;
virtual std::string getDebugName() const { return getID().asString(); }
virtual const LLUUID& getID() const = 0;
//-------------------------------------------------------------------------
// End Interface

View File

@ -29,6 +29,7 @@
#include "llassettype.h"
#include "lldictionary.h"
#include "llmemory.h"
#include "llsd.h"
#include "llsingleton.h"
///----------------------------------------------------------------------------
@ -246,3 +247,19 @@ bool LLAssetType::lookupIsAssetIDKnowable(EType asset_type)
}
return false;
}
LLSD LLAssetType::getTypeNames()
{
LLSD type_names;
const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
for (S32 type = AT_TEXTURE; type < AT_COUNT; ++type)
{
const AssetEntry *entry = dict->lookup((LLAssetType::EType) type);
// skip llassettype_bad_lookup
if (entry)
{
type_names.append(entry->mTypeName);
}
}
return type_names;
}

View File

@ -165,6 +165,8 @@ public:
static bool lookupIsAssetFetchByIDAllowed(EType asset_type); // the asset allows direct download
static bool lookupIsAssetIDKnowable(EType asset_type); // asset data can be known by the viewer
static LLSD getTypeNames();
static const std::string BADLOOKUP;
protected:

View File

@ -638,6 +638,14 @@ public:
{
getCPUIDInfo();
uint64_t frequency = getSysctlInt64("hw.cpufrequency");
if (!frequency)
{
auto tbfrequency = getSysctlInt64("hw.tbfrequency");
struct clockinfo clockrate;
auto clockrate_len = sizeof(clockrate);
if (!sysctlbyname("kern.clockrate", &clockrate, &clockrate_len, NULL, 0))
frequency = tbfrequency * clockrate.hz;
}
setInfo(eFrequency, (F64)frequency / (F64)1000000);
}

View File

@ -556,6 +556,45 @@ LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter
} // namespace llsd
/*****************************************************************************
* LLSDParam<std::vector<T>>
*****************************************************************************/
// Given an LLSD array, return a const std::vector<T>&, where T is a type
// supported by LLSDParam. Bonus: if the LLSD value is actually a scalar,
// return a single-element vector containing the converted value.
template <typename T>
class LLSDParam<std::vector<T>>: public LLSDParamBase
{
public:
LLSDParam(const LLSD& array)
{
// treat undefined "array" as empty vector
if (array.isDefined())
{
// what if it's a scalar?
if (! array.isArray())
{
v.push_back(LLSDParam<T>(array));
}
else // really is an array
{
// reserve space for the array entries
v.reserve(array.size());
for (const auto& item : llsd::inArray(array))
{
v.push_back(LLSDParam<T>(item));
}
}
}
}
operator const std::vector<T>&() const { return v; }
private:
std::vector<T> v;
};
/*****************************************************************************
* toArray(), toMap()
*****************************************************************************/

View File

@ -55,7 +55,7 @@ namespace LL
* ThreadPool listens for application shutdown messages on the "LLApp"
* LLEventPump. Call close() to shut down this ThreadPool early.
*/
virtual void close();
void close();
std::string getName() const { return mName; }
size_t getWidth() const { return mThreads.size(); }
@ -122,7 +122,7 @@ namespace LL
size_t threads=1,
size_t capacity=1024*1024,
bool auto_shutdown = true):
ThreadPoolBase(name, threads, new queue_t(name, capacity), auto_shutdown)
ThreadPoolBase(name, threads, new queue_t(name, capacity, false), auto_shutdown)
{}
~ThreadPoolUsing() override {}

View File

@ -21,6 +21,7 @@
#include "llcoros.h"
#include LLCOROS_MUTEX_HEADER
#include "llerror.h"
#include "llevents.h"
#include "llexception.h"
#include "stringize.h"
@ -30,11 +31,38 @@ using Lock = LLCoros::LockType;
/*****************************************************************************
* WorkQueueBase
*****************************************************************************/
LL::WorkQueueBase::WorkQueueBase(const std::string& name):
super(makeName(name))
LL::WorkQueueBase::WorkQueueBase(const std::string& name, bool auto_shutdown)
: super(makeName(name))
{
// TODO: register for "LLApp" events so we can implicitly close() on
// viewer shutdown.
if (auto_shutdown)
{
// Register for "LLApp" events so we can implicitly close() on viewer shutdown
std::string listener_name = "WorkQueue:" + getKey();
LLEventPumps::instance().obtain("LLApp").listen(
listener_name,
[this](const LLSD& stat)
{
std::string status(stat["status"]);
if (status != "running")
{
// Viewer is shutting down, close this queue
LL_DEBUGS("WorkQueue") << getKey() << " closing on app shutdown" << LL_ENDL;
close();
}
return false;
});
// Store the listener name so we can unregister in the destructor
mListenerName = listener_name;
}
}
LL::WorkQueueBase::~WorkQueueBase()
{
if (!mListenerName.empty() && !LLEventPumps::wasDeleted())
{
LLEventPumps::instance().obtain("LLApp").stopListening(mListenerName);
}
}
void LL::WorkQueueBase::runUntilClose()
@ -220,8 +248,8 @@ void LL::WorkQueueBase::checkCoroutine(const std::string& method)
/*****************************************************************************
* WorkQueue
*****************************************************************************/
LL::WorkQueue::WorkQueue(const std::string& name, size_t capacity):
super(name),
LL::WorkQueue::WorkQueue(const std::string& name, size_t capacity, bool auto_shutdown):
super(name, auto_shutdown),
mQueue(capacity)
{
}
@ -269,8 +297,8 @@ bool LL::WorkQueue::tryPop_(Work& work)
/*****************************************************************************
* WorkSchedule
*****************************************************************************/
LL::WorkSchedule::WorkSchedule(const std::string& name, size_t capacity):
super(name),
LL::WorkSchedule::WorkSchedule(const std::string& name, size_t capacity, bool auto_shutdown):
super(name, auto_shutdown),
mQueue(capacity)
{
}

View File

@ -51,7 +51,9 @@ namespace LL
* You may omit the WorkQueueBase name, in which case a unique name is
* synthesized; for practical purposes that makes it anonymous.
*/
WorkQueueBase(const std::string& name);
WorkQueueBase(const std::string& name, bool auto_shutdown);
virtual ~WorkQueueBase();
/**
* Since the point of WorkQueue is to pass work to some other worker
@ -197,6 +199,9 @@ namespace LL
private:
virtual Work pop_() = 0;
virtual bool tryPop_(Work&) = 0;
// Name used for the LLApp event listener (empty if not registered)
std::string mListenerName;
};
/*****************************************************************************
@ -212,7 +217,7 @@ namespace LL
* You may omit the WorkQueue name, in which case a unique name is
* synthesized; for practical purposes that makes it anonymous.
*/
WorkQueue(const std::string& name = std::string(), size_t capacity=1024);
WorkQueue(const std::string& name = std::string(), size_t capacity=1024, bool auto_shutdown = true);
/**
* Since the point of WorkQueue is to pass work to some other worker
@ -282,7 +287,7 @@ namespace LL
* You may omit the WorkSchedule name, in which case a unique name is
* synthesized; for practical purposes that makes it anonymous.
*/
WorkSchedule(const std::string& name = std::string(), size_t capacity=1024);
WorkSchedule(const std::string& name = std::string(), size_t capacity=1024, bool auto_shutdown = true);
/**
* Since the point of WorkSchedule is to pass work to some other worker

View File

@ -558,6 +558,12 @@ bool LLImageBMP::encode(const LLImageRaw* raw_image, F32 encode_time)
LL_INFOS() << "Dropping alpha information during BMP encoding" << LL_ENDL;
}
if (raw_image->isBufferInvalid())
{
setLastError("Invalid input, no buffer");
return false;
}
setSize(raw_image->getWidth(), raw_image->getHeight(), dst_components);
U8 magic[14];

View File

@ -329,6 +329,12 @@ bool LLImageDXT::encodeDXT(const LLImageRaw* raw_image, F32 time, bool explicit_
{
llassert_always(raw_image);
if (raw_image->isBufferInvalid())
{
setLastError("Invalid input, no buffer");
return false;
}
S32 ncomponents = raw_image->getComponents();
EFileFormat format;
switch (ncomponents)

View File

@ -492,6 +492,12 @@ bool LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time )
resetLastError();
if (raw_image->isBufferInvalid())
{
setLastError("Invalid input, no buffer");
return false;
}
LLImageDataSharedLock lockIn(raw_image);
LLImageDataLock lockOut(this);

View File

@ -956,6 +956,12 @@ bool LLImageJ2COJ::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decod
bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time, bool reversible)
{
if (raw_image.isBufferInvalid())
{
base.setLastError("Invalid input, no buffer");
return false;
}
JPEG2KEncode encode(comment_text, reversible);
bool encoded = encode.encode(raw_image, base);
if (!encoded)

View File

@ -29,6 +29,7 @@
#include "llfoldertype.h"
#include "lldictionary.h"
#include "llmemory.h"
#include "llsd.h"
#include "llsingleton.h"
///----------------------------------------------------------------------------
@ -222,3 +223,21 @@ const std::string &LLFolderType::badLookup()
static const std::string sBadLookup = "llfoldertype_bad_lookup";
return sBadLookup;
}
LLSD LLFolderType::getTypeNames()
{
LLSD type_names;
for (S32 type = FT_TEXTURE; type < FT_COUNT; ++type)
{
if (lookupIsEnsembleType((LLFolderType::EType)type))
continue;
const FolderEntry* entry = LLFolderDictionary::getInstance()->lookup((LLFolderType::EType)type);
// skip llfoldertype_bad_lookup
if (entry)
{
type_names.append(entry->mName);
}
}
return type_names;
}

View File

@ -123,6 +123,8 @@ public:
static const std::string& badLookup(); // error string when a lookup fails
static LLSD getTypeNames();
protected:
LLFolderType() {}
~LLFolderType() {}

View File

@ -46,6 +46,7 @@ static const std::string INV_ITEM_ID_LABEL("item_id");
static const std::string INV_FOLDER_ID_LABEL("cat_id");
static const std::string INV_PARENT_ID_LABEL("parent_id");
static const std::string INV_THUMBNAIL_LABEL("thumbnail");
static const std::string INV_FAVORITE_LABEL("favorite");
static const std::string INV_THUMBNAIL_ID_LABEL("thumbnail_id");
static const std::string INV_ASSET_TYPE_LABEL("type");
static const std::string INV_PREFERRED_TYPE_LABEL("preferred_type");
@ -59,6 +60,7 @@ static const std::string INV_LINKED_ID_LABEL("linked_id");
static const std::string INV_SALE_INFO_LABEL("sale_info");
static const std::string INV_FLAGS_LABEL("flags");
static const std::string INV_CREATION_DATE_LABEL("created_at");
static const std::string INV_TOGGLED_LABEL("toggled");
// key used by agent-inventory-service
static const std::string INV_ASSET_TYPE_LABEL_WS("type_default");
@ -82,14 +84,16 @@ LLInventoryObject::LLInventoryObject(const LLUUID& uuid,
mParentUUID(parent_uuid),
mType(type),
mName(name),
mCreationDate(0)
mCreationDate(0),
mFavorite(false)
{
correctInventoryName(mName);
}
LLInventoryObject::LLInventoryObject()
: mType(LLAssetType::AT_NONE),
mCreationDate(0)
mCreationDate(0),
mFavorite(false)
{
}
@ -104,6 +108,7 @@ void LLInventoryObject::copyObject(const LLInventoryObject* other)
mType = other->mType;
mName = other->mName;
mThumbnailUUID = other->mThumbnailUUID;
mFavorite = other->mFavorite;
}
const LLUUID& LLInventoryObject::getUUID() const
@ -121,6 +126,11 @@ const LLUUID& LLInventoryObject::getThumbnailUUID() const
return mThumbnailUUID;
}
bool LLInventoryObject::getIsFavorite() const
{
return mFavorite;
}
const std::string& LLInventoryObject::getName() const
{
return mName;
@ -175,6 +185,11 @@ void LLInventoryObject::setThumbnailUUID(const LLUUID& thumbnail_uuid)
mThumbnailUUID = thumbnail_uuid;
}
void LLInventoryObject::setFavorite(bool favorite)
{
mFavorite = favorite;
}
void LLInventoryObject::setType(LLAssetType::EType type)
{
mType = type;
@ -247,6 +262,23 @@ bool LLInventoryObject::importLegacyStream(std::istream& input_stream)
{
setThumbnailUUID(LLUUID::null);
}
if (metadata.has("favorite"))
{
const LLSD& favorite = metadata["favorite"];
if (favorite.has("toggled"))
{
setFavorite(favorite["toggled"].asBoolean());
}
else
{
setFavorite(false);
}
}
else
{
setFavorite(false);
}
}
else if(0 == strcmp("name", keyword))
{
@ -813,6 +845,23 @@ bool LLInventoryItem::importLegacyStream(std::istream& input_stream)
{
setThumbnailUUID(LLUUID::null);
}
if (metadata.has("favorite"))
{
const LLSD& favorite = metadata["favorite"];
if (favorite.has("toggled"))
{
setFavorite(favorite["toggled"].asBoolean());
}
else
{
setFavorite(false);
}
}
else
{
setFavorite(false);
}
}
else if(0 == strcmp("inv_type", keyword))
{
@ -957,7 +1006,7 @@ bool LLInventoryItem::exportLegacyStream(std::ostream& output_stream, bool inclu
LLSD LLInventoryItem::asLLSD() const
{
LLSD sd = LLSD();
LLSD sd;
asLLSD(sd);
return sd;
}
@ -966,13 +1015,18 @@ void LLInventoryItem::asLLSD( LLSD& sd ) const
{
sd[INV_ITEM_ID_LABEL] = mUUID;
sd[INV_PARENT_ID_LABEL] = mParentUUID;
sd[INV_PERMISSIONS_LABEL] = ll_create_sd_from_permissions(mPermissions);
ll_fill_sd_from_permissions(sd[INV_PERMISSIONS_LABEL], mPermissions);
if (mThumbnailUUID.notNull())
{
sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID);
}
if (mFavorite)
{
sd[INV_FAVORITE_LABEL] = LLSD().with(INV_TOGGLED_LABEL, mFavorite);
}
U32 mask = mPermissions.getMaskBase();
if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
|| (mAssetUUID.isNull()))
@ -987,19 +1041,22 @@ void LLInventoryItem::asLLSD( LLSD& sd ) const
cipher.encrypt(shadow_id.mData, UUID_BYTES);
sd[INV_SHADOW_ID_LABEL] = shadow_id;
}
sd[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(mType);
sd[INV_INVENTORY_TYPE_LABEL] = mInventoryType;
sd[INV_ASSET_TYPE_LABEL] = std::string(LLAssetType::lookup(mType));
const std::string inv_type_str = LLInventoryType::lookup(mInventoryType);
if(!inv_type_str.empty())
{
sd[INV_INVENTORY_TYPE_LABEL] = inv_type_str;
}
else
{
sd[INV_INVENTORY_TYPE_LABEL] = (LLSD::Integer)mInventoryType;
}
//sd[INV_FLAGS_LABEL] = (S32)mFlags;
sd[INV_FLAGS_LABEL] = ll_sd_from_U32(mFlags);
sd[INV_SALE_INFO_LABEL] = mSaleInfo.asLLSD();
mSaleInfo.asLLSD(sd[INV_SALE_INFO_LABEL]);
sd[INV_NAME_LABEL] = mName;
sd[INV_DESC_LABEL] = mDescription;
sd[INV_CREATION_DATE_LABEL] = (S32) mCreationDate;
sd[INV_CREATION_DATE_LABEL] = (LLSD::Integer)mCreationDate;
}
bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
@ -1015,6 +1072,8 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
// TODO - figure out if this should be moved into the noclobber fields above
mThumbnailUUID.setNull();
mFavorite = false;
mPermissions.init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null);
// iterate as map to avoid making unnecessary temp copies of everything
LLSD::map_const_iterator i, end;
@ -1060,9 +1119,20 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new)
continue;
}
if (i->first == INV_FAVORITE_LABEL)
{
const LLSD& favorite_map = i->second;
const std::string w = INV_TOGGLED_LABEL;
if (favorite_map.has(w))
{
mFavorite = favorite_map[w].asBoolean();
}
continue;
}
if (i->first == INV_PERMISSIONS_LABEL)
{
mPermissions = ll_permissions_from_sd(i->second);
mPermissions.importLLSD(i->second);
continue;
}
@ -1255,6 +1325,11 @@ LLSD LLInventoryCategory::asLLSD() const
sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID);
}
if (mFavorite)
{
sd[INV_FAVORITE_LABEL] = LLSD().with(INV_TOGGLED_LABEL, mFavorite);
}
return sd;
}
@ -1266,11 +1341,17 @@ LLSD LLInventoryCategory::asAISCreateCatLLSD() const
S8 type = static_cast<S8>(mPreferredType);
sd[INV_ASSET_TYPE_LABEL_WS] = type;
sd[INV_NAME_LABEL] = mName;
if (mThumbnailUUID.notNull())
{
sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID);
}
if (mFavorite)
{
sd[INV_FAVORITE_LABEL] = LLSD().with(INV_TOGGLED_LABEL, mFavorite);
}
return sd;
}
@ -1318,6 +1399,17 @@ bool LLInventoryCategory::fromLLSD(const LLSD& sd)
mThumbnailUUID = sd[w];
}
}
mFavorite = false;
w = INV_FAVORITE_LABEL;
if (sd.has(w))
{
const LLSD& favorite_map = sd[w];
w = INV_TOGGLED_LABEL;
if (favorite_map.has(w))
{
mFavorite = favorite_map[w].asBoolean();
}
}
w = INV_ASSET_TYPE_LABEL;
if (sd.has(w))
{
@ -1440,6 +1532,23 @@ bool LLInventoryCategory::importLegacyStream(std::istream& input_stream)
{
setThumbnailUUID(LLUUID::null);
}
if (metadata.has("favorite"))
{
const LLSD& favorite = metadata["favorite"];
if (favorite.has("toggled"))
{
setFavorite(favorite["toggled"].asBoolean());
}
else
{
setFavorite(false);
}
}
else
{
setFavorite(false);
}
}
else
{
@ -1474,12 +1583,11 @@ bool LLInventoryCategory::exportLegacyStream(std::ostream& output_stream, bool)
return true;
}
LLSD LLInventoryCategory::exportLLSD() const
void LLInventoryCategory::exportLLSD(LLSD& cat_data) const
{
LLSD cat_data;
cat_data[INV_FOLDER_ID_LABEL] = mUUID;
cat_data[INV_PARENT_ID_LABEL] = mParentUUID;
cat_data[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(mType);
cat_data[INV_ASSET_TYPE_LABEL] = std::string(LLAssetType::lookup(mType));
cat_data[INV_PREFERRED_TYPE_LABEL] = LLFolderType::lookup(mPreferredType);
cat_data[INV_NAME_LABEL] = mName;
@ -1487,49 +1595,76 @@ LLSD LLInventoryCategory::exportLLSD() const
{
cat_data[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID);
}
return cat_data;
if (mFavorite)
{
cat_data[INV_FAVORITE_LABEL] = LLSD().with(INV_TOGGLED_LABEL, mFavorite);
}
}
bool LLInventoryCategory::importLLSD(const LLSD& cat_data)
bool LLInventoryCategory::importLLSDMap(const LLSD& cat_data)
{
if (cat_data.has(INV_FOLDER_ID_LABEL))
LLSD::map_const_iterator i, end;
end = cat_data.endMap();
for ( i = cat_data.beginMap(); i != end; ++i)
{
setUUID(cat_data[INV_FOLDER_ID_LABEL].asUUID());
importLLSD(i->first, i->second);
}
if (cat_data.has(INV_PARENT_ID_LABEL))
{
setParent(cat_data[INV_PARENT_ID_LABEL].asUUID());
}
if (cat_data.has(INV_ASSET_TYPE_LABEL))
{
setType(LLAssetType::lookup(cat_data[INV_ASSET_TYPE_LABEL].asString()));
}
if (cat_data.has(INV_PREFERRED_TYPE_LABEL))
{
setPreferredType(LLFolderType::lookup(cat_data[INV_PREFERRED_TYPE_LABEL].asString()));
}
if (cat_data.has(INV_THUMBNAIL_LABEL))
{
LLUUID thumbnail_uuid;
const LLSD &thumbnail_data = cat_data[INV_THUMBNAIL_LABEL];
if (thumbnail_data.has(INV_ASSET_ID_LABEL))
{
thumbnail_uuid = thumbnail_data[INV_ASSET_ID_LABEL].asUUID();
}
setThumbnailUUID(thumbnail_uuid);
}
if (cat_data.has(INV_NAME_LABEL))
{
mName = cat_data[INV_NAME_LABEL].asString();
LLStringUtil::replaceNonstandardASCII(mName, ' ');
LLStringUtil::replaceChar(mName, '|', ' ');
}
return true;
}
bool LLInventoryCategory::importLLSD(const std::string& label, const LLSD& value)
{
if (label == INV_FOLDER_ID_LABEL)
{
setUUID(value.asUUID());
return true;
}
else if (label == INV_PARENT_ID_LABEL)
{
setParent(value.asUUID());
return true;
}
else if (label == INV_ASSET_TYPE_LABEL)
{
setType(LLAssetType::lookup(value.asString()));
return true;
}
else if (label == INV_PREFERRED_TYPE_LABEL)
{
setPreferredType(LLFolderType::lookup(value.asString()));
return true;
}
else if (label == INV_THUMBNAIL_LABEL)
{
LLUUID thumbnail_uuid;
if (value.has(INV_ASSET_ID_LABEL))
{
thumbnail_uuid = value[INV_ASSET_ID_LABEL].asUUID();
}
setThumbnailUUID(thumbnail_uuid);
return true;
}
if (label == INV_FAVORITE_LABEL)
{
bool favorite = false;
if (value.has(INV_TOGGLED_LABEL))
{
favorite = value[INV_TOGGLED_LABEL].asBoolean();
}
setFavorite(favorite);
}
else if (label == INV_NAME_LABEL)
{
mName = value.asString();
LLStringUtil::replaceNonstandardASCII(mName, ' ');
LLStringUtil::replaceChar(mName, '|', ' ');
return true;
}
return false;
}
///----------------------------------------------------------------------------
/// Local function definitions
/// Local function definitions for testing purposes
///----------------------------------------------------------------------------
LLSD ll_create_sd_from_inventory_item(LLPointer<LLInventoryItem> item)

View File

@ -71,6 +71,7 @@ public:
virtual const LLUUID& getLinkedUUID() const; // inventoryID that this item points to, else this item's inventoryID
const LLUUID& getParentUUID() const;
virtual const LLUUID& getThumbnailUUID() const;
virtual bool getIsFavorite() const;
virtual const std::string& getName() const;
virtual LLAssetType::EType getType() const;
LLAssetType::EType getActualType() const; // bypasses indirection for linked items
@ -86,6 +87,7 @@ public:
virtual void rename(const std::string& new_name);
void setParent(const LLUUID& new_parent);
virtual void setThumbnailUUID(const LLUUID& thumbnail_uuid);
virtual void setFavorite(bool favorite);
void setType(LLAssetType::EType type);
virtual void setCreationDate(time_t creation_date_utc); // only stored for items
@ -111,6 +113,7 @@ protected:
LLUUID mUUID;
LLUUID mParentUUID; // Parent category. Root categories have LLUUID::NULL.
LLUUID mThumbnailUUID;
bool mFavorite;
LLAssetType::EType mType;
std::string mName;
time_t mCreationDate; // seconds from 1/1/1970, UTC
@ -270,8 +273,9 @@ public:
virtual bool importLegacyStream(std::istream& input_stream);
virtual bool exportLegacyStream(std::ostream& output_stream, bool include_asset_key = true) const;
LLSD exportLLSD() const;
bool importLLSD(const LLSD& cat_data);
virtual void exportLLSD(LLSD& sd) const;
bool importLLSDMap(const LLSD& cat_data);
virtual bool importLLSD(const std::string& label, const LLSD& value);
//--------------------------------------------------------------------
// Member Variables
//--------------------------------------------------------------------
@ -285,6 +289,7 @@ protected:
//
// These functions convert between structured data and an inventory
// item, appropriate for serialization.
// Not up to date (no favorites, nor thumbnails), for testing purposes
//-----------------------------------------------------------------------------
LLSD ll_create_sd_from_inventory_item(LLPointer<LLInventoryItem> item);
LLSD ll_create_sd_from_inventory_category(LLPointer<LLInventoryCategory> cat);

View File

@ -704,6 +704,79 @@ bool LLPermissions::exportLegacyStream(std::ostream& output_stream) const
return true;
}
static const std::string PERM_CREATOR_ID_LABEL("creator_id");
static const std::string PERM_OWNER_ID_LABEL("owner_id");
static const std::string PERM_LAST_OWNER_ID_LABEL("last_owner_id");
static const std::string PERM_GROUP_ID_LABEL("group_id");
static const std::string PERM_IS_OWNER_GROUP_LABEL("is_owner_group");
static const std::string PERM_BASE_MASK_LABEL("base_mask");
static const std::string PERM_OWNER_MASK_LABEL("owner_mask");
static const std::string PERM_GROUP_MASK_LABEL("group_mask");
static const std::string PERM_EVERYONE_MASK_LABEL("everyone_mask");
static const std::string PERM_NEXT_OWNER_MASK_LABEL("next_owner_mask");
void LLPermissions::importLLSD(const LLSD& sd_perm)
{
LLSD::map_const_iterator i, end;
end = sd_perm.endMap();
for (i = sd_perm.beginMap(); i != end; ++i)
{
const std::string& label = i->first;
if (label == PERM_CREATOR_ID_LABEL)
{
mCreator = i->second.asUUID();
continue;
}
if (label == PERM_OWNER_ID_LABEL)
{
mOwner = i->second.asUUID();
continue;
}
if (label == PERM_LAST_OWNER_ID_LABEL)
{
mLastOwner = i->second.asUUID();
continue;
}
if (label == PERM_GROUP_ID_LABEL)
{
mGroup = i->second.asUUID();
continue;
}
if (label == PERM_BASE_MASK_LABEL)
{
PermissionMask mask = i->second.asInteger();
mMaskBase = mask;
continue;
}
if (label == PERM_OWNER_MASK_LABEL)
{
PermissionMask mask = i->second.asInteger();
mMaskOwner = mask;
continue;
}
if (label == PERM_EVERYONE_MASK_LABEL)
{
PermissionMask mask = i->second.asInteger();
mMaskEveryone = mask;
continue;
}
if (label == PERM_GROUP_MASK_LABEL)
{
PermissionMask mask = i->second.asInteger();
mMaskGroup = mask;
continue;
}
if (label == PERM_NEXT_OWNER_MASK_LABEL)
{
PermissionMask mask = i->second.asInteger();
mMaskNextOwner = mask;
continue;
}
}
fix();
}
bool LLPermissions::operator==(const LLPermissions &rhs) const
{
return
@ -1016,55 +1089,30 @@ std::string mask_to_string(U32 mask, bool isOpenSim /*=false*/) // <FS:Beq/> rem
///----------------------------------------------------------------------------
/// exported functions
///----------------------------------------------------------------------------
static const std::string PERM_CREATOR_ID_LABEL("creator_id");
static const std::string PERM_OWNER_ID_LABEL("owner_id");
static const std::string PERM_LAST_OWNER_ID_LABEL("last_owner_id");
static const std::string PERM_GROUP_ID_LABEL("group_id");
static const std::string PERM_IS_OWNER_GROUP_LABEL("is_owner_group");
static const std::string PERM_BASE_MASK_LABEL("base_mask");
static const std::string PERM_OWNER_MASK_LABEL("owner_mask");
static const std::string PERM_GROUP_MASK_LABEL("group_mask");
static const std::string PERM_EVERYONE_MASK_LABEL("everyone_mask");
static const std::string PERM_NEXT_OWNER_MASK_LABEL("next_owner_mask");
LLSD ll_create_sd_from_permissions(const LLPermissions& perm)
{
LLSD rv;
ll_fill_sd_from_permissions(rv, perm);
return rv;
}
void ll_fill_sd_from_permissions(LLSD& rv, const LLPermissions& perm)
{
rv[PERM_CREATOR_ID_LABEL] = perm.getCreator();
rv[PERM_OWNER_ID_LABEL] = perm.getOwner();
rv[PERM_LAST_OWNER_ID_LABEL] = perm.getLastOwner();
rv[PERM_GROUP_ID_LABEL] = perm.getGroup();
rv[PERM_IS_OWNER_GROUP_LABEL] = perm.isGroupOwned();
rv[PERM_BASE_MASK_LABEL] = (S32)perm.getMaskBase();
rv[PERM_OWNER_MASK_LABEL] = (S32)perm.getMaskOwner();
rv[PERM_GROUP_MASK_LABEL] = (S32)perm.getMaskGroup();
rv[PERM_EVERYONE_MASK_LABEL] = (S32)perm.getMaskEveryone();
rv[PERM_NEXT_OWNER_MASK_LABEL] = (S32)perm.getMaskNextOwner();
return rv;
rv[PERM_BASE_MASK_LABEL] = (LLSD::Integer)perm.getMaskBase();
rv[PERM_OWNER_MASK_LABEL] = (LLSD::Integer)perm.getMaskOwner();
rv[PERM_GROUP_MASK_LABEL] = (LLSD::Integer)perm.getMaskGroup();
rv[PERM_EVERYONE_MASK_LABEL] = (LLSD::Integer)perm.getMaskEveryone();
rv[PERM_NEXT_OWNER_MASK_LABEL] = (LLSD::Integer)perm.getMaskNextOwner();
}
LLPermissions ll_permissions_from_sd(const LLSD& sd_perm)
{
LLPermissions rv;
rv.init(
sd_perm[PERM_CREATOR_ID_LABEL].asUUID(),
sd_perm[PERM_OWNER_ID_LABEL].asUUID(),
sd_perm[PERM_LAST_OWNER_ID_LABEL].asUUID(),
sd_perm[PERM_GROUP_ID_LABEL].asUUID());
// We do a cast to U32 here since LLSD does not attempt to
// represent unsigned ints.
PermissionMask mask;
mask = (U32)(sd_perm[PERM_BASE_MASK_LABEL].asInteger());
rv.setMaskBase(mask);
mask = (U32)(sd_perm[PERM_OWNER_MASK_LABEL].asInteger());
rv.setMaskOwner(mask);
mask = (U32)(sd_perm[PERM_EVERYONE_MASK_LABEL].asInteger());
rv.setMaskEveryone(mask);
mask = (U32)(sd_perm[PERM_GROUP_MASK_LABEL].asInteger());
rv.setMaskGroup(mask);
mask = (U32)(sd_perm[PERM_NEXT_OWNER_MASK_LABEL].asInteger());
rv.setMaskNext(mask);
rv.fix();
rv.importLLSD(sd_perm);
return rv;
}

View File

@ -302,6 +302,8 @@ public:
bool importLegacyStream(std::istream& input_stream);
bool exportLegacyStream(std::ostream& output_stream) const;
void importLLSD(const LLSD& sd_perm);
bool operator==(const LLPermissions &rhs) const;
bool operator!=(const LLPermissions &rhs) const;
@ -447,6 +449,7 @@ protected:
// like 'creator_id', 'owner_id', etc, with the value copied from the
// permission object.
LLSD ll_create_sd_from_permissions(const LLPermissions& perm);
void ll_fill_sd_from_permissions(LLSD& rv, const LLPermissions& perm);
LLPermissions ll_permissions_from_sd(const LLSD& sd_perm);
#endif

View File

@ -90,15 +90,20 @@ bool LLSaleInfo::exportLegacyStream(std::ostream& output_stream) const
LLSD LLSaleInfo::asLLSD() const
{
LLSD sd;
asLLSD(sd);
return sd;
}
void LLSaleInfo::asLLSD(LLSD& sd) const
{
const char* type = lookup(mSaleType);
if (!type)
{
LL_WARNS_ONCE() << "Unknown sale type: " << mSaleType << LL_ENDL;
type = lookup(LLSaleInfo::FS_NOT);
}
sd["sale_type"] = type;
sd["sale_type"] = std::string(type);
sd["sale_price"] = mSalePrice;
return sd;
}
bool LLSaleInfo::fromLLSD(const LLSD& sd, bool& has_perm_mask, U32& perm_mask)

View File

@ -86,6 +86,7 @@ public:
bool exportLegacyStream(std::ostream& output_stream) const;
LLSD asLLSD() const;
void asLLSD(LLSD &sd) const;
operator LLSD() const { return asLLSD(); }
bool fromLLSD(const LLSD& sd, bool& has_perm_mask, U32& perm_mask);
bool importLegacyStream(std::istream& input_stream, bool& has_perm_mask, U32& perm_mask);

View File

@ -39,6 +39,34 @@
#pragma warning(disable: 4702)
#endif
void set_random_inventory_metadata(LLInventoryObject* obj)
{
S32 extra = rand() % 4;
switch (extra)
{
case 0:
{
LLUUID thumbnail_id;
thumbnail_id.generate();
obj->setThumbnailUUID(thumbnail_id);
break;
}
case 1:
obj->setFavorite(true);
break;
case 2:
{
LLUUID thumbnail_id;
thumbnail_id.generate();
obj->setThumbnailUUID(thumbnail_id);
obj->setFavorite(true);
break;
}
default:
break;
}
}
LLPointer<LLInventoryItem> create_random_inventory_item()
{
LLUUID item_id;
@ -75,6 +103,7 @@ LLPointer<LLInventoryItem> create_random_inventory_item()
sale_info,
flags,
creation);
set_random_inventory_metadata(item);
return item;
}
@ -90,6 +119,7 @@ LLPointer<LLInventoryCategory> create_random_inventory_cat()
parent_id,
LLFolderType::FT_NONE,
std::string("Sample category"));
set_random_inventory_metadata(cat);
return cat;
}
@ -290,6 +320,7 @@ namespace tut
src->setCreationDate(new_creation);
// test a save/load cycle to LLSD and back again
// Note: ll_create_sd_from_inventory_item does not support metadata
LLSD sd = ll_create_sd_from_inventory_item(src);
LLPointer<LLInventoryItem> dst = new LLInventoryItem;
bool successful_parse = dst->fromLLSD(sd);
@ -329,7 +360,9 @@ namespace tut
}
LLPointer<LLInventoryItem> src1 = create_random_inventory_item();
fileXML << LLSDOStreamer<LLSDNotationFormatter>(src1->asLLSD()) << std::endl;
LLSD sd;
src1->asLLSD(sd);
fileXML << LLSDOStreamer<LLSDNotationFormatter>(sd) << std::endl;
fileXML.close();
@ -364,13 +397,13 @@ namespace tut
ensure_equals("8.name::getName() failed", src1->getName(), src2->getName());
ensure_equals("9.description::getDescription() failed", src1->getDescription(), src2->getDescription());
ensure_equals("10.creation::getCreationDate() failed", src1->getCreationDate(), src2->getCreationDate());
ensure_equals("13.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID());
ensure_equals("14.favorites::getIsFavorite() failed", src1->getIsFavorite(), src2->getIsFavorite());
}
template<> template<>
void inventory_object::test<8>()
{
LLPointer<LLInventoryItem> src1 = create_random_inventory_item();
std::ostringstream ostream;
@ -390,8 +423,8 @@ namespace tut
ensure_equals("8.name::getName() failed", src1->getName(), src2->getName());
ensure_equals("9.description::getDescription() failed", src1->getDescription(), src2->getDescription());
ensure_equals("10.creation::getCreationDate() failed", src1->getCreationDate(), src2->getCreationDate());
ensure_equals("11.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID());
ensure_equals("12.favorites::getIsFavorite() failed", false, src2->getIsFavorite()); // not supposed to carry over
}
template<> template<>
@ -421,6 +454,8 @@ namespace tut
ensure_equals("10.name::getName() failed", src1->getName(), src2->getName());
ensure_equals("11.description::getDescription() failed", src1->getDescription(), src2->getDescription());
ensure_equals("12.creation::getCreationDate() failed", src1->getCreationDate(), src2->getCreationDate());
ensure_equals("13.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID());
ensure_equals("14.favorites::getIsFavorite() failed", src1->getIsFavorite(), src2->getIsFavorite());
}
//******class LLInventoryCategory*******//
@ -458,7 +493,9 @@ namespace tut
}
LLPointer<LLInventoryCategory> src1 = create_random_inventory_cat();
fileXML << LLSDOStreamer<LLSDNotationFormatter>(src1->exportLLSD()) << std::endl;
LLSD sd;
src1->exportLLSD(sd);
fileXML << LLSDOStreamer<LLSDNotationFormatter>(sd) << std::endl;
fileXML.close();
llifstream file(filename.c_str());
@ -481,13 +518,15 @@ namespace tut
file.close();
LLPointer<LLInventoryCategory> src2 = new LLInventoryCategory();
src2->importLLSD(s_item);
src2->importLLSDMap(s_item);
ensure_equals("1.item id::getUUID() failed", src1->getUUID(), src2->getUUID());
ensure_equals("2.parent::getParentUUID() failed", src1->getParentUUID(), src2->getParentUUID());
ensure_equals("3.type::getType() failed", src1->getType(), src2->getType());
ensure_equals("4.preferred type::getPreferredType() failed", src1->getPreferredType(), src2->getPreferredType());
ensure_equals("5.name::getName() failed", src1->getName(), src2->getName());
ensure_equals("6.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID());
ensure_equals("7.favorites::getIsFavorite() failed", src1->getIsFavorite(), src2->getIsFavorite());
}
template<> template<>
@ -507,6 +546,7 @@ namespace tut
ensure_equals("3.type::getType() failed", src1->getType(), src2->getType());
ensure_equals("4.preferred type::getPreferredType() failed", src1->getPreferredType(), src2->getPreferredType());
ensure_equals("5.name::getName() failed", src1->getName(), src2->getName());
ensure_equals("13.thumbnails::getThumbnailUUID() failed", src1->getThumbnailUUID(), src2->getThumbnailUUID());
ensure_equals("14.favorites::getIsFavorite() failed", false, src2->getIsFavorite()); // currently not supposed to carry over
}
}

View File

@ -687,6 +687,12 @@ bool LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
bool vflip = true;
bool hflip = false;
if (raw_image.isBufferInvalid())
{
base.setLastError("Invalid input, no buffer");
return false;
}
try
{
// Set up input image files

View File

@ -328,28 +328,30 @@ void LLCoordFrame::rotate(const LLMatrix3 &rotation_matrix)
}
// Rotate 2 normalized orthogonal vectors in direction from `source` to `target`
static void rotate2(LLVector3& source, LLVector3& target, F32 angle)
{
F32 sx = source[VX], sy = source[VY], sz = source[VZ];
F32 tx = target[VX], ty = target[VY], tz = target[VZ];
F32 c = cosf(angle), s = sinf(angle);
source.set(sx * c + tx * s, sy * c + ty * s, sz * c + tz * s);
target.set(tx * c - sx * s, ty * c - sy * s, tz * c - sz * s);
}
void LLCoordFrame::roll(F32 angle)
{
LLQuaternion q(angle, mXAxis);
LLMatrix3 rotation_matrix(q);
rotate(rotation_matrix);
CHECK_FINITE_OBJ();
rotate2(mYAxis, mZAxis, angle);
}
void LLCoordFrame::pitch(F32 angle)
{
LLQuaternion q(angle, mYAxis);
LLMatrix3 rotation_matrix(q);
rotate(rotation_matrix);
CHECK_FINITE_OBJ();
rotate2(mZAxis, mXAxis, angle);
}
void LLCoordFrame::yaw(F32 angle)
{
LLQuaternion q(angle, mZAxis);
LLMatrix3 rotation_matrix(q);
rotate(rotation_matrix);
CHECK_FINITE_OBJ();
rotate2(mXAxis, mYAxis, angle);
}
// get*() routines

View File

@ -57,7 +57,7 @@ LLQuaternion::LLQuaternion(const LLMatrix3 &mat)
LLQuaternion::LLQuaternion(F32 angle, const LLVector4 &vec)
{
F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]);
F32 mag = vec.length();
if (mag > FP_MAG_THRESHOLD)
{
angle *= 0.5;
@ -76,7 +76,7 @@ LLQuaternion::LLQuaternion(F32 angle, const LLVector4 &vec)
LLQuaternion::LLQuaternion(F32 angle, const LLVector3 &vec)
{
F32 mag = sqrtf(vec.mV[VX] * vec.mV[VX] + vec.mV[VY] * vec.mV[VY] + vec.mV[VZ] * vec.mV[VZ]);
F32 mag = vec.length();
if (mag > FP_MAG_THRESHOLD)
{
angle *= 0.5;

View File

@ -5760,7 +5760,15 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents)
S32 vert_count = 0;
if (!data.p.empty())
{
vert_count = static_cast<S32>(meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count));
try
{
vert_count = static_cast<S32>(meshopt_generateVertexRemapMulti(&remap[0], nullptr, data.p.size(), data.p.size(), mos, stream_count));
}
catch (std::bad_alloc&)
{
LLError::LLUserWarningMsg::showOutOfMemory();
LL_ERRS("LLCoros") << "Failed to allocate memory for VertexRemap: " << (S32)data.p.size() << LL_ENDL;
}
}
if (vert_count < 65535 && vert_count != 0)

View File

@ -66,6 +66,13 @@ F32 angle_between(const LLVector2& a, const LLVector2& b)
return angle;
}
F32 signed_angle_between(const LLVector2& a, const LLVector2& b)
{
F32 angle = angle_between(a, b);
F32 rhombus_square = a[VX] * b[VY] - b[VX] * a[VY];
return rhombus_square < 0 ? -angle : angle;
}
bool are_parallel(const LLVector2& a, const LLVector2& b, F32 epsilon)
{
LLVector2 an = a;

View File

@ -107,13 +107,14 @@ class LLVector2
friend LLVector2 operator-(const LLVector2 &a); // Return vector -a
friend std::ostream& operator<<(std::ostream& s, const LLVector2 &a); // Stream a
friend std::ostream& operator<<(std::ostream& s, const LLVector2 &a); // Stream a
};
// Non-member functions
F32 angle_between(const LLVector2& a, const LLVector2& b); // Returns angle (radians) between a and b
F32 signed_angle_between(const LLVector2& a, const LLVector2& b); // Returns signed angle (radians) between a and b
bool are_parallel(const LLVector2& a, const LLVector2& b, F32 epsilon = F_APPROXIMATELY_ZERO); // Returns true if a and b are very close to parallel
F32 dist_vec(const LLVector2& a, const LLVector2& b); // Returns distance between a and b
F32 dist_vec_squared(const LLVector2& a, const LLVector2& b);// Returns distance squared between a and b
@ -124,26 +125,22 @@ LLVector2 lerp(const LLVector2& a, const LLVector2& b, F32 u); // Returns a vect
inline LLVector2::LLVector2()
{
mV[VX] = 0.f;
mV[VY] = 0.f;
clear();
}
inline LLVector2::LLVector2(F32 x, F32 y)
{
mV[VX] = x;
mV[VY] = y;
set(x, y);
}
inline LLVector2::LLVector2(const F32 *vec)
{
mV[VX] = vec[VX];
mV[VY] = vec[VY];
set(vec);
}
inline LLVector2::LLVector2(const LLVector3 &vec)
{
mV[VX] = vec.mV[VX];
mV[VY] = vec.mV[VY];
set(vec.mV);
}
inline LLVector2::LLVector2(const LLSD &sd)
@ -155,28 +152,24 @@ inline LLVector2::LLVector2(const LLSD &sd)
inline void LLVector2::clear()
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mV[VX] = mV[VY] = 0.f;
}
inline void LLVector2::setZero()
{
mV[VX] = 0.f;
mV[VY] = 0.f;
clear();
}
// deprecated
inline void LLVector2::clearVec()
{
mV[VX] = 0.f;
mV[VY] = 0.f;
clear();
}
// deprecated
inline void LLVector2::zeroVec()
{
mV[VX] = 0.f;
mV[VY] = 0.f;
clear();
}
inline void LLVector2::set(F32 x, F32 y)
@ -187,36 +180,31 @@ inline void LLVector2::set(F32 x, F32 y)
inline void LLVector2::set(const LLVector2 &vec)
{
mV[VX] = vec.mV[VX];
mV[VY] = vec.mV[VY];
set(vec.mV);
}
inline void LLVector2::set(const F32 *vec)
{
mV[VX] = vec[VX];
mV[VY] = vec[VY];
set(vec[VX], vec[VY]);
}
// deprecated
inline void LLVector2::setVec(F32 x, F32 y)
{
mV[VX] = x;
mV[VY] = y;
set(x, y);
}
// deprecated
inline void LLVector2::setVec(const LLVector2 &vec)
{
mV[VX] = vec.mV[VX];
mV[VY] = vec.mV[VY];
set(vec);
}
// deprecated
inline void LLVector2::setVec(const F32 *vec)
{
mV[VX] = vec[VX];
mV[VY] = vec[VY];
set(vec);
}
@ -224,7 +212,7 @@ inline void LLVector2::setVec(const F32 *vec)
inline F32 LLVector2::length() const
{
return sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY]);
return sqrt(lengthSquared());
}
inline F32 LLVector2::lengthSquared() const
@ -234,61 +222,42 @@ inline F32 LLVector2::lengthSquared() const
inline F32 LLVector2::normalize()
{
F32 mag = sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY]);
F32 oomag;
F32 mag = length();
if (mag > FP_MAG_THRESHOLD)
{
oomag = 1.f/mag;
mV[VX] *= oomag;
mV[VY] *= oomag;
*this /= mag;
}
else
{
mV[VX] = 0.f;
mV[VY] = 0.f;
clear();
mag = 0;
}
return (mag);
return mag;
}
// checker
inline bool LLVector2::isFinite() const
{
return (llfinite(mV[VX]) && llfinite(mV[VY]));
return llfinite(mV[VX]) && llfinite(mV[VY]);
}
// deprecated
inline F32 LLVector2::magVec() const
{
return sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY]);
return length();
}
// deprecated
inline F32 LLVector2::magVecSquared() const
{
return mV[VX]*mV[VX] + mV[VY]*mV[VY];
return lengthSquared();
}
// deprecated
inline F32 LLVector2::normVec()
{
F32 mag = sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY]);
F32 oomag;
if (mag > FP_MAG_THRESHOLD)
{
oomag = 1.f/mag;
mV[VX] *= oomag;
mV[VY] *= oomag;
}
else
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mag = 0;
}
return (mag);
return normalize();
}
inline const LLVector2& LLVector2::scaleVec(const LLVector2& vec)
@ -301,11 +270,7 @@ inline const LLVector2& LLVector2::scaleVec(const LLVector2& vec)
inline bool LLVector2::isNull() const
{
if (F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY])
{
return true;
}
return false;
return F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY];
}
@ -405,10 +370,7 @@ inline const LLVector2& operator*=(LLVector2& a, F32 k)
inline const LLVector2& operator/=(LLVector2& a, F32 k)
{
F32 t = 1.f / k;
a.mV[VX] *= t;
a.mV[VY] *= t;
return a;
return a *= 1.f / k;
}
inline LLVector2 operator-(const LLVector2& a)

View File

@ -112,24 +112,24 @@ class LLVector3
const LLVector3& setVec(const LLVector4 &vec); // deprecated
const LLVector3& setVec(const LLVector3d &vec); // deprecated
F32 length() const; // Returns magnitude of LLVector3
F32 lengthSquared() const; // Returns magnitude squared of LLVector3
F32 magVec() const; // deprecated
F32 magVecSquared() const; // deprecated
F32 length() const; // Returns magnitude of LLVector3
F32 lengthSquared() const; // Returns magnitude squared of LLVector3
F32 magVec() const; // deprecated
F32 magVecSquared() const; // deprecated
inline F32 normalize(); // Normalizes and returns the magnitude of LLVector3
inline F32 normVec(); // deprecated
inline F32 normalize(); // Normalizes and returns the magnitude of LLVector3
inline F32 normVec(); // deprecated
inline bool inRange( F32 min, F32 max ) const; // Returns true if all values of the vector are between min and max
inline bool inRange(F32 min, F32 max) const; // Returns true if all values of the vector are between min and max
const LLVector3& rotVec(F32 angle, const LLVector3 &vec); // Rotates about vec by angle radians
const LLVector3& rotVec(F32 angle, F32 x, F32 y, F32 z); // Rotates about x,y,z by angle radians
const LLVector3& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat
const LLVector3& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q
const LLVector3& transVec(const LLMatrix4& mat); // Transforms by LLMatrix4 mat (mat * v)
const LLVector3& rotVec(F32 angle, const LLVector3 &vec); // Rotates about vec by angle radians
const LLVector3& rotVec(F32 angle, F32 x, F32 y, F32 z); // Rotates about x,y,z by angle radians
const LLVector3& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat
const LLVector3& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q
const LLVector3& transVec(const LLMatrix4& mat); // Transforms by LLMatrix4 mat (mat * v)
const LLVector3& scaleVec(const LLVector3& vec); // scales per component by vec
LLVector3 scaledVec(const LLVector3& vec) const; // get a copy of this vector scaled by vec
const LLVector3& scaleVec(const LLVector3& vec); // scales per component by vec
LLVector3 scaledVec(const LLVector3& vec) const; // get a copy of this vector scaled by vec
bool isNull() const; // Returns true if vector has a _very_small_ length
bool isExactlyZero() const { return !mV[VX] && !mV[VY] && !mV[VZ]; }
@ -183,23 +183,17 @@ bool box_valid_and_non_zero(const LLVector3* box);
inline LLVector3::LLVector3()
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mV[VZ] = 0.f;
clear();
}
inline LLVector3::LLVector3(const F32 x, const F32 y, const F32 z)
{
mV[VX] = x;
mV[VY] = y;
mV[VZ] = z;
set(x, y, z);
}
inline LLVector3::LLVector3(const F32 *vec)
{
mV[VX] = vec[VX];
mV[VY] = vec[VY];
mV[VZ] = vec[VZ];
set(vec);
}
inline LLVector3::LLVector3(const glm::vec3& vec)
@ -230,7 +224,7 @@ inline LLVector3::LLVector3(const LLVector3 &copy)
// checker
inline bool LLVector3::isFinite() const
{
return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]));
return llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]);
}
@ -238,30 +232,22 @@ inline bool LLVector3::isFinite() const
inline void LLVector3::clear()
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mV[VZ] = 0.f;
set(0.f, 0.f, 0.f);
}
inline void LLVector3::setZero()
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mV[VZ] = 0.f;
clear();
}
inline void LLVector3::clearVec()
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mV[VZ] = 0.f;
clear();
}
inline void LLVector3::zeroVec()
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mV[VZ] = 0.f;
clear();
}
inline void LLVector3::set(F32 x, F32 y, F32 z)
@ -273,16 +259,12 @@ inline void LLVector3::set(F32 x, F32 y, F32 z)
inline void LLVector3::set(const LLVector3& vec)
{
mV[VX] = vec.mV[VX];
mV[VY] = vec.mV[VY];
mV[VZ] = vec.mV[VZ];
set(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
}
inline void LLVector3::set(const F32* vec)
{
mV[VX] = vec[VX];
mV[VY] = vec[VY];
mV[VZ] = vec[VZ];
set(vec[VX], vec[VY], vec[VZ]);
}
inline void LLVector3::set(const glm::vec4& vec)
@ -302,95 +284,66 @@ inline void LLVector3::set(const glm::vec3& vec)
// deprecated
inline void LLVector3::setVec(F32 x, F32 y, F32 z)
{
mV[VX] = x;
mV[VY] = y;
mV[VZ] = z;
set(x, y, z);
}
// deprecated
inline void LLVector3::setVec(const LLVector3& vec)
{
mV[VX] = vec.mV[VX];
mV[VY] = vec.mV[VY];
mV[VZ] = vec.mV[VZ];
set(vec);
}
// deprecated
inline void LLVector3::setVec(const F32* vec)
{
mV[VX] = vec[0];
mV[VY] = vec[1];
mV[VZ] = vec[2];
set(vec);
}
inline F32 LLVector3::normalize()
{
F32 mag = (F32) sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
F32 oomag;
if (mag > FP_MAG_THRESHOLD)
{
oomag = 1.f/mag;
mV[VX] *= oomag;
mV[VY] *= oomag;
mV[VZ] *= oomag;
*this /= mag;
}
else
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mV[VZ] = 0.f;
clear();
mag = 0;
}
return (mag);
return mag;
}
// deprecated
inline F32 LLVector3::normVec()
{
F32 mag = sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
F32 oomag;
if (mag > FP_MAG_THRESHOLD)
{
oomag = 1.f/mag;
mV[VX] *= oomag;
mV[VY] *= oomag;
mV[VZ] *= oomag;
}
else
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mV[VZ] = 0.f;
mag = 0;
}
return (mag);
return normalize();
}
// LLVector3 Magnitude and Normalization Functions
inline F32 LLVector3::length() const
inline F32 LLVector3::length() const
{
return sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
return sqrt(lengthSquared());
}
inline F32 LLVector3::lengthSquared() const
inline F32 LLVector3::lengthSquared() const
{
return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ];
}
inline F32 LLVector3::magVec() const
inline F32 LLVector3::magVec() const
{
return sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
return length();
}
inline F32 LLVector3::magVecSquared() const
inline F32 LLVector3::magVecSquared() const
{
return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ];
return lengthSquared();
}
inline bool LLVector3::inRange( F32 min, F32 max ) const
inline bool LLVector3::inRange(F32 min, F32 max) const
{
return mV[VX] >= min && mV[VX] <= max &&
mV[VY] >= min && mV[VY] <= max &&
@ -416,7 +369,7 @@ inline F32 operator*(const LLVector3& a, const LLVector3& b)
inline LLVector3 operator%(const LLVector3& a, const LLVector3& b)
{
return LLVector3( a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY] );
return LLVector3(a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY]);
}
inline LLVector3 operator/(const LLVector3& a, F32 k)
@ -476,7 +429,7 @@ inline const LLVector3& operator-=(LLVector3& a, const LLVector3& b)
inline const LLVector3& operator%=(LLVector3& a, const LLVector3& b)
{
LLVector3 ret( a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY]);
LLVector3 ret(a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY]);
a = ret;
return a;
}
@ -499,9 +452,7 @@ inline const LLVector3& operator*=(LLVector3& a, const LLVector3& b)
inline const LLVector3& operator/=(LLVector3& a, F32 k)
{
a.mV[VX] /= k;
a.mV[VY] /= k;
a.mV[VZ] /= k;
a *= 1.f / k;
return a;
}
@ -526,7 +477,7 @@ inline F32 dist_vec(const LLVector3& a, const LLVector3& b)
F32 x = a.mV[VX] - b.mV[VX];
F32 y = a.mV[VY] - b.mV[VY];
F32 z = a.mV[VZ] - b.mV[VZ];
return sqrt( x*x + y*y + z*z );
return sqrt(x*x + y*y + z*z);
}
inline F32 dist_vec_squared(const LLVector3& a, const LLVector3& b)
@ -551,10 +502,7 @@ inline LLVector3 projected_vec(const LLVector3& a, const LLVector3& b)
{
return ((a * b) / bb) * b;
}
else
{
return b.zero;
}
return b.zero;
}
inline LLVector3 inverse_projected_vec(const LLVector3& a, const LLVector3& b)
@ -591,11 +539,7 @@ inline LLVector3 lerp(const LLVector3& a, const LLVector3& b, F32 u)
inline bool LLVector3::isNull() const
{
if ( F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ] )
{
return true;
}
return false;
return F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ];
}
inline void update_min_max(LLVector3& min, LLVector3& max, const LLVector3& pos)
@ -636,7 +580,7 @@ inline F32 angle_between(const LLVector3& a, const LLVector3& b)
ab = 0.0f; // get rid of negative zero
}
LLVector3 c = a % b; // crossproduct
return atan2f(sqrtf(c * c), ab); // return the angle
return atan2f(c.length(), ab); // return the angle
}
inline bool are_parallel(const LLVector3& a, const LLVector3& b, F32 epsilon)
@ -646,7 +590,7 @@ inline bool are_parallel(const LLVector3& a, const LLVector3& b, F32 epsilon)
an.normalize();
bn.normalize();
F32 dot = an * bn;
if ( (1.0f - fabs(dot)) < epsilon)
if (1.0f - fabs(dot) < epsilon)
{
return true;
}

View File

@ -107,9 +107,9 @@ public:
F32 lengthSquared() const; // Returns magnitude squared of LLVector4
F32 normalize(); // Normalizes and returns the magnitude of LLVector4
F32 magVec() const; // deprecated
F32 magVecSquared() const; // deprecated
F32 normVec(); // deprecated
F32 magVec() const; // deprecated
F32 magVecSquared() const; // deprecated
F32 normVec(); // deprecated
// Sets all values to absolute value of their original values
// Returns true if data changed
@ -118,8 +118,8 @@ public:
bool isExactlyClear() const { return (mV[VW] == 1.0f) && !mV[VX] && !mV[VY] && !mV[VZ]; }
bool isExactlyZero() const { return !mV[VW] && !mV[VX] && !mV[VY] && !mV[VZ]; }
const LLVector4& rotVec(const LLMatrix4 &mat); // Rotates by MAT4 mat
const LLVector4& rotVec(const LLQuaternion &q); // Rotates by QUAT q
const LLVector4& rotVec(const LLMatrix4 &mat); // Rotates by MAT4 mat
const LLVector4& rotVec(const LLQuaternion &q); // Rotates by QUAT q
const LLVector4& scaleVec(const LLVector4& vec); // Scales component-wise by vec
@ -159,34 +159,22 @@ LLVector4 lerp(const LLVector4 &a, const LLVector4 &b, F32 u); // Returns a vect
inline LLVector4::LLVector4(void)
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mV[VZ] = 0.f;
mV[VW] = 1.f;
clear();
}
inline LLVector4::LLVector4(F32 x, F32 y, F32 z)
{
mV[VX] = x;
mV[VY] = y;
mV[VZ] = z;
mV[VW] = 1.f;
set(x, y, z, 1.f);
}
inline LLVector4::LLVector4(F32 x, F32 y, F32 z, F32 w)
{
mV[VX] = x;
mV[VY] = y;
mV[VZ] = z;
mV[VW] = w;
set(x, y, z, w);
}
inline LLVector4::LLVector4(const F32 *vec)
{
mV[VX] = vec[VX];
mV[VY] = vec[VY];
mV[VZ] = vec[VZ];
mV[VW] = vec[VW];
set(vec);
}
inline LLVector4::LLVector4(const F64 *vec)
@ -215,18 +203,12 @@ inline LLVector4::LLVector4(const LLVector2 &vec, F32 z, F32 w)
inline LLVector4::LLVector4(const LLVector3 &vec)
{
mV[VX] = vec.mV[VX];
mV[VY] = vec.mV[VY];
mV[VZ] = vec.mV[VZ];
mV[VW] = 1.f;
set(vec, 1.f);
}
inline LLVector4::LLVector4(const LLVector3 &vec, F32 w)
{
mV[VX] = vec.mV[VX];
mV[VY] = vec.mV[VY];
mV[VZ] = vec.mV[VZ];
mV[VW] = w;
set(vec, w);
}
inline LLVector4::LLVector4(const LLSD &sd)
@ -252,43 +234,31 @@ inline LLVector4::LLVector4(const glm::vec4& vec)
inline bool LLVector4::isFinite() const
{
return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]) && llfinite(mV[VW]));
return llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]) && llfinite(mV[VW]);
}
// Clear and Assignment Functions
inline void LLVector4::clear()
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mV[VZ] = 0.f;
mV[VW] = 1.f;
set(0.f, 0.f, 0.f, 1.f);
}
// deprecated
inline void LLVector4::clearVec()
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mV[VZ] = 0.f;
mV[VW] = 1.f;
clear();
}
// deprecated
inline void LLVector4::zeroVec()
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mV[VZ] = 0.f;
mV[VW] = 0.f;
set(0.f, 0.f, 0.f, 0.f);
}
inline void LLVector4::set(F32 x, F32 y, F32 z)
{
mV[VX] = x;
mV[VY] = y;
mV[VZ] = z;
mV[VW] = 1.f;
set(x, y, z, 1.f);
}
inline void LLVector4::set(F32 x, F32 y, F32 z, F32 w)
@ -301,10 +271,7 @@ inline void LLVector4::set(F32 x, F32 y, F32 z, F32 w)
inline void LLVector4::set(const LLVector4& vec)
{
mV[VX] = vec.mV[VX];
mV[VY] = vec.mV[VY];
mV[VZ] = vec.mV[VZ];
mV[VW] = vec.mV[VW];
set(vec.mV);
}
inline void LLVector4::set(const LLVector3& vec, F32 w)
@ -322,7 +289,6 @@ inline void LLVector4::set(const F32* vec)
mV[VZ] = vec[VZ];
mV[VW] = vec[VW];
}
inline void LLVector4::set(const glm::vec4& vec)
{
mV[VX] = vec.x;
@ -342,68 +308,53 @@ inline void LLVector4::set(const glm::vec3& vec, F32 w)
// deprecated
inline void LLVector4::setVec(F32 x, F32 y, F32 z)
{
mV[VX] = x;
mV[VY] = y;
mV[VZ] = z;
mV[VW] = 1.f;
set(x, y, z);
}
// deprecated
inline void LLVector4::setVec(F32 x, F32 y, F32 z, F32 w)
{
mV[VX] = x;
mV[VY] = y;
mV[VZ] = z;
mV[VW] = w;
set(x, y, z, w);
}
// deprecated
inline void LLVector4::setVec(const LLVector4& vec)
{
mV[VX] = vec.mV[VX];
mV[VY] = vec.mV[VY];
mV[VZ] = vec.mV[VZ];
mV[VW] = vec.mV[VW];
set(vec);
}
// deprecated
inline void LLVector4::setVec(const LLVector3& vec, F32 w)
{
mV[VX] = vec.mV[VX];
mV[VY] = vec.mV[VY];
mV[VZ] = vec.mV[VZ];
mV[VW] = w;
set(vec, w);
}
// deprecated
inline void LLVector4::setVec(const F32* vec)
{
mV[VX] = vec[VX];
mV[VY] = vec[VY];
mV[VZ] = vec[VZ];
mV[VW] = vec[VW];
set(vec);
}
// LLVector4 Magnitude and Normalization Functions
inline F32 LLVector4::length() const
inline F32 LLVector4::length() const
{
return sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
return sqrt(lengthSquared());
}
inline F32 LLVector4::lengthSquared() const
inline F32 LLVector4::lengthSquared() const
{
return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ];
}
inline F32 LLVector4::magVec() const
inline F32 LLVector4::magVec() const
{
return sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
return length();
}
inline F32 LLVector4::magVecSquared() const
inline F32 LLVector4::magVecSquared() const
{
return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ];
return lengthSquared();
}
// LLVector4 Operators
@ -422,7 +373,7 @@ inline LLVector4 operator-(const LLVector4& a, const LLVector4& b)
inline F32 operator*(const LLVector4& a, const LLVector4& b)
{
return (a.mV[VX]*b.mV[VX] + a.mV[VY]*b.mV[VY] + a.mV[VZ]*b.mV[VZ]);
return a.mV[VX]*b.mV[VX] + a.mV[VY]*b.mV[VY] + a.mV[VZ]*b.mV[VZ];
}
inline LLVector4 operator%(const LLVector4& a, const LLVector4& b)
@ -495,11 +446,7 @@ inline const LLVector4& operator*=(LLVector4& a, F32 k)
inline const LLVector4& operator/=(LLVector4& a, F32 k)
{
F32 t = 1.f / k;
a.mV[VX] *= t;
a.mV[VY] *= t;
a.mV[VZ] *= t;
return a;
return a *= 1.f / k;
}
inline LLVector4 operator-(const LLVector4& a)
@ -510,7 +457,7 @@ inline LLVector4 operator-(const LLVector4& a)
// [RLVa:KB] - RlvBehaviourModifierCompMin/Max
inline bool operator<(const LLVector4& lhs, const LLVector4& rhs)
{
return std::tie(lhs.mV[0], lhs.mV[1], lhs.mV[2], lhs.mV[3]) < std::tie(rhs.mV[0], rhs.mV[1], rhs.mV[2], rhs.mV[3]);
return std::tie(lhs.mV[VX], lhs.mV[VY], lhs.mV[VZ], lhs.mV[VW]) < std::tie(rhs.mV[VX], rhs.mV[VY], rhs.mV[VZ], rhs.mV[VW]);
}
// [/RLVa:KB]
@ -524,16 +471,16 @@ inline LLVector4::operator glm::vec4() const
return glm::make_vec4(mV);
}
inline F32 dist_vec(const LLVector4& a, const LLVector4& b)
inline F32 dist_vec(const LLVector4& a, const LLVector4& b)
{
LLVector4 vec = a - b;
return (vec.length());
return vec.length();
}
inline F32 dist_vec_squared(const LLVector4& a, const LLVector4& b)
inline F32 dist_vec_squared(const LLVector4& a, const LLVector4& b)
{
LLVector4 vec = a - b;
return (vec.lengthSquared());
return vec.lengthSquared();
}
inline LLVector4 lerp(const LLVector4& a, const LLVector4& b, F32 u)
@ -545,17 +492,13 @@ inline LLVector4 lerp(const LLVector4& a, const LLVector4& b, F32 u)
a.mV[VW] + (b.mV[VW] - a.mV[VW]) * u);
}
inline F32 LLVector4::normalize()
inline F32 LLVector4::normalize()
{
F32 mag = sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
F32 oomag;
if (mag > FP_MAG_THRESHOLD)
{
oomag = 1.f/mag;
mV[VX] *= oomag;
mV[VY] *= oomag;
mV[VZ] *= oomag;
*this /= mag;
}
else
{
@ -564,30 +507,13 @@ inline F32 LLVector4::normalize()
mV[VZ] = 0.f;
mag = 0.f;
}
return (mag);
return mag;
}
// deprecated
inline F32 LLVector4::normVec()
inline F32 LLVector4::normVec()
{
F32 mag = sqrt(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
F32 oomag;
if (mag > FP_MAG_THRESHOLD)
{
oomag = 1.f/mag;
mV[VX] *= oomag;
mV[VY] *= oomag;
mV[VZ] *= oomag;
}
else
{
mV[VX] = 0.f;
mV[VY] = 0.f;
mV[VZ] = 0.f;
mag = 0.f;
}
return (mag);
return normalize();
}
// Because apparently some parts of the viewer use this for color info.

View File

@ -69,7 +69,12 @@ LLModel::~LLModel()
{
if (mDecompID >= 0)
{
LLConvexDecomposition::getInstance()->deleteDecomposition(mDecompID);
// can be null on shutdown
LLConvexDecomposition* decomp = LLConvexDecomposition::getInstance();
if (decomp)
{
decomp->deleteDecomposition(mDecompID);
}
}
mPhysics.mMesh.clear();
}

View File

@ -140,7 +140,7 @@ public:
S32 notify(const LLSD& info);
bool notifyChildren(const LLSD& info);
void draw();
virtual void draw();
void storeOpenCloseState();
void restoreOpenCloseState();

View File

@ -70,7 +70,7 @@ const LLRect& LLFlatListView::getItemsRect() const
bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*/, EAddPosition pos /*= ADD_BOTTOM*/,bool rearrange /*= true*/)
{
if (!item) return false;
if (value.isUndefined()) return false;
if (value.isUndefined()) return false; // item stays an orphan?!!!
//force uniqueness of items, easiest check but unreliable
if (item->getParent() == mItemsPanel) return false;
@ -1370,9 +1370,17 @@ bool LLFlatListViewEx::getForceShowingUnmatchedItems() const
return mForceShowingUnmatchedItems;
}
void LLFlatListViewEx::setForceShowingUnmatchedItems(bool show)
void LLFlatListViewEx::setForceShowingUnmatchedItems(bool show, bool notify_parent)
{
if (mForceShowingUnmatchedItems != show)
{
mForceShowingUnmatchedItems = show;
if (!mFilterSubString.empty())
{
updateNoItemsMessage(mFilterSubString);
filterItems(false, true);
}
}
}
void LLFlatListViewEx::setFilterSubString(const std::string& filter_str, bool notify_parent)
@ -1440,6 +1448,7 @@ void LLFlatListViewEx::filterItems(bool re_sort, bool notify_parent)
if (visibility_changed && notify_parent)
{
rearrangeItems();
notifyParentItemsRectChanged();
}
}

View File

@ -498,7 +498,11 @@ public:
bool getForceShowingUnmatchedItems() const;
void setForceShowingUnmatchedItems(bool show);
/**
* Sets filtered out items to stay visible. Can result in rect changes,
* so can notify_parent if rect changes
*/
void setForceShowingUnmatchedItems(bool show, bool notify_parent);
/**
* Sets up new filter string and filters the list.

View File

@ -338,8 +338,10 @@ void LLFloater::initFloater(const Params& p)
// Help button: '?'
//SL-14050 Disable all Help question marks
// <FS:Ansariel> Nope!
mButtonsEnabled[BUTTON_HELP] = !mHelpTopic.empty();// false;
// <FS:TJ> Disable the help button only if the debug setting is on
static LLUICachedControl<bool> hide_help_buttons ("FSHideHelpButtons", false);
mButtonsEnabled[BUTTON_HELP] = !mHelpTopic.empty() && !hide_help_buttons;// false;
// </FS:TJ>
// Minimize button only for top draggers
if ( !mDragOnLeft && mCanMinimize )

View File

@ -226,6 +226,7 @@ public:
void scrollToShowSelection();
void scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect);
void setScrollContainer( LLScrollContainer* parent ) { mScrollContainer = parent; }
LLScrollContainer* getScrollContainer() { return mScrollContainer; }
LLRect getVisibleRect();
bool search(LLFolderViewItem* first_item, const std::string &search_string, bool backward);

View File

@ -31,11 +31,12 @@
#include "llfolderviewitem.h"
#include "llfolderview.h"
#include "llfolderviewmodel.h"
#include "llpanel.h"
#include "llcallbacklist.h"
#include "llcriticaldamp.h"
#include "llclipboard.h"
#include "llfocusmgr.h" // gFocusMgr
#include "llnotificationsutil.h"
#include "llpanel.h"
#include "lltrans.h"
#include "llwindow.h"
@ -62,7 +63,11 @@ LLUIColor LLFolderViewItem::sProtectedColor;
S32 LLFolderViewItem::sTopPad = 0;
LLUIImagePtr LLFolderViewItem::sFolderArrowImg;
LLUIImagePtr LLFolderViewItem::sSelectionImg;
LLUIImagePtr LLFolderViewItem::sFavoriteImg;
LLUIImagePtr LLFolderViewItem::sFavoriteContentImg;
LLFontGL* LLFolderViewItem::sSuffixFont = nullptr;
LLUIColor LLFolderViewItem::sFavoriteColor;
bool LLFolderViewItem::sColorSetInitialized = false;
// only integers can be initialized in header
const F32 LLFolderViewItem::FOLDER_CLOSE_TIME_CONSTANT = 0.02f;
@ -70,6 +75,9 @@ const F32 LLFolderViewItem::FOLDER_OPEN_TIME_CONSTANT = 0.03f;
const LLColor4U DEFAULT_WHITE(255, 255, 255);
constexpr S32 FAVORITE_IMAGE_SIZE = 14;
constexpr S32 FAVORITE_IMAGE_PAD = 3;
//static
LLFontGL* LLFolderViewItem::getLabelFontForStyle(U8 style)
@ -104,6 +112,8 @@ void LLFolderViewItem::initClass()
sTopPad = default_params.item_top_pad;
sFolderArrowImg = default_params.folder_arrow_image;
sSelectionImg = default_params.selection_image;
sFavoriteImg = default_params.favorite_image;
sFavoriteContentImg = default_params.favorite_content_image;
sSuffixFont = getLabelFontForStyle(LLFontGL::NORMAL);
// <FS:Ansariel> Make inventory selection color independent from menu color
@ -132,6 +142,8 @@ void LLFolderViewItem::cleanupClass()
sFonts.clear();
sFolderArrowImg = nullptr;
sSelectionImg = nullptr;
sFavoriteImg = nullptr;
sFavoriteContentImg = nullptr;
sSuffixFont = nullptr;
}
@ -140,13 +152,15 @@ void LLFolderViewItem::cleanupClass()
LLFolderViewItem::Params::Params()
: root(),
listener(),
favorite_image("favorite_image"),
favorite_content_image("favorite_content_image"),
folder_arrow_image("folder_arrow_image"),
folder_indentation("folder_indentation"),
selection_image("selection_image"),
item_height("item_height"),
item_top_pad("item_top_pad"),
creation_date(),
allow_wear("allow_wear", true),
marketplace_item("marketplace_item", false),
allow_drop("allow_drop", true),
font_color("font_color"),
font_highlight_color("font_highlight_color"),
@ -169,6 +183,8 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p)
: LLView(p),
mLabelWidth(0),
mLabelWidthDirty(false),
mIsFavorite(false),
mHasFavorites(false),
mSuffixNeedsRefresh(false),
mLabelPaddingRight(DEFAULT_LABEL_PADDING_RIGHT),
mParentFolder( NULL ),
@ -189,7 +205,7 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p)
mRoot(p.root),
mViewModelItem(p.listener),
mIsMouseOverTitle(false),
mAllowWear(p.allow_wear),
mMarketplaceItem(p.marketplace_item),
mAllowDrop(p.allow_drop),
mFontColor(p.font_color),
mFontHighlightColor(p.font_highlight_color),
@ -206,6 +222,21 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p)
mForInventory(p.for_inventory),
mItemTopPad(p.item_top_pad)
{
if (!sColorSetInitialized)
{
sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE);
sFlashBgColor = LLUIColorTable::instance().getColor("MenuItemFlashBgColor", DEFAULT_WHITE);
sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE);
sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE);
sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE);
sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE);
sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE);
sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE);
sFavoriteColor = LLUIColorTable::instance().getColor("InventoryFavoriteColor", DEFAULT_WHITE);
sColorSetInitialized = true;
}
if (mViewModelItem)
{
mViewModelItem->setFolderViewItem(this);
@ -228,6 +259,7 @@ bool LLFolderViewItem::postBuild()
// getDisplayName() is expensive (due to internal getLabelSuffix() and name building)
// it also sets search strings so it requires a filter reset
mLabel = utf8str_to_wstring(vmi->getDisplayName());
mIsFavorite = vmi->isFavorite() && !vmi->isItemInTrash();
setToolTip(vmi->getName());
// Dirty the filter flag of the model from the view (CHUI-849)
@ -342,6 +374,7 @@ void LLFolderViewItem::refresh()
mLabel = utf8str_to_wstring(vmi.getDisplayName());
mLabelFontBuffer.reset();
mIsFavorite = vmi.isFavorite() && !vmi.isItemInTrash();
setToolTip(vmi.getName());
// icons are slightly expensive to get, can be optimized
// see LLInventoryIcon::getIcon()
@ -376,6 +409,8 @@ void LLFolderViewItem::refreshSuffix()
mIconOpen = vmi->getIconOpen();
mIconOverlay = vmi->getIconOverlay();
mIsFavorite = vmi->isFavorite() && !vmi->isItemInTrash();
if (mRoot->useLabelSuffix())
{
// Very Expensive!
@ -463,6 +498,10 @@ S32 LLFolderViewItem::arrange( S32* width, S32* height )
}
mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel.c_str()) + getLabelFontForStyle(LLFontGL::NORMAL)->getWidth(mLabelSuffix.c_str()) + mLabelPaddingRight;
mLabelWidthDirty = false;
if (mIsFavorite)
{
mLabelWidth += FAVORITE_IMAGE_SIZE + FAVORITE_IMAGE_PAD;
}
}
*width = llmax(*width, mLabelWidth);
@ -589,10 +628,15 @@ void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags)
void LLFolderViewItem::openItem( void )
{
if (mAllowWear || !getViewModelItem()->isItemWearable())
if (!mMarketplaceItem || !getViewModelItem()->isItemWearable())
{
getViewModelItem()->openItem();
}
else if (mMarketplaceItem)
{
// Wearing an object from any listing, active or not, is verbotten
LLNotificationsUtil::add("AlertMerchantListingCannotWear");
}
}
void LLFolderViewItem::rename(const std::string& new_name)
@ -837,6 +881,45 @@ void LLFolderViewItem::drawOpenFolderArrow()
}
}
void LLFolderViewItem::drawFavoriteIcon()
{
static LLUICachedControl<bool> draw_star("InventoryFavoritesUseStar", true);
static LLUICachedControl<bool> draw_hollow_star("InventoryFavoritesUseHollowStar", true);
LLUIImage* favorite_image = nullptr;
if (draw_star && mIsFavorite)
{
favorite_image = sFavoriteImg;
}
else if (draw_hollow_star && mHasFavorites && !isOpen())
{
favorite_image = sFavoriteContentImg;
}
if (favorite_image)
{
S32 x_offset = 0;
LLScrollContainer* scroll = mRoot->getScrollContainer();
if (scroll)
{
S32 width = scroll->getVisibleContentRect().getWidth();
S32 offset = scroll->getDocPosHorizontal();
x_offset = width + offset;
}
else
{
x_offset = getRect().getWidth();
}
gl_draw_scaled_image(
x_offset - FAVORITE_IMAGE_SIZE - FAVORITE_IMAGE_PAD,
getRect().getHeight() - mItemHeight + FAVORITE_IMAGE_PAD,
FAVORITE_IMAGE_SIZE,
FAVORITE_IMAGE_SIZE,
favorite_image->getImage(),
sFgColor);
}
}
/*virtual*/ bool LLFolderViewItem::isHighlightAllowed()
{
return mIsSelected;
@ -994,6 +1077,7 @@ void LLFolderViewItem::draw()
{
drawOpenFolderArrow();
}
drawFavoriteIcon();
drawHighlight(show_context, filled, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor);
@ -1065,7 +1149,20 @@ void LLFolderViewItem::draw()
}
}
LLColor4 color = (mIsSelected && filled) ? mFontHighlightColor : mFontColor;
static LLUICachedControl<bool> highlight_color("InventoryFavoritesColorText", true);
LLColor4 color;
if (mIsSelected && filled)
{
color = mFontHighlightColor;
}
else if (mIsFavorite && highlight_color)
{
color = sFavoriteColor;
}
else
{
color = mFontColor;
}
if (isFadeItem())
{
@ -1203,7 +1300,8 @@ LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ):
mIsFolderComplete(false), // folder might have children that are not loaded yet.
mAreChildrenInited(false), // folder might have children that are not built yet.
mLastArrangeGeneration( -1 ),
mLastCalculatedWidth(0)
mLastCalculatedWidth(0),
mFavoritesDirtyFlags(0)
{
}
@ -1229,6 +1327,11 @@ LLFolderViewFolder::~LLFolderViewFolder( void )
// The LLView base class takes care of object destruction. make sure that we
// don't have mouse or keyboard focus
gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
if (mFavoritesDirtyFlags)
{
gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, this);
}
}
// addToFolder() returns true if it succeeds. false otherwise
@ -1872,6 +1975,140 @@ bool LLFolderViewFolder::isMovable()
return true;
}
void LLFolderViewFolder::updateHasFavorites(bool new_childs_value)
{
if (mFavoritesDirtyFlags == 0)
{
gIdleCallbacks.addFunction(&LLFolderViewFolder::onIdleUpdateFavorites, this);
}
if (new_childs_value)
{
mFavoritesDirtyFlags |= FAVORITE_ADDED;
}
else
{
mFavoritesDirtyFlags |= FAVORITE_REMOVED;
}
}
void LLFolderViewFolder::onIdleUpdateFavorites(void* data)
{
LLFolderViewFolder* self = reinterpret_cast<LLFolderViewFolder*>(data);
if (self->mFavoritesDirtyFlags == 0)
{
// already processed either on previous run or by a different callback
gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, self);
return;
}
if (self->getViewModelItem()->isItemInTrash())
{
// do not display favorite-stars in trash
self->mFavoritesDirtyFlags = 0;
gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, self);
return;
}
if (self->mFavoritesDirtyFlags == FAVORITE_ADDED)
{
if (!self->mHasFavorites)
{
// propagate up, exclude root
LLFolderViewFolder* parent = self;
while (parent
&& (!parent->hasFavorites() || parent->mFavoritesDirtyFlags)
&& !parent->getViewModelItem()->isAgentInventoryRoot())
{
parent->setHasFavorites(true);
if (parent->mFavoritesDirtyFlags)
{
// 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 = parent->getParentFolder();
}
}
else
{
// already up to date
self->mFavoritesDirtyFlags = 0;
gIdleCallbacks.deleteFunction(&LLFolderViewFolder::onIdleUpdateFavorites, self);
}
}
else if (self->mFavoritesDirtyFlags > FAVORITE_ADDED)
{
// full check
LLFolderViewFolder* parent = self;
while (parent && !parent->getViewModelItem()->isAgentInventoryRoot())
{
bool has_favorites = false;
for (items_t::iterator iter = parent->mItems.begin();
iter != parent->mItems.end();)
{
items_t::iterator iit = iter++;
if ((*iit)->isFavorite())
{
has_favorites = true;
break;
}
}
for (folders_t::iterator iter = parent->mFolders.begin();
iter != parent->mFolders.end() && !has_favorites;)
{
folders_t::iterator fit = iter++;
if ((*fit)->isFavorite() || (*fit)->hasFavorites())
{
has_favorites = true;
break;
}
}
if (!has_favorites)
{
if (parent->hasFavorites())
{
parent->setHasFavorites(false);
}
else
{
// Nothing changed
break;
}
}
else
{
// propagate up, exclude root
while (parent
&& (!parent->hasFavorites() || parent->mFavoritesDirtyFlags)
&& !parent->getViewModelItem()->isAgentInventoryRoot())
{
parent->setHasFavorites(true);
if (parent->mFavoritesDirtyFlags)
{
// 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 = parent->getParentFolder();
}
break;
}
if (parent->mFavoritesDirtyFlags)
{
// 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 = parent->getParentFolder();
}
}
}
bool LLFolderViewFolder::isRemovable()
{

View File

@ -50,7 +50,9 @@ class LLFolderViewItem : public LLView
public:
struct Params : public LLInitParam::Block<Params, LLView::Params>
{
Optional<LLUIImage*> folder_arrow_image,
Optional<LLUIImage*> favorite_image,
favorite_content_image,
folder_arrow_image,
selection_image;
Mandatory<LLFolderView*> root;
Mandatory<LLFolderViewModelItem*> listener;
@ -60,7 +62,7 @@ public:
item_top_pad;
Optional<time_t> creation_date;
Optional<bool> allow_wear;
Optional<bool> marketplace_item;
Optional<bool> allow_drop;
Optional<LLUIColor> font_color;
@ -97,6 +99,8 @@ protected:
LLWString mLabel;
S32 mLabelWidth;
bool mLabelWidthDirty;
bool mIsFavorite;
bool mHasFavorites;
S32 mLabelPaddingRight;
LLFolderViewFolder* mParentFolder;
LLPointer<LLFolderViewModelItem> mViewModelItem;
@ -126,7 +130,7 @@ protected:
mIsCurSelection,
mDragAndDropTarget,
mIsMouseOverTitle,
mAllowWear,
mMarketplaceItem,
mAllowDrop,
mSingleFolderMode,
mDoubleClickOverride,
@ -137,6 +141,7 @@ protected:
LLUIColor mFontColor;
LLUIColor mFontHighlightColor;
static bool sColorSetInitialized;
// <FS:Ansariel> Inventory specials
bool mForInventory;
@ -153,9 +158,11 @@ protected:
static LLUIColor sFilterTextColor;
static LLUIColor sSuffixColor;
static LLUIColor sSearchStatusColor;
static LLUIColor sFavoriteColor;
// <FS:Ansariel> Special for protected items
static LLUIColor sProtectedColor;
// this is an internal method used for adding items to folders. A
// no-op at this level, but reimplemented in derived classes.
virtual void addItem(LLFolderViewItem*) { }
@ -218,6 +225,8 @@ public:
// Returns true is this object and all of its children can be moved
virtual bool isMovable();
bool isFavorite() const { return mIsFavorite; }
// destroys this item recursively
virtual void destroyView();
@ -308,6 +317,7 @@ public:
// virtual void handleDropped();
virtual void draw();
void drawOpenFolderArrow();
void drawFavoriteIcon();
void drawHighlight(bool showContent, bool hasKeyboardFocus, const LLUIColor& selectColor, const LLUIColor& flashColor, const LLUIColor& outlineColor, const LLUIColor& mouseOverColor);
void drawLabel(const LLFontGL* font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x);
virtual bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
@ -326,6 +336,8 @@ private:
static S32 sTopPad;
static LLUIImagePtr sFolderArrowImg;
static LLUIImagePtr sSelectionImg;
static LLUIImagePtr sFavoriteImg;
static LLUIImagePtr sFavoriteContentImg;
static LLFontGL* sSuffixFont;
LLFontVertexBuffer mLabelFontBuffer;
@ -415,6 +427,18 @@ public:
// Returns true is this object and all of its children can be moved
virtual bool isMovable();
bool isFavorite() const { return mIsFavorite; }
bool hasFavorites() const { return mHasFavorites; }
void setHasFavorites(bool val) { mHasFavorites = val; }
void updateHasFavorites(bool new_childs_value);
private:
static void onIdleUpdateFavorites(void* data);
constexpr static S32 FAVORITE_ADDED = 1;
constexpr static S32 FAVORITE_REMOVED = 2;
S32 mFavoritesDirtyFlags { 0 };
public:
// destroys this folder, and all children
virtual void destroyView();
void destroyRoot();

View File

@ -163,6 +163,7 @@ public:
virtual void navigateToFolder(bool new_window = false, bool change_mode = false) = 0;
virtual bool isFavorite() const = 0;
virtual bool isItemWearable() const { return false; }
virtual bool isItemRenameable() const = 0;
@ -172,6 +173,7 @@ public:
virtual void move( LLFolderViewModelItem* parent_listener ) = 0;
virtual bool isItemRemovable( bool check_worn = true ) const = 0; // Can be destroyed
virtual bool isItemInTrash(void) const = 0;
virtual bool removeItem() = 0;
virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) = 0;
@ -184,6 +186,9 @@ public:
virtual void pasteFromClipboard() = 0;
virtual void pasteLinkFromClipboard() = 0;
virtual bool isAgentInventory() const = 0;
virtual bool isAgentInventoryRoot() const = 0;
virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0;
virtual bool potentiallyVisible() = 0; // is the item definitely visible or we haven't made up our minds yet?
@ -220,6 +225,7 @@ public:
virtual S32 getSortVersion() = 0;
virtual void setSortVersion(S32 version) = 0;
virtual void setParent(LLFolderViewModelItem* parent) = 0;
virtual const LLFolderViewModelItem* getParent() = 0;
virtual bool hasParent() = 0;
// <FS:ND/>
@ -259,14 +265,14 @@ public:
mChildren.clear();
}
void requestSort() { mSortVersion = -1; }
S32 getSortVersion() { return mSortVersion; }
void setSortVersion(S32 version) { mSortVersion = version;}
void requestSort() override { mSortVersion = -1; }
S32 getSortVersion() override { return mSortVersion; }
void setSortVersion(S32 version) override { mSortVersion = version;}
S32 getLastFilterGeneration() const { return mLastFilterGeneration; }
S32 getLastFilterGeneration() const override { return mLastFilterGeneration; }
S32 getLastFolderFilterGeneration() const { return mLastFolderFilterGeneration; }
S32 getMarkedDirtyGeneration() const { return mMarkedDirtyGeneration; }
void dirtyFilter()
S32 getMarkedDirtyGeneration() const override { return mMarkedDirtyGeneration; }
void dirtyFilter() override
{
if(mMarkedDirtyGeneration < 0)
{
@ -281,7 +287,7 @@ public:
mParent->dirtyFilter();
}
}
void dirtyDescendantsFilter()
void dirtyDescendantsFilter() override
{
mMostFilteredDescendantGeneration = -1;
if (mParent)
@ -289,13 +295,13 @@ public:
mParent->dirtyDescendantsFilter();
}
}
bool hasFilterStringMatch();
std::string::size_type getFilterStringOffset();
std::string::size_type getFilterStringSize();
bool hasFilterStringMatch() override;
std::string::size_type getFilterStringOffset() override;
std::string::size_type getFilterStringSize() override;
typedef std::list<LLFolderViewModelItem*> child_list_t;
typedef std::list<LLPointer<LLFolderViewModelItem> > child_list_t;
virtual void addChild(LLFolderViewModelItem* child)
virtual void addChild(LLFolderViewModelItem* child) override
{
mChildren.push_back(child);
child->setParent(this);
@ -303,15 +309,15 @@ public:
requestSort();
}
virtual void removeChild(LLFolderViewModelItem* child)
virtual void removeChild(LLFolderViewModelItem* child) override final
{
mChildren.remove(child);
child->setParent(NULL);
mChildren.remove(child);
dirtyDescendantsFilter();
dirtyFilter();
}
virtual void clearChildren()
virtual void clearChildren() override
{
// We are working with models that belong to views as LLPointers, clean the list, let poiters handle the rest
std::for_each(mChildren.begin(), mChildren.end(), [](LLFolderViewModelItem* c) {c->setParent(NULL); });
@ -324,7 +330,7 @@ public:
child_list_t::const_iterator getChildrenEnd() const { return mChildren.end(); }
child_list_t::size_type getChildrenCount() const { return mChildren.size(); }
void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0)
void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) override
{
mPassedFilter = passed;
mLastFilterGeneration = filter_generation;
@ -333,20 +339,20 @@ public:
mMarkedDirtyGeneration = -1;
}
void setPassedFolderFilter(bool passed, S32 filter_generation)
void setPassedFolderFilter(bool passed, S32 filter_generation) override
{
mPassedFolderFilter = passed;
mLastFolderFilterGeneration = filter_generation;
}
virtual bool potentiallyVisible()
virtual bool potentiallyVisible() override
{
return passedFilter() // we've passed the filter
|| (getLastFilterGeneration() < mRootViewModel.getFilter().getFirstSuccessGeneration()) // or we don't know yet
|| descendantsPassedFilter();
}
virtual bool passedFilter(S32 filter_generation = -1)
virtual bool passedFilter(S32 filter_generation = -1) override
{
if (filter_generation < 0)
{
@ -357,7 +363,7 @@ public:
return passed_folder_filter && (passed_filter || descendantsPassedFilter(filter_generation));
}
virtual bool descendantsPassedFilter(S32 filter_generation = -1)
virtual bool descendantsPassedFilter(S32 filter_generation = -1) override
{
if (filter_generation < 0)
{
@ -366,10 +372,10 @@ public:
return mMostFilteredDescendantGeneration >= filter_generation;
}
protected:
virtual void setParent(LLFolderViewModelItem* parent) { mParent = parent; }
virtual bool hasParent() { return mParent != NULL; }
virtual void setParent(LLFolderViewModelItem* parent) override final { mParent = parent; }
virtual const LLFolderViewModelItem* getParent() override { return mParent; };
virtual bool hasParent() override { return mParent != NULL; }
// <FS:ND/>
virtual LLFolderViewModelItem* getParent() const { return mParent; }
@ -389,7 +395,7 @@ protected:
LLFolderViewModelItem* mParent;
LLFolderViewModelInterface& mRootViewModel;
void setFolderViewItem(LLFolderViewItem* folder_view_item) { mFolderViewItem = folder_view_item;}
void setFolderViewItem(LLFolderViewItem* folder_view_item) override { mFolderViewItem = folder_view_item;}
LLFolderViewItem* mFolderViewItem;
};
@ -403,15 +409,15 @@ public:
mFolderView(NULL)
{}
virtual void requestSortAll()
virtual void requestSortAll() override
{
// sort everything
mTargetSortVersion++;
}
virtual std::string getStatusText(bool is_empty_folder = false);
virtual void filter();
virtual std::string getStatusText(bool is_empty_folder = false) override;
virtual void filter() override;
void setFolderView(LLFolderView* folder_view) { mFolderView = folder_view;}
void setFolderView(LLFolderView* folder_view) override { mFolderView = folder_view;}
protected:
bool needsSort(class LLFolderViewModelItem* item);
@ -441,14 +447,14 @@ public:
virtual const SortType& getSorter() const { return *mSorter; }
virtual void setSorter(const SortType& sorter) { mSorter.reset(new SortType(sorter)); requestSortAll(); }
virtual FilterType& getFilter() { return *mFilter; }
virtual const FilterType& getFilter() const { return *mFilter; }
virtual FilterType& getFilter() override { return *mFilter; }
virtual const FilterType& getFilter() const override { return *mFilter; }
virtual void setFilter(const FilterType& filter) { mFilter.reset(new FilterType(filter)); }
// By default, we assume the content is available. If a network fetch mechanism is implemented for the model,
// this method needs to be overloaded and return the relevant fetch status.
virtual bool contentsReady() { return true; }
virtual bool isFolderComplete(LLFolderViewFolder* folder) { return true; }
virtual bool contentsReady() override { return true; }
virtual bool isFolderComplete(LLFolderViewFolder* folder) override { return true; }
struct ViewModelCompare
{
@ -469,7 +475,7 @@ public:
const SortType& mSorter;
};
void sort(LLFolderViewFolder* folder)
void sort(LLFolderViewFolder* folder) override
{
if (needsSort(folder->getViewModelItem()))
{

View File

@ -46,6 +46,7 @@
#include "llfocusmgr.h"
#include "llcoord.h"
#include "llwindow.h"
#include "llemojihelper.h"
#include "llcriticaldamp.h"
#include "lluictrlfactory.h"
@ -1432,6 +1433,7 @@ void LLMenuItemBranchDownGL::openMenu( void )
}
else
{
LLEmojiHelper::instance().hideHelper(nullptr, true);
if (branch->getTornOff())
{
LLFloater * branch_parent = dynamic_cast<LLFloater *>(branch->getParent());

View File

@ -28,6 +28,7 @@
#include "llmodaldialog.h"
#include "llemojihelper.h"
#include "llfocusmgr.h"
#include "v4color.h"
#include "v2math.h"
@ -35,6 +36,7 @@
#include "llwindow.h"
#include "llkeyboard.h"
#include "llmenugl.h"
// static
std::list<LLModalDialog*> LLModalDialog::sModalStack;
@ -98,7 +100,7 @@ void LLModalDialog::onOpen(const LLSD& key)
{
if (mModal)
{
// If Modal, Hide the active modal dialog
// If Modal, hide the active modal dialog
if (!sModalStack.empty())
{
LLModalDialog* front = sModalStack.front();
@ -155,6 +157,12 @@ void LLModalDialog::setVisible( bool visible )
{
if( visible )
{
// Hide all menus currently shown
LLMenuGL::sMenuContainer->hideMenus();
// Hide EmojiPicker if it is shown
LLEmojiHelper::instance().hideHelper(nullptr, true);
// This is a modal dialog. It sucks up all mouse and keyboard operations.
gFocusMgr.setMouseCapture( this );
@ -301,7 +309,6 @@ void LLModalDialog::centerOnScreen()
centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY])));
}
// static
void LLModalDialog::onAppFocusLost()
{
@ -333,6 +340,7 @@ void LLModalDialog::onAppFocusGained()
}
}
// static
void LLModalDialog::shutdownModals()
{
// This method is only for use during app shutdown. ~LLModalDialog()

View File

@ -1215,6 +1215,14 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s
S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length)
{
S32 text_length = (S32)getLength();
if (pos >= text_length || pos < 0)
{
return 0; // nothing to remove
}
// Clamp length to not go past the end of the text
length = std::min(length, text_length - pos);
beforeValueChange();
segment_set_t::iterator seg_iter = getSegIterContaining(pos);
while(seg_iter != mSegments.end())
@ -2407,6 +2415,7 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
registrar.add("Url.ReportAbuse", boost::bind(&LLUrlAction::reportAbuse, url));
registrar.add("Url.SendIM", boost::bind(&LLUrlAction::sendIM, url));
registrar.add("Url.ShowOnMap", boost::bind(&LLUrlAction::showLocationOnMap, url));
registrar.add("Url.ShowParcelOnMap", boost::bind(&LLUrlAction::showParcelOnMap, url));
registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url));
registrar.add("Url.CopyUrl", boost::bind(&LLUrlAction::copyURLToClipboard, url));

View File

@ -276,6 +276,8 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :
mEnableTooltipPaste(p.enable_tooltip_paste),
mPassDelete(false),
mKeepSelectionOnReturn(false),
mSelectAllOnFocusReceived(false),
mSelectedOnFocusReceived(false),
mEnableTabRemove(p.enable_tab_remove) // <FS:Ansariel> FIRE-15591: Optional tab remove
{
mSourceID.generate();
@ -450,6 +452,7 @@ void LLTextEditor::selectNext(const std::string& search_text_in, bool case_insen
// [/SL:KB]
mIsSelecting = true;
mSelectedOnFocusReceived = false;
mSelectionEnd = mCursorPos;
mSelectionStart = llmin((S32)getLength(), (S32)(mCursorPos + search_text.size()));
}
@ -741,6 +744,13 @@ bool LLTextEditor::canSelectAll() const
return true;
}
//virtual
void LLTextEditor::deselect()
{
LLTextBase::deselect();
mSelectedOnFocusReceived = false;
}
// virtual
void LLTextEditor::selectAll()
{
@ -759,6 +769,11 @@ void LLTextEditor::selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_p
endSelection();
}
void LLTextEditor::setSelectAllOnFocusReceived(bool b)
{
mSelectAllOnFocusReceived = b;
}
void LLTextEditor::insertEmoji(llwchar emoji)
{
static LLUICachedControl<bool> useBWEmojis( "FSUseBWEmojis", false); // <FS:Beq/> Add B&W emoji font support
@ -861,8 +876,16 @@ bool LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
// Delay cursor flashing
resetCursorBlink();
mSelectedOnFocusReceived = false;
if (handled && !gFocusMgr.getMouseCapture())
{
if (!mask && mSelectAllOnFocusReceived)
{
mIsSelecting = false;
mSelectionStart = getLength();
mSelectionEnd = 0;
mSelectedOnFocusReceived = true;
}
gFocusMgr.setMouseCapture( this );
}
return handled;
@ -2400,6 +2423,11 @@ void LLTextEditor::focusLostHelper()
gEditMenuHandler = NULL;
}
if (mSelectedOnFocusReceived)
{
deselect();
}
if (mCommitOnFocusLost)
{
onCommit();

View File

@ -145,8 +145,10 @@ public:
virtual bool canDoDelete() const;
virtual void selectAll();
virtual bool canSelectAll() const;
virtual void deselect();
void selectByCursorPosition(S32 prev_cursor_pos, S32 next_cursor_pos);
void setSelectAllOnFocusReceived(bool b);
virtual bool canLoadOrSaveToFile();
@ -356,6 +358,8 @@ private:
bool mEnableTooltipPaste;
bool mPassDelete;
bool mKeepSelectionOnReturn; // disabling of removing selected text after pressing of Enter
bool mSelectAllOnFocusReceived;
bool mSelectedOnFocusReceived;
bool mEnableTabRemove; // <FS:Ansariel> FIRE-15591: Optional tab remove
LLUUID mSourceID;

View File

@ -30,6 +30,7 @@
#include "llview.h"
#include "llwindow.h"
#include "llurlregistry.h"
#include "v3dmath.h"
// global state for the callback functions
@ -128,6 +129,23 @@ void LLUrlAction::showLocationOnMap(std::string url)
}
}
void LLUrlAction::showParcelOnMap(std::string url)
{
LLSD path_array = LLURI(url).pathArray();
auto path_parts = path_array.size();
if (path_parts < 3) // no parcel id
{
LL_WARNS() << "Global coordinates are missing in url: [" << url << "]" << LL_ENDL;
return;
}
LLVector3d parcel_pos = LLUrlEntryParcel::getParcelPos(LLUUID(LLURI::unescape(path_array[2])));
std::ostringstream pos;
pos << parcel_pos.mdV[VX] << '/' << parcel_pos.mdV[VY] << '/' << parcel_pos.mdV[VZ];
executeSLURL("secondlife:///app/worldmap_global/" + pos.str());
}
void LLUrlAction::copyURLToClipboard(std::string url)
{
LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(url));
@ -142,6 +160,16 @@ void LLUrlAction::copyLabelToClipboard(std::string url)
}
}
std::string LLUrlAction::getURLLabel(std::string url)
{
LLUrlMatch match;
if (LLUrlRegistry::instance().findUrl(url, match))
{
return match.getLabel();
}
return "";
}
void LLUrlAction::showProfile(std::string url)
{
// Get id from 'secondlife:///app/{cmd}/{id}/{action}'

View File

@ -63,6 +63,8 @@ public:
/// if the Url specifies an SL location, show it on a map
static void showLocationOnMap(std::string url);
static void showParcelOnMap(std::string url);
/// perform the appropriate action for left-clicking on a Url
static void clickAction(std::string url, bool trusted_content);
@ -72,6 +74,8 @@ public:
/// copy a Url to the clipboard
static void copyURLToClipboard(std::string url);
static std::string getURLLabel(std::string url);
/// if the Url specifies an SL command in the form like 'app/{cmd}/{id}/*', show its profile
static void showProfile(std::string url);
static std::string getUserID(std::string url);

View File

@ -41,11 +41,7 @@
#include "lluicolortable.h"
#include "message.h"
#include "llexperiencecache.h"
// <FS:AW> hop:// protocol>
//#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))"
#define APP_HEADER_REGEX "(((hop|x-grid-location-info)://[-\\w\\.\\:\\@]+/app)|((hop|secondlife):///app))"
// </FS:AW>
#include "v3dmath.h"
// Utility functions
std::string localize_slapp_label(const std::string& url, const std::string& full_name);
@ -1202,6 +1198,7 @@ LLUUID LLUrlEntryParcel::sSessionID(LLUUID::null);
LLHost LLUrlEntryParcel::sRegionHost;
bool LLUrlEntryParcel::sDisconnected(false);
std::set<LLUrlEntryParcel*> LLUrlEntryParcel::sParcelInfoObservers;
std::map<LLUUID, LLVector3d> LLUrlEntryParcel::sParcelPos;
///
/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
@ -1294,6 +1291,20 @@ void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data)
url_entry->onParcelInfoReceived(parcel_data.parcel_id.asString(), label);
}
}
if (sParcelPos.find(parcel_data.parcel_id) == sParcelPos.end())
{
sParcelPos[parcel_data.parcel_id] = LLVector3d(parcel_data.global_x, parcel_data.global_y, parcel_data.global_z);
}
}
// static
LLVector3d LLUrlEntryParcel::getParcelPos(const LLUUID& parcel_id)
{
if (sParcelPos.find(parcel_id) != sParcelPos.end())
{
return sParcelPos[parcel_id];
}
return LLVector3d();
}
//

View File

@ -41,6 +41,12 @@
#include <map>
class LLAvatarName;
class LLVector3d;
// <FS:AW> hop:// protocol>
//#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))"
#define APP_HEADER_REGEX "(((hop|x-grid-location-info)://[-\\w\\.\\:\\@]+/app)|((hop|secondlife):///app))"
// </FS:AW>
typedef boost::signals2::signal<void (const std::string& url,
const std::string& label,
@ -478,6 +484,8 @@ public:
// Processes parcel label and triggers notifying observers.
static void processParcelInfo(const LLParcelData& parcel_data);
static LLVector3d getParcelPos(const LLUUID& parcel_id);
// Next setters are used to update agent and viewer connection information
// upon events like user login, viewer disconnect and user changing region host.
// These setters are made public to be accessible from newview and should not be
@ -491,6 +499,7 @@ private:
static LLHost sRegionHost;
static bool sDisconnected;
static std::set<LLUrlEntryParcel*> sParcelInfoObservers;
static std::map<LLUUID, LLVector3d> sParcelPos;
};
///

View File

@ -670,7 +670,10 @@ LLWebRTCPeerConnectionInterface *LLWebRTCImpl::newPeerConnection()
peerConnection->init(this);
mPeerConnections.emplace_back(peerConnection);
peerConnection->enableSenderTracks(!mMute);
// Should it really start disabled?
// Seems like something doesn't get the memo and senders need to be reset later
// to remove the voice indicator from taskbar
peerConnection->enableSenderTracks(false);
if (mPeerConnections.empty())
{
setRecording(true);
@ -704,7 +707,7 @@ void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnectionInterface* peer_conn
LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl() :
mWebRTCImpl(nullptr),
mPeerConnection(nullptr),
mMute(true),
mMute(MUTE_INITIAL),
mAnswerReceived(false)
{
}
@ -739,6 +742,19 @@ void LLWebRTCPeerConnectionImpl::terminate()
}
}
// to remove 'Secondlife is recording' icon from taskbar
// if user was speaking
auto senders = mPeerConnection->GetSenders();
for (auto& sender : senders)
{
auto track = sender->track();
if (track)
{
track->set_enabled(false);
}
}
mPeerConnection->SetAudioRecording(false);
mPeerConnection->Close();
if (mLocalStream)
{
@ -828,6 +844,7 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti
audioOptions.auto_gain_control = true;
audioOptions.echo_cancellation = true;
audioOptions.noise_suppression = true;
audioOptions.init_recording_on_send = false;
mLocalStream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream");
@ -887,6 +904,7 @@ void LLWebRTCPeerConnectionImpl::enableSenderTracks(bool enable)
// set_enabled shouldn't be done on the worker thread.
if (mPeerConnection)
{
mPeerConnection->SetAudioRecording(enable);
auto senders = mPeerConnection->GetSenders();
for (auto &sender : senders)
{
@ -932,12 +950,23 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp)
void LLWebRTCPeerConnectionImpl::setMute(bool mute)
{
mMute = mute;
EMicMuteState new_state = mute ? MUTE_MUTED : MUTE_UNMUTED;
if (new_state == mMute)
{
return; // no change
}
bool force_reset = mMute == MUTE_INITIAL && mute;
bool enable = !mute;
mMute = new_state;
mWebRTCImpl->PostSignalingTask(
[this]()
[this, force_reset, enable]()
{
if (mPeerConnection)
{
// SetAudioRecording must be called before enabling/disabling tracks.
mPeerConnection->SetAudioRecording(enable);
auto senders = mPeerConnection->GetSenders();
RTC_LOG(LS_INFO) << __FUNCTION__ << (mMute ? "disabling" : "enabling") << " streams count " << senders.size();
@ -946,7 +975,14 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute)
auto track = sender->track();
if (track)
{
track->set_enabled(!mMute);
if (force_reset)
{
// Force notify observers
// Was it disabled too early?
// Without this microphone icon in Win's taskbar will stay
track->set_enabled(true);
}
track->set_enabled(enable);
}
}
}
@ -955,7 +991,17 @@ void LLWebRTCPeerConnectionImpl::setMute(bool mute)
void LLWebRTCPeerConnectionImpl::resetMute()
{
setMute(mMute);
switch(mMute)
{
case MUTE_MUTED:
setMute(true);
break;
case MUTE_UNMUTED:
setMute(false);
break;
default:
break;
}
}
void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume)

View File

@ -417,7 +417,12 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface,
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> mPeerConnectionFactory;
bool mMute;
typedef enum {
MUTE_INITIAL,
MUTE_MUTED,
MUTE_UNMUTED,
} EMicMuteState;
EMicMuteState mMute;
// signaling
std::vector<LLWebRTCSignalingObserver *> mSignalingObserverList;

View File

@ -76,6 +76,11 @@
#pragma comment(lib, "dxguid.lib") // needed for llurlentry test to build on some systems
#pragma comment(lib, "dinput8")
#pragma comment(lib, "UxTheme.lib")
#pragma comment(lib, "Dwmapi.lib")
#include <Uxtheme.h>
#include <dwmapi.h> // needed for DwmSetWindowAttribute to set window theme
const S32 MAX_MESSAGE_PER_UPDATE = 20;
const S32 BITS_PER_PIXEL = 32;
const S32 MAX_NUM_RESOLUTIONS = 32;
@ -85,6 +90,10 @@ const F32 ICON_FLASH_TIME = 0.5f;
#define USER_DEFAULT_SCREEN_DPI 96 // Win7
#endif
#ifndef WM_DWMCOLORIZATIONCOLORCHANGED
#define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320
#endif
// Claim a couple unused GetMessage() message IDs
const UINT WM_DUMMY_(WM_USER + 0x0017);
const UINT WM_POST_FUNCTION_(WM_USER + 0x0018);
@ -104,6 +113,7 @@ static std::thread::id sMainThreadId;
LPWSTR gIconResource = IDI_APPLICATION;
LPWSTR gIconSmallResource = IDI_APPLICATION;
LPDIRECTINPUT8 gDirectInput8;
LLW32MsgCallback gAsyncMsgCallback = NULL;
@ -137,6 +147,17 @@ typedef HRESULT(STDAPICALLTYPE *GetDpiForMonitorType)(
_Out_ UINT *dpiX,
_Out_ UINT *dpiY);
typedef enum PREFERRED_APP_MODE
{
DEFAULT,
ALLOW_DARK,
FORCE_DARK,
FORCE_LIGHT,
MAX
} PREFERRED_APP_MODE;
typedef PREFERRED_APP_MODE(WINAPI* fnSetPreferredAppMode)(PREFERRED_APP_MODE mode);
//
// LLWindowWin32
//
@ -350,10 +371,14 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
LLWindowWin32Thread();
void run() override;
void close() override;
// closes queue, wakes thread, waits until thread closes
void wakeAndDestroy();
// Detroys handles and window
// Either post to or call from window thread
void destroyWindow();
// Closes queue, wakes thread, waits until thread closes.
// Call from main thread
bool wakeAndDestroy();
void glReady()
{
@ -410,6 +435,7 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
// until after some graphics setup. See SL-20177. -Cosmic,2023-09-18
bool mGLReady = false;
bool mGotGLBuffer = false;
LLAtomicBool mDeleteOnExit = false;
};
@ -508,6 +534,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
mFSAASamples = fsaa_samples;
mIconResource = gIconResource;
mIconSmallResource = gIconSmallResource;
mOverrideAspectRatio = 0.f;
mNativeAspectRatio = 0.f;
mInputProcessingPaused = false;
@ -849,6 +876,8 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
// Initialize (boot strap) the Language text input management,
// based on the system's (or user's) default settings.
allowLanguageTextInput(NULL, false);
updateWindowTheme();
setCustomIcon();
}
@ -860,6 +889,7 @@ LLWindowWin32::~LLWindowWin32()
}
delete mDragDrop;
mDragDrop = NULL;
delete [] mWindowTitle;
mWindowTitle = NULL;
@ -871,6 +901,7 @@ LLWindowWin32::~LLWindowWin32()
mWindowClassName = NULL;
delete mWindowThread;
mWindowThread = NULL;
}
void LLWindowWin32::show()
@ -979,7 +1010,7 @@ void LLWindowWin32::close()
// Restore gamma to the system values.
restoreGamma();
LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL;
LL_INFOS("Window") << "Cleanup and destruction of Window Thread" << LL_ENDL;
if (sWindowHandleForMessageBox == mWindowHandle)
{
@ -989,7 +1020,11 @@ void LLWindowWin32::close()
mhDC = NULL;
mWindowHandle = NULL;
mWindowThread->wakeAndDestroy();
if (mWindowThread->wakeAndDestroy())
{
// thread will delete itselfs once done
mWindowThread = NULL;
}
}
bool LLWindowWin32::isValid()
@ -1528,10 +1563,10 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo
LL_INFOS("Window") << "pixel formats done." << LL_ENDL ;
S32 swap_method = 0;
S32 swap_method = 0;
S32 cur_format = 0;
const S32 max_format = (S32)num_formats - 1;
GLint swap_query = WGL_SWAP_METHOD_ARB;
GLint swap_query = WGL_SWAP_METHOD_ARB;
// SL-14705 Fix name tags showing in front of objects with AMD GPUs.
// On AMD hardware we need to iterate from the first pixel format to the end.
@ -1754,6 +1789,7 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw
// important to call DestroyWindow() from the window thread
if (!destroy_window_handler(oldWindowHandle))
{
LL_WARNS("Window") << "Failed to properly close window before recreating it!"
<< LL_ENDL;
}
@ -3112,6 +3148,17 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
WINDOW_IMP_POST(window_imp->mMouseVanish = true);
}
}
// Check if theme-related settings changed
else if (l_param && (wcscmp((LPCWSTR)l_param, L"ImmersiveColorSet") == 0))
{
WINDOW_IMP_POST(window_imp->updateWindowTheme());
}
}
break;
case WM_DWMCOLORIZATIONCOLORCHANGED:
{
WINDOW_IMP_POST(window_imp->updateWindowTheme());
}
break;
@ -3204,10 +3251,14 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_
break;
}
}
else
else // (NULL == window_imp)
{
// (NULL == window_imp)
LL_DEBUGS("Window") << "No window implementation to handle message with, message code: " << U32(u_msg) << LL_ENDL;
if (u_msg == WM_DESTROY)
{
PostQuitMessage(0); // Posts WM_QUIT with an exit code of 0
return 0;
}
}
// pass unhandled messages down to Windows
@ -4735,25 +4786,11 @@ void LLWindowWin32::getWindowChrome( U32 &aChromeW, U32 &aChromeH )
#endif // LL_WINDOWS
inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread()
: LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE, true /*should be false, temporary workaround for SL-18721*/)
: LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE, false)
{
LL::ThreadPool::start();
}
void LLWindowWin32::LLWindowWin32Thread::close()
{
if (!mQueue->isClosed())
{
LL_WARNS() << "Closing window thread without using destroy_window_handler" << LL_ENDL;
LL::ThreadPool::close();
// Workaround for SL-18721 in case window closes too early and abruptly
LLSplashScreen::show();
LLSplashScreen::update("..."); // will be updated later
}
}
/**
* LogChange is to log changes in status while trying to avoid spamming the
* log with repeated messages, especially in a tight loop. It refuses to log
@ -4919,6 +4956,7 @@ void LLWindowWin32::LLWindowWin32Thread::run()
{
sWindowThreadId = std::this_thread::get_id();
LogChange logger("Window");
//as good a place as any to up the MM timer resolution (see ms_sleep)
//attempt to set timer resolution to 1ms
TIMECAPS tc;
@ -4956,6 +4994,7 @@ void LLWindowWin32::LLWindowWin32Thread::run()
", ", msg.wParam, ")");
TranslateMessage(&msg);
DispatchMessage(&msg);
mMessageQueue.pushFront(msg);
}
}
@ -4975,108 +5014,102 @@ void LLWindowWin32::LLWindowWin32Thread::run()
}
#endif
}
destroyWindow();
if (mDeleteOnExit)
{
delete this;
}
}
void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy()
void LLWindowWin32::LLWindowWin32Thread::destroyWindow()
{
if (mWindowHandleThrd != NULL && IsWindow(mWindowHandleThrd))
{
if (mhDCThrd)
{
if (!ReleaseDC(mWindowHandleThrd, mhDCThrd))
{
LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL;
}
mhDCThrd = NULL;
}
// This causes WM_DESTROY to be sent *immediately*
if (!destroy_window_handler(mWindowHandleThrd))
{
LL_WARNS("Window") << "Failed to destroy Window! " << std::hex << GetLastError() << LL_ENDL;
}
}
else
{
// Something killed the window while we were busy destroying gl or handle somehow got broken
LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL;
}
mWindowHandleThrd = NULL;
mhDCThrd = NULL;
}
bool LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy()
{
if (mQueue->isClosed())
{
LL_WARNS() << "Tried to close Queue. Win32 thread Queue already closed." << LL_ENDL;
return;
LL_WARNS("Window") << "Tried to close Queue. Win32 thread Queue already closed." << LL_ENDL;
return false;
}
// Make sure we don't leave a blank toolbar button.
// Also hiding window now prevents user from suspending it
// via some action (like dragging it around)
ShowWindow(mWindowHandleThrd, SW_HIDE);
// Stop checking budget
mGLReady = false;
// Schedule destruction
// Capture current handle before we lose it
HWND old_handle = mWindowHandleThrd;
post([this]()
{
if (IsWindow(mWindowHandleThrd))
{
if (mhDCThrd)
{
if (!ReleaseDC(mWindowHandleThrd, mhDCThrd))
{
LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL;
}
mhDCThrd = NULL;
}
// This causes WM_DESTROY to be sent *immediately*
if (!destroy_window_handler(mWindowHandleThrd))
{
LL_WARNS("Window") << "Failed to destroy Window! " << std::hex << GetLastError() << LL_ENDL;
}
}
else
{
// Something killed the window while we were busy destroying gl or handle somehow got broken
LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL;
}
mWindowHandleThrd = NULL;
mhDCThrd = NULL;
mGLReady = false;
});
// Clear the user data to prevent callbacks from finding us
if (old_handle)
{
SetWindowLongPtr(old_handle, GWLP_USERDATA, NULL);
}
LL_DEBUGS("Window") << "Closing window's pool queue" << LL_ENDL;
// Signal thread to clean up when done
mDeleteOnExit = true;
LL_INFOS("Window") << "Detaching window's thread" << LL_ENDL;
// Cleanly detach threads instead of joining them to avoid blocking the main thread
// This is acceptable since the thread will self-delete with mDeleteOnExit
// Doing it before close() to make sure thread doesn't die before or mid detach.
for (auto& pair : mThreads)
{
try {
// Only detach if the thread is joinable
if (pair.second.joinable())
{
pair.second.detach();
}
}
catch (const std::system_error& e) {
LL_WARNS("Window") << "Exception detaching thread: " << e.what() << LL_ENDL;
}
}
// Close the queue.
LL_INFOS("Window") << "Closing window's pool queue" << LL_ENDL;
mQueue->close();
// Post a nonsense user message to wake up the thread in
// case it is waiting for a getMessage()
// Wake up the thread if it's stuck in GetMessage()
if (old_handle)
{
WPARAM wparam{ 0xB0B0 };
LL_DEBUGS("Window") << "PostMessage(" << std::hex << old_handle
<< ", " << WM_DUMMY_
<< ", " << wparam << ")" << std::dec << LL_ENDL;
// Use PostMessage to signal thread to wake up
PostMessage(old_handle, WM_DUMMY_, wparam, 0x1337);
}
// There are cases where window will refuse to close,
// can't wait forever on join, check state instead
LLTimer timeout;
timeout.setTimerExpirySec(2.0);
while (!getQueue().done() && !timeout.hasExpired() && mWindowHandleThrd)
{
ms_sleep(100);
}
if (getQueue().done() || mWindowHandleThrd == NULL)
{
// Window is closed, started closing or is cleaning up
// now wait for our single thread to die.
if (mWindowHandleThrd)
{
LL_INFOS("Window") << "Window is closing, waiting on pool's thread to join, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL;
}
else
{
LL_DEBUGS("Window") << "Waiting on pool's thread, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL;
}
for (auto& pair : mThreads)
{
pair.second.join();
}
}
else
{
// Something suspended window thread, can't afford to wait forever
// so kill thread instead
// Ex: This can happen if user starts dragging window arround (if it
// was visible) or a modal notification pops up
LL_WARNS("Window") << "Window is frozen, couldn't perform clean exit" << LL_ENDL;
for (auto& pair : mThreads)
{
// very unsafe
TerminateThread(pair.second.native_handle(), 0);
pair.second.detach();
}
}
LL_DEBUGS("Window") << "thread pool shutdown complete" << LL_ENDL;
LL_INFOS("Window") << "Thread pool shutdown complete" << LL_ENDL;
return true;
}
void LLWindowWin32::post(const std::function<void()>& func)
@ -5137,3 +5170,69 @@ void LLWindowWin32::updateWindowRect()
});
}
}
bool LLWindowWin32::isSystemAppDarkMode()
{
HKEY hKey;
DWORD dwValue = 1; // Default to light theme
DWORD dwSize = sizeof(DWORD);
// Check registry for system theme preference
LSTATUS ret_code =
RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", 0, KEY_READ, &hKey);
if (ERROR_SUCCESS == ret_code)
{
if (RegQueryValueExW(hKey, L"AppsUseLightTheme", NULL, NULL, (LPBYTE)&dwValue, &dwSize) != ERROR_SUCCESS)
{
// If AppsUseLightTheme is not found, check SystemUsesLightTheme
dwSize = sizeof(DWORD);
RegQueryValueExW(hKey, L"SystemUsesLightTheme", NULL, NULL, (LPBYTE)&dwValue, &dwSize);
}
RegCloseKey(hKey);
}
// Return true if dark mode
return dwValue == 0;
}
void LLWindowWin32::updateWindowTheme()
{
bool use_dark_mode = isSystemAppDarkMode();
if (use_dark_mode == mCurrentDarkMode)
{
return;
}
mCurrentDarkMode = use_dark_mode;
HMODULE hUxTheme = LoadLibraryExW(L"uxtheme.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (hUxTheme)
{
auto SetPreferredAppMode = (fnSetPreferredAppMode)GetProcAddress(hUxTheme, "SetPreferredAppMode");
if (SetPreferredAppMode)
{
SetPreferredAppMode(use_dark_mode ? ALLOW_DARK : FORCE_LIGHT);
}
FreeLibrary(hUxTheme);
}
BOOL dark_mode(use_dark_mode);
DwmSetWindowAttribute(mWindowHandle, DWMWA_USE_IMMERSIVE_DARK_MODE, &dark_mode, sizeof(dark_mode));
LL_INFOS("Window") << "Viewer window theme is set to " << (use_dark_mode ? "dark" : "light") << " mode" << LL_ENDL;
}
void LLWindowWin32::setCustomIcon()
{
if (mWindowHandle)
{
HICON hDefaultIcon = LoadIcon(mhInstance, mIconResource);
HICON hSmallIcon = LoadIcon(mhInstance, mIconSmallResource);
mWindowThread->post([=]()
{
SendMessage(mWindowHandle, WM_SETICON, ICON_BIG, (LPARAM)hDefaultIcon);
SendMessage(mWindowHandle, WM_SETICON, ICON_SMALL, (LPARAM)hSmallIcon);
SetClassLongPtr(mWindowHandle, GCLP_HICON, (LONG_PTR)hDefaultIcon);
SetClassLongPtr(mWindowHandle, GCLP_HICONSM, (LONG_PTR)hSmallIcon);
});
}
}

View File

@ -217,6 +217,7 @@ protected:
bool mCustomGammaSet;
LPWSTR mIconResource;
LPWSTR mIconSmallResource;
bool mInputProcessingPaused;
// The following variables are for Language Text Input control.
@ -249,6 +250,11 @@ protected:
RECT mRect;
RECT mClientRect;
void updateWindowTheme();
bool isSystemAppDarkMode();
void setCustomIcon();
bool mCurrentDarkMode { false };
struct LLWindowWin32Thread;
LLWindowWin32Thread* mWindowThread = nullptr;
LLThreadSafeQueue<std::function<void()>> mFunctionQueue;
@ -288,6 +294,7 @@ private:
extern LLW32MsgCallback gAsyncMsgCallback;
extern LPWSTR gIconResource;
extern LPWSTR gIconSmallResource;
S32 OSMessageBoxWin32(const std::string& text, const std::string& caption, U32 type);

View File

@ -165,7 +165,10 @@ LLControlVariable::LLControlVariable(const std::string& name, eControlType type,
{
if ((persist != PERSIST_NO) && mComment.empty())
{
LL_ERRS() << "Must supply a comment for control " << mName << ". Please perform a clean installation!" << LL_ENDL;
// File isn't actually missing, but something is wrong with it
// so the main point is to warn user to reinstall
LLError::LLUserWarningMsg::showMissingFiles();
LL_ERRS() << "Must supply a comment for control " << mName << LL_ENDL;
}
//Push back versus setValue'ing here, since we don't want to call a signal yet
mValues.push_back(initial);

View File

@ -15,6 +15,9 @@ include(CMakeCopyIfDifferent)
include(CubemapToEquirectangularJS)
include(GLIB)
include(DragDrop)
if (USE_DISCORD)
include(Discord)
endif ()
include(EXPAT)
include(FMODSTUDIO)
include(GLOD) # <FS:Beq/> restore GLOD
@ -467,6 +470,7 @@ set(viewer_SOURCE_FILES
llhudeffectpointat.cpp
llhudeffecttrail.cpp
llhudeffectblob.cpp
llhudeffectresetskeleton.cpp
llhudicon.cpp
llhudmanager.cpp
llhudnametag.cpp
@ -492,6 +496,7 @@ set(viewer_SOURCE_FILES
llinventorygallerymenu.cpp
llinventoryicon.cpp
llinventoryitemslist.cpp
llinventorylistener.cpp
llinventorylistitem.cpp
llinventorymodel.cpp
llinventorymodelbackgroundfetch.cpp
@ -1310,6 +1315,7 @@ set(viewer_HEADER_FILES
llhudeffectpointat.h
llhudeffecttrail.h
llhudeffectblob.h
llhudeffectresetskeleton.h
llhudicon.h
llhudmanager.h
llhudnametag.h
@ -1334,6 +1340,7 @@ set(viewer_HEADER_FILES
llinventorygallerymenu.h
llinventoryicon.h
llinventoryitemslist.h
llinventorylistener.h
llinventorylistitem.h
llinventorymodel.h
llinventorymodelbackgroundfetch.h
@ -2025,6 +2032,7 @@ if (WINDOWS)
res/firestorm_icon.BMP
res/firestorm_icon.ico
res-sdl/firestorm_icon.BMP
res/ll_icon_small.ico
res/resource.h
res/toolpickobject.cur
res/toolpickobject2.cur
@ -2319,6 +2327,12 @@ if (WINDOWS)
)
endif (ADDRESS_SIZE EQUAL 64)
if (TARGET ll::discord_sdk)
list(APPEND COPY_INPUT_DEPENDENCIES
${SHARED_LIB_STAGING_DIR}/discord_partner_sdk.dll
)
endif ()
if (TARGET ll::fmodstudio)
list(APPEND COPY_INPUT_DEPENDENCIES
${SHARED_LIB_STAGING_DIR}/fmod.dll
@ -2342,6 +2356,7 @@ if (WINDOWS)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
"--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"
@ -2383,6 +2398,7 @@ if (WINDOWS)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
"--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"
@ -2450,6 +2466,7 @@ if (WINDOWS)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
"--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"
@ -2554,6 +2571,10 @@ target_link_libraries(${VIEWER_BINARY_NAME}
fs::discord # <FS:Ansariel> Discord support
)
if (USE_DISCORD)
target_link_libraries(${VIEWER_BINARY_NAME} ll::discord_sdk )
endif ()
if( TARGET ll::intel_memops )
target_link_libraries(${VIEWER_BINARY_NAME} ll::intel_memops )
endif()
@ -2637,6 +2658,7 @@ if (LINUX)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
"--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"
@ -2665,6 +2687,7 @@ if (LINUX)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
"--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"
@ -2761,6 +2784,7 @@ if (DARWIN)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
"--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"
@ -2799,6 +2823,7 @@ if (DARWIN)
--arch=${ARCH}
--artwork=${ARTWORK_DIR}
"--bugsplat=${BUGSPLAT_DB}"
"--discord=${USE_DISCORD}"
"--fmodstudio=${USE_FMODSTUDIO}"
"--openal=${USE_OPENAL}"
"--tracy=${USE_TRACY}"

View File

@ -1 +1 @@
7.2.0
7.2.1

View File

@ -2767,6 +2767,39 @@
<key>Value</key>
<integer>1</integer>
</map>
<key>EnableDiscord</key>
<map>
<key>Comment</key>
<string>When set, connect to Discord to enable Rich Presence (UNUSED)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>ShowDiscordActivityDetails</key>
<map>
<key>Comment</key>
<string>When set, show avatar name on Discord Rich Presence (UNUSED)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>ShowDiscordActivityState</key>
<map>
<key>Comment</key>
<string>When set, show location on Discord Rich Presence (UNUSED)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>EnableDiskCacheDebugInfo</key>
<map>
<key>Comment</key>
@ -2781,13 +2814,13 @@
<key>DiskCachePercentOfTotal</key>
<map>
<key>Comment</key>
<string>The percent of total cache size (defined by CacheSize) to use for the disk cache (UNUSED)</string>
<string>The percent of total cache size (defined by CacheSize) to use for the disk cache (ex: asset storage, excludes textures) (UNUSED)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<real>40.0</real>
<real>35.0</real>
</map>
<key>DiskCacheDirName</key>
<map>
@ -3650,6 +3683,17 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>DebugSelectionLODs</key>
<map>
<key>Comment</key>
<string>Force selection to show specific LOD, -1 for off, 0 - lowest, 4 - high.</string>
<key>Persist</key>
<integer>0</integer>
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>-1</integer>
</map>
<key>AnimatedObjectsAllowLeftClick</key>
<map>
<key>Comment</key>
@ -8111,10 +8155,10 @@
<key>Value</key>
<integer>0</integer>
</map>
<key>FSPoserResetBaseRotationOnEdit</key>
<key>FSPoserPelvisUnlockedForBvhSave</key>
<map>
<key>Comment</key>
<string>Whether to reset the base-rotation of a joint to zero when a user edits it.</string>
<string>Whether the mPelvis joint should be position/rotationally locked when a BVH is created.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@ -13194,6 +13238,17 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>1</integer>
</map>
<key>ObscureBalanceInStatusBar</key>
<map>
<key>Comment</key>
<string>If true, balance will be shows as '*' (UNUSED)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>RenderUIBuffer</key>
<map>
<key>Comment</key>
@ -18154,7 +18209,7 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>FullScreen</key>
<map>
<key>Comment</key>
<string>run a fullscreen session</string>
<string>Run a fullscreen session. MacOS not supported</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@ -18664,10 +18719,32 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>1</integer>
</map>
<key>OutfitGallerySortByName</key>
<key>OutfitGallerySortOrder</key>
<map>
<key>Comment</key>
<string>Always sort outfits by name in Outfit Gallery</string>
<string>Gallery sorting: 0 - sort outfits by name, 1 - images frst, 2 - favorites first</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>OutfitListSortOrder</key>
<map>
<key>Comment</key>
<string>How outfit list in Avatar's floater is sorted. 0 - by name 1 - favorites to top</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>OutfitListFilterFullList</key>
<map>
<key>Comment</key>
<string> 0 - show only matches. 1 - show all items in outfit as long as outfit or item inside matches.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@ -23353,6 +23430,50 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>0</integer>
</map>
<key>InventoryFavoritesUseStar</key>
<map>
<key>Comment</key>
<string>Show star near favorited items in inventory</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>InventoryFavoritesUseHollowStar</key>
<map>
<key>Comment</key>
<string>Show star near folders that contain favorites</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>InventoryFavoritesColorText</key>
<map>
<key>Comment</key>
<string>render favorite items using InventoryFavoriteText as color</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>InventoryAddAttachmentBehavior</key>
<map>
<key>Comment</key>
<string>Defines behavior when hitting return on an inventory item</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>StatsReportMaxDuration</key>
<map>
<key>Comment</key>
@ -23463,6 +23584,50 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>31</integer>
</map>
<key>EnableSelectionHints</key>
<map>
<key>Comment</key>
<string>Whether or not to send editing hints to animate the arm when editing an object. (UNUSED)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>EnableLookAtTarget</key>
<map>
<key>Comment</key>
<string>Whether or not to animate the avatar head and send look at targets when moving the cursor or focusing on objects (UNUSED)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>LimitLookAtTarget</key>
<map>
<key>Comment</key>
<string>Whether or not to clamp the look at targets around the avatar head before sending (UNUSED)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>LimitLookAtTargetDistance</key>
<map>
<key>Comment</key>
<string>Distance to limit look at target to (UNUSED)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
<integer>2</integer>
</map>
<key>FSNetMapPhantomOpacity</key>
<map>
@ -26762,5 +26927,27 @@ Change of this parameter will affect the layout of buttons in notification toast
<integer>0</integer>
</map>
<!-- </FS:Zi> Area Search Defaults -->
<key>FSHideHelpButtons</key>
<map>
<key>Comment</key>
<string>When enabled, hides the help button from floaters (requires restart)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSResetSkeletonOnStandUp</key>
<map>
<key>Comment</key>
<string>Resets own avatar skeleton upon standing up and sends the reset to all surrounding avatars</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
</map>
</llsd>

View File

@ -602,7 +602,7 @@
<key>KeepConversationLogTranscripts</key>
<map>
<key>Comment</key>
<string>Keep a conversation log and transcripts</string>
<string>Keep a conversation log and transcripts 2 - both, 1 - logs, 0 - none</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>

View File

@ -1087,7 +1087,7 @@ protected:
menu->setItemEnabled("Chat History", false);
menu->setItemEnabled("Add Contact Set", false);
menu->setItemEnabled("Invite Group", false);
menu->setItemEnabled("Zoom In", false);
menu->setItemEnabled("Zoom In", true);
menu->setItemEnabled("track", false);
menu->setItemEnabled("Share", false);
menu->setItemEnabled("Pay", false);

View File

@ -60,9 +60,9 @@ constexpr char XML_JOINT_DELTAROT_STRING_PREFIX[] = "joint_delta_ro
constexpr char BVH_JOINT_TRANSFORM_STRING_PREFIX[] = "bvh_joint_transform_";
constexpr std::string_view POSER_TRACKPAD_SENSITIVITY_SAVE_KEY = "FSPoserTrackpadSensitivity";
constexpr std::string_view POSER_STOPPOSINGWHENCLOSED_SAVE_KEY = "FSPoserStopPosingWhenClosed";
constexpr std::string_view POSER_RESETBASEROTONEDIT_SAVE_KEY = "FSPoserResetBaseRotationOnEdit";
constexpr std::string_view POSER_SAVEEXTERNALFORMAT_SAVE_KEY = "FSPoserSaveExternalFileAlso";
constexpr std::string_view POSER_SAVECONFIRMREQUIRED_SAVE_KEY = "FSPoserOnSaveConfirmOverwrite";
constexpr std::string_view POSER_UNLOCKPELVISINBVH_SAVE_KEY = "FSPoserPelvisUnlockedForBvhSave";
constexpr char ICON_SAVE_OK[] = "icon_rotation_is_own_work";
constexpr char ICON_SAVE_FAILED[] = "icon_save_failed_button";
@ -193,6 +193,8 @@ bool FSFloaterPoser::postBuild()
mFlipJointBtn = getChild<LLButton>("FlipJoint_avatar");
mRecaptureBtn = getChild<LLButton>("button_RecaptureParts");
mTogglePosingBonesBtn = getChild<LLButton>("toggle_PosingSelectedBones");
mToggleLockWorldRotBtn = getChild<LLButton>("toggle_LockWorldRotation");
mToggleLockWorldRotBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onClickLockWorldRotBtn(); });
mToggleMirrorRotationBtn = getChild<LLButton>("button_toggleMirrorRotation");
mToggleSympatheticRotationBtn = getChild<LLButton>("button_toggleSympatheticRotation");
@ -211,9 +213,10 @@ bool FSFloaterPoser::postBuild()
mMiscJointsPnl = getChild<LLPanel>("misc_joints_panel");
mCollisionVolumesPnl = getChild<LLPanel>("collision_volumes_panel");
mUnlockPelvisInBvhSaveCbx = getChild<LLCheckBoxCtrl>("unlock_pelvis_for_bvh_save_checkbox");
mUnlockPelvisInBvhSaveCbx->setVisible(getSavingToBvh());
mAlsoSaveBvhCbx = getChild<LLCheckBoxCtrl>("also_save_bvh_checkbox");
mResetBaseRotCbx = getChild<LLCheckBoxCtrl>("reset_base_rotation_on_edit_checkbox");
mResetBaseRotCbx->setCommitCallback([this](LLUICtrl*, const LLSD&) { onClickSetBaseRotZero(); });
mAlsoSaveBvhCbx->setCommitCallback([this](LLUICtrl*, const LLSD&) { onClickSavingToBvh(); });
mTrackpadSensitivitySpnr = getChild<LLUICtrl>("trackpad_sensitivity_spinner");
mYawSpnr = getChild<LLUICtrl>("limb_yaw_spinner");
@ -366,10 +369,10 @@ void FSFloaterPoser::onPoseFileSelect()
mPoseSaveNameEditor->setText(name);
bool isDeltaSave = !poseFileStartsFromTeePose(name);
if (isDeltaSave && hasString("LoadDiffLabel"))
mLoadPosesBtn->setLabel(getString("LoadDiffLabel"));
else if (hasString("LoadPoseLabel"))
mLoadPosesBtn->setLabel(getString("LoadPoseLabel"));
if (isDeltaSave)
mLoadPosesBtn->setLabel(tryGetString("LoadDiffLabel"));
else
mLoadPosesBtn->setLabel(tryGetString("LoadPoseLabel"));
}
void FSFloaterPoser::doPoseSave(LLVOAvatar* avatar, const std::string& filename)
@ -386,24 +389,21 @@ void FSFloaterPoser::doPoseSave(LLVOAvatar* avatar, const std::string& filename)
if (getSavingToBvh())
savePoseToBvh(avatar, filename);
if (hasString(ICON_SAVE_OK))
mSavePosesBtn->setImageOverlay(getString(ICON_SAVE_OK), mSavePosesBtn->getImageOverlayHAlign());
mSavePosesBtn->setImageOverlay(tryGetString(ICON_SAVE_OK), mSavePosesBtn->getImageOverlayHAlign());
setSavePosesButtonText(!mPoserAnimator.allBaseRotationsAreZero(avatar));
}
else
{
if (hasString(ICON_SAVE_FAILED))
mSavePosesBtn->setImageOverlay(getString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign());
}
mSavePosesBtn->setImageOverlay(tryGetString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign());
}
}
void FSFloaterPoser::onClickPoseSave()
{
std::string filename = mPoseSaveNameEditor->getValue().asString();
if (filename.empty() && hasString(ICON_SAVE_FAILED))
if (filename.empty())
{
mSavePosesBtn->setImageOverlay(getString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign());
mSavePosesBtn->setImageOverlay(tryGetString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign());
return;
}
@ -444,8 +444,7 @@ void FSFloaterPoser::onClickPoseSave()
void FSFloaterPoser::onMouseLeaveSavePoseBtn()
{
if (hasString("icon_save_button"))
mSavePosesBtn->setImageOverlay(getString("icon_save_button"), mSavePosesBtn->getImageOverlayHAlign());
mSavePosesBtn->setImageOverlay(tryGetString("icon_save_button"), mSavePosesBtn->getImageOverlayHAlign());
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
@ -523,6 +522,7 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
{
std::string bone_name = pj.jointName();
bool posingThisJoint = mPoserAnimator.isPosingAvatarJoint(avatar, pj);
bool jointRotLocked = mPoserAnimator.getRotationIsWorldLocked(avatar, pj);
record[bone_name] = bone_name;
record[bone_name]["enabled"] = posingThisJoint;
@ -538,9 +538,10 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
continue;
record[bone_name]["jointBaseRotationIsZero"] = baseRotationIsZero;
record[bone_name]["rotation"] = rotation.getValue();
record[bone_name]["position"] = position.getValue();
record[bone_name]["scale"] = scale.getValue();
record[bone_name]["rotation"] = rotation.getValue();
record[bone_name]["position"] = position.getValue();
record[bone_name]["scale"] = scale.getValue();
record[bone_name]["worldLocked"] = jointRotLocked;
}
std::string fullSavePath =
@ -691,7 +692,8 @@ void FSFloaterPoser::updatePosedBones(const std::string& jointName)
if (!poserJoint)
return;
mPoserAnimator.recaptureJointAsDelta(avatar, poserJoint, getUiSelectedBoneDeflectionStyle());
bool savingToExternal = getSavingToBvh();
mPoserAnimator.recaptureJointAsDelta(avatar, poserJoint, savingToExternal, getUiSelectedBoneDeflectionStyle());
refreshRotationSlidersAndSpinners();
refreshPositionSlidersAndSpinners();
@ -723,6 +725,9 @@ void FSFloaterPoser::onClickSymmetrize(const S32 ID)
refreshRotationSlidersAndSpinners();
enableOrDisableRedoAndUndoButton();
refreshTrackpadCursor();
if (getSavingToBvh())
refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::onCommitSpinner(const LLUICtrl* spinner, const S32 id)
@ -964,6 +969,8 @@ void FSFloaterPoser::onClickLoadHandPose(bool isRightHand)
mPoserAnimator.loadJointRotation(avatar, poserJoint, true, vec3);
}
}
addBoldToScrollList(mHandJointsScrollList, avatar);
}
catch ( const std::exception& e )
{
@ -1041,6 +1048,7 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
LLQuaternion quat;
bool enabled;
bool setJointBaseRotationToZero;
bool worldLocked;
S32 version = 0;
bool startFromZeroRot = true;
@ -1116,6 +1124,9 @@ void FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
vec3.setValue(control_map["scale"]);
mPoserAnimator.loadJointScale(avatar, poserJoint, loadPositionsAndScalesAsDeltas, vec3);
}
worldLocked = control_map.has("worldLocked") ? control_map["worldLocked"].asBoolean() : false;
mPoserAnimator.setRotationIsWorldLocked(avatar, *poserJoint, worldLocked);
}
}
}
@ -1310,8 +1321,8 @@ LLSD FSFloaterPoser::createRowForJoint(const std::string& jointName, bool isHead
return NULL;
std::string headerValue = "";
if (isHeaderRow && hasString("icon_category"))
headerValue = getString("icon_category");
if (isHeaderRow)
headerValue = tryGetString("icon_category");
std::string jointValue = jointName;
std::string parameterName = (isHeaderRow ? XML_LIST_HEADER_STRING_PREFIX : XML_LIST_TITLE_STRING_PREFIX) + jointName;
@ -1404,6 +1415,8 @@ void FSFloaterPoser::onUndoLastChange()
refreshPositionSlidersAndSpinners();
refreshScaleSlidersAndSpinners();
refreshTrackpadCursor();
if (getSavingToBvh())
refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::onSetAvatarToTpose()
@ -1447,6 +1460,7 @@ void FSFloaterPoser::onResetJoint(const LLSD data)
refreshScaleSlidersAndSpinners();
refreshTrackpadCursor();
enableOrDisableRedoAndUndoButton();
refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::onRedoLastChange()
@ -1474,6 +1488,8 @@ void FSFloaterPoser::onRedoLastChange()
refreshTrackpadCursor();
refreshScaleSlidersAndSpinners();
refreshPositionSlidersAndSpinners();
if (getSavingToBvh())
refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::enableOrDisableRedoAndUndoButton()
@ -1967,7 +1983,7 @@ void FSFloaterPoser::setSelectedJointsRotation(const LLVector3& absoluteRot, con
return;
auto selectedJoints = getUiSelectedPoserJoints();
bool savingToExternal = getWhetherToResetBaseRotationOnEdit();
bool savingToExternal = getSavingToBvh();
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
for (auto item : selectedJoints)
@ -2081,7 +2097,6 @@ void FSFloaterPoser::onJointTabSelect()
refreshTrackpadCursor();
enableOrDisableRedoAndUndoButton();
refreshScaleSlidersAndSpinners();
onClickSetBaseRotZero();
}
E_BoneAxisTranslation FSFloaterPoser::getJointTranslation(const std::string& jointName) const
@ -2348,8 +2363,7 @@ void FSFloaterPoser::refreshTextHighlightingOnJointScrollLists()
void FSFloaterPoser::setSavePosesButtonText(bool setAsSaveDiff)
{
if (hasString("SavePoseLabel") && hasString("SaveDiffLabel"))
setAsSaveDiff ? mSavePosesBtn->setLabel(getString("SaveDiffLabel")) : mSavePosesBtn->setLabel(getString("SavePoseLabel"));
setAsSaveDiff ? mSavePosesBtn->setLabel(tryGetString("SaveDiffLabel")) : mSavePosesBtn->setLabel(tryGetString("SavePoseLabel"));
}
void FSFloaterPoser::addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* avatar)
@ -2361,32 +2375,56 @@ void FSFloaterPoser::addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* ava
return;
std::string iconValue = "";
bool considerExternalFormatSaving = getWhetherToResetBaseRotationOnEdit();
if (considerExternalFormatSaving && hasString("icon_rotation_is_own_work"))
iconValue = getString("icon_rotation_is_own_work");
bool considerExternalFormatSaving = getSavingToBvh();
for (auto listItem : list->getAllData())
{
FSPoserAnimator::FSPoserJoint *userData = static_cast<FSPoserAnimator::FSPoserJoint *>(listItem->getUserdata());
if (!userData)
FSPoserAnimator::FSPoserJoint *poserJoint = static_cast<FSPoserAnimator::FSPoserJoint *>(listItem->getUserdata());
if (!poserJoint)
continue;
if (considerExternalFormatSaving)
{
if (mPoserAnimator.baseRotationIsZero(avatar, *userData))
((LLScrollListText*) listItem->getColumn(COL_ICON))->setValue(iconValue);
else
((LLScrollListText*) listItem->getColumn(COL_ICON))->setValue("");
}
((LLScrollListText*)listItem->getColumn(COL_ICON))->setValue(getScrollListIconForJoint(avatar, *poserJoint));
if (mPoserAnimator.isPosingAvatarJoint(avatar, *userData))
if (mPoserAnimator.isPosingAvatarJoint(avatar, *poserJoint))
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD);
else
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::NORMAL);
}
}
std::string FSFloaterPoser::getScrollListIconForJoint(LLVOAvatar* avatar, FSPoserAnimator::FSPoserJoint joint)
{
if (!avatar)
return "";
if (mPoserAnimator.getRotationIsWorldLocked(avatar, joint))
return tryGetString("icon_rotation_is_world_locked");
if (!getSavingToBvh())
return "";
if (joint.boneType() == COL_VOLUMES)
return tryGetString("icon_rotation_does_not_export");
if (mPoserAnimator.userSetBaseRotationToZero(avatar, joint))
{
if (mPoserAnimator.exportRotationWillLockJoint(avatar, joint))
return tryGetString("icon_rotation_bvh_locked_edited");
else
return tryGetString("icon_rotation_bvh_locked_unedited");
}
else
return tryGetString("icon_rotation_bvh_unlocked");
}
std::string FSFloaterPoser::tryGetString(std::string name)
{
if (name.empty())
return "";
return hasString(name) ? getString(name) : "";
}
bool FSFloaterPoser::savePoseToBvh(LLVOAvatar* avatar, const std::string& poseFileName)
{
if (poseFileName.empty())
@ -2552,7 +2590,9 @@ void FSFloaterPoser::writeBvhMotion(llofstream* fileStream, LLVOAvatar* avatar,
if (!joint)
return;
auto rotation = mPoserAnimator.getJointRotation(avatar, *joint, SWAP_NOTHING, NEGATE_NOTHING);
bool lockPelvisJoint = gSavedSettings.getBOOL(POSER_UNLOCKPELVISINBVH_SAVE_KEY);
auto rotation = mPoserAnimator.getJointExportRotation(avatar, *joint, !lockPelvisJoint);
auto position = mPoserAnimator.getJointPosition(avatar, *joint);
switch (joint->boneType())
@ -2644,12 +2684,39 @@ S32 FSFloaterPoser::getBvhJointNegation(const std::string& jointName) const
return result;
}
bool FSFloaterPoser::getWhetherToResetBaseRotationOnEdit() { return gSavedSettings.getBOOL(POSER_RESETBASEROTONEDIT_SAVE_KEY); }
void FSFloaterPoser::onClickSetBaseRotZero() { mAlsoSaveBvhCbx->setEnabled(getWhetherToResetBaseRotationOnEdit()); }
bool FSFloaterPoser::getSavingToBvh()
{
return getWhetherToResetBaseRotationOnEdit() && gSavedSettings.getBOOL(POSER_RESETBASEROTONEDIT_SAVE_KEY);
return gSavedSettings.getBOOL(POSER_SAVEEXTERNALFORMAT_SAVE_KEY);
}
void FSFloaterPoser::onClickSavingToBvh()
{
mUnlockPelvisInBvhSaveCbx->setVisible(getSavingToBvh());
refreshTextHighlightingOnJointScrollLists();
}
void FSFloaterPoser::onClickLockWorldRotBtn()
{
auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1)
return;
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
for (auto item : selectedJoints)
{
bool currentlyPosingJoint = mPoserAnimator.isPosingAvatarJoint(avatar, *item);
if (!currentlyPosingJoint)
continue;
bool newLockState = !mPoserAnimator.getRotationIsWorldLocked(avatar, *item);
mPoserAnimator.setRotationIsWorldLocked(avatar, *item, newLockState);
}
refreshTextHighlightingOnJointScrollLists();
}

View File

@ -264,10 +264,11 @@ public:
void onClickLoadLeftHandPose();
void onClickLoadRightHandPose();
void onClickLoadHandPose(bool isRightHand);
void onClickSetBaseRotZero();
void onClickSavingToBvh();
void onCommitSpinner(const LLUICtrl* spinner, const S32 ID);
void onCommitSlider(const LLUICtrl* slider, const S32 id);
void onClickSymmetrize(const S32 ID);
void onClickLockWorldRotBtn();
// UI Refreshments
void refreshRotationSlidersAndSpinners();
@ -351,12 +352,19 @@ public:
void addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* avatar);
/// <summary>
/// Gets whether the user wishes to reset the base-rotation to zero when they start editing a joint.
/// Gets a string for a joint on a scroll-list.
/// </summary>
/// <remarks>
/// If a joint has a base-rotation of zero, the rotation then appears to be the user's work and qualifies to save to a re-importable format.
/// </remarks>
bool getWhetherToResetBaseRotationOnEdit();
/// <param name="avatar">The avatar owning the supplied joint.</param>
/// <param name="joint">The joint to query.</param>
/// <returns>A string naming an icon to present with the joint.</returns>
std::string getScrollListIconForJoint(LLVOAvatar* avatar, FSPoserAnimator::FSPoserJoint joint);
/// <summary>
/// Tries to get the named string from the XUI.
/// </summary>
/// <param name="name">The name of the string.</param>
/// <returns>The named string, if it exists, otherwise an empty string.</returns>
std::string tryGetString(std::string name);
/// <summary>
/// Gets the name of an item from the supplied object ID.
@ -483,6 +491,7 @@ public:
LLButton* mFlipJointBtn{ nullptr };
LLButton* mRecaptureBtn{ nullptr };
LLButton* mTogglePosingBonesBtn{ nullptr };
LLButton* mToggleLockWorldRotBtn{ nullptr };
LLButton* mToggleMirrorRotationBtn{ nullptr };
LLButton* mToggleSympatheticRotationBtn{ nullptr };
LLButton* mToggleDeltaModeBtn{ nullptr };
@ -503,8 +512,8 @@ public:
LLPanel* mCollisionVolumesPnl{ nullptr };
LLPanel* mPosesLoadSavePnl{ nullptr };
LLCheckBoxCtrl* mResetBaseRotCbx{ nullptr };
LLCheckBoxCtrl* mAlsoSaveBvhCbx{ nullptr };
LLCheckBoxCtrl* mUnlockPelvisInBvhSaveCbx{ nullptr };
LLUICtrl* mTrackpadSensitivitySpnr{ nullptr };
LLUICtrl* mYawSpnr{ nullptr };

View File

@ -57,9 +57,13 @@ void FSJointPose::setPublicPosition(const LLVector3& pos)
mCurrentState.mPosition.set(pos);
}
void FSJointPose::setPublicRotation(const LLQuaternion& rot)
void FSJointPose::setPublicRotation(bool zeroBase, const LLQuaternion& rot)
{
addStateToUndo(FSJointState(mCurrentState));
if (zeroBase)
zeroBaseRotation(true);
mCurrentState.mRotation.set(rot);
}
@ -79,6 +83,12 @@ void FSJointPose::redoLastChange()
mCurrentState = redoLastStateChange(FSJointState(mCurrentState));
}
void FSJointPose::resetJoint()
{
addStateToUndo(FSJointState(mCurrentState));
mCurrentState.resetJoint();
}
void FSJointPose::addStateToUndo(FSJointState stateToAddToUndo)
{
auto timeIntervalSinceLastChange = std::chrono::system_clock::now() - mTimeLastUpdatedCurrentState;
@ -125,7 +135,7 @@ FSJointPose::FSJointState FSJointPose::redoLastStateChange(FSJointState thingToS
mUndoneJointStatesIndex -= 1;
mUndoneJointStatesIndex = llclamp(mUndoneJointStatesIndex, 0, mLastSetJointStates.size() - 1);
auto result = mLastSetJointStates.at(mUndoneJointStatesIndex);
FSJointState result = mLastSetJointStates.at(mUndoneJointStatesIndex);
if (mUndoneJointStatesIndex == 0)
mLastSetJointStates.pop_front();
@ -145,14 +155,14 @@ void FSJointPose::recaptureJoint()
mCurrentState = FSJointState(joint);
}
void FSJointPose::recaptureJointAsDelta()
LLQuaternion FSJointPose::recaptureJointAsDelta(bool zeroBase)
{
LLJoint* joint = mJointState->getJoint();
if (!joint)
return;
return LLQuaternion::DEFAULT;
addStateToUndo(FSJointState(mCurrentState));
mCurrentState.updateFromJoint(joint);
return mCurrentState.updateFromJoint(joint, zeroBase);
}
void FSJointPose::swapRotationWith(FSJointPose* oppositeJoint)
@ -198,15 +208,13 @@ void FSJointPose::reflectRotation()
mCurrentState.reflectRotation();
}
void FSJointPose::zeroBaseRotation()
void FSJointPose::zeroBaseRotation(bool lockInBvh)
{
if (mIsCollisionVolume)
return;
if (!isBaseRotationZero())
purgeUndoQueue();
mCurrentState.zeroBaseRotation();
mCurrentState.mUserSpecifiedBaseZero = lockInBvh;
}
bool FSJointPose::isBaseRotationZero() const
@ -219,10 +227,37 @@ bool FSJointPose::isBaseRotationZero() const
void FSJointPose::purgeUndoQueue()
{
if (mIsCollisionVolume)
return;
mUndoneJointStatesIndex = 0;
mLastSetJointStates.clear();
}
bool FSJointPose::userHasSetBaseRotationToZero() const
{
if (mIsCollisionVolume)
return false;
return mCurrentState.mUserSpecifiedBaseZero;
}
bool FSJointPose::getWorldRotationLockState() const
{
if (mIsCollisionVolume)
return false;
return mCurrentState.mRotationIsWorldLocked;
}
void FSJointPose::setWorldRotationLockState(bool newState)
{
if (mIsCollisionVolume)
return;
mCurrentState.mRotationIsWorldLocked = newState;
}
bool FSJointPose::canPerformUndo() const
{
switch (mLastSetJointStates.size())

View File

@ -77,6 +77,11 @@ class FSJointPose
/// </summary>
void redoLastChange();
/// <summary>
/// Resets the joint to its conditions when posing started.
/// </summary>
void resetJoint();
/// <summary>
/// Gets the 'public' rotation of the joint.
/// </summary>
@ -85,12 +90,14 @@ class FSJointPose
/// <summary>
/// Sets the 'public' rotation of the joint.
/// </summary>
/// <param name="zeroBase">Whether to zero the base rotation on setting the supplied rotation.</param>
/// <param name="rot">The change in rotation to apply.</param>
/// <remarks>
/// 'Public rotation' is the amount of rotation the user has added to the initial state.
/// Public rotation is what a user may save to an external format (such as BVH).
/// This distinguishes 'private' rotation, which is the state inherited from something like a pose in-world.
/// </remarks>
void setPublicRotation(const LLQuaternion& rot);
void setPublicRotation(bool zeroBase, const LLQuaternion& rot);
/// <summary>
/// Reflects the base and delta rotation of the represented joint left-right.
@ -100,7 +107,8 @@ class FSJointPose
/// <summary>
/// Sets the private rotation of the represented joint to zero.
/// </summary>
void zeroBaseRotation();
/// <param name="lockInBvh">Whether the joint should be locked if exported to BVH.</param>
void zeroBaseRotation(bool lockInBvh);
/// <summary>
/// Queries whether the represented joint is zero.
@ -153,13 +161,33 @@ class FSJointPose
/// <summary>
/// Recalculates the delta reltive to the base for a new rotation.
/// </summary>
void recaptureJointAsDelta();
/// <param name="zeroBase">Whether to zero the base rotation on setting the supplied rotation.</param>
/// <returns>The rotation of the public difference between before and after recapture.</returns>
LLQuaternion recaptureJointAsDelta(bool zeroBase);
/// <summary>
/// Clears the undo/redo deque.
/// </summary>
void purgeUndoQueue();
/// <summary>
/// Gets whether the user has specified the base rotation of a joint to be zero.
/// </summary>
/// <returns>True if the user performed some action to specify zero rotation as the base, otherwise false.</returns>
bool userHasSetBaseRotationToZero() const;
/// <summary>
/// Gets whether the rotation of a joint has been 'locked' so that its world rotation can remain constant while parent joints change.
/// </summary>
/// <returns>True if the joint is rotationally locked to the world, otherwise false.</returns>
bool getWorldRotationLockState() const;
/// <summary>
/// Sets whether the world-rotation of a joint has been 'locked' so that as its parent joints change rotation or position, this joint keeps a constant world rotation.
/// </summary>
/// <param name="newState">The new state for the world-rotation lock.</param>
void setWorldRotationLockState(bool newState);
/// <summary>
/// Reverts the position/rotation/scale to their values when the animation begun.
/// This treatment is required for certain joints, particularly Collision Volumes and those bones not commonly animated by an AO.
@ -180,22 +208,16 @@ class FSJointPose
public:
FSJointState(LLJoint* joint)
{
mStartingRotation.set(joint->getRotation());
mBaseRotation.set(joint->getRotation());
mBasePosition.set(joint->getPosition());
mBaseScale.set(joint->getScale());
}
FSJointState() = default;
LLQuaternion mDeltaRotation;
LLQuaternion getTargetRotation() const { return mRotation * mBaseRotation; }
LLVector3 getTargetPosition() const { return mPosition + mBasePosition; }
LLVector3 getTargetScale() const { return mScale + mBaseScale; }
void updateRotation(const LLQuaternion& newRotation)
{
auto inv_base = mBaseRotation;
inv_base.conjugate();
mDeltaRotation = newRotation * inv_base;
};
void reflectRotation()
{
@ -209,10 +231,21 @@ class FSJointPose
{
mBaseRotation.set(otherState.mBaseRotation);
mRotation.set(otherState.mRotation);
mUserSpecifiedBaseZero = otherState.mUserSpecifiedBaseZero;
}
bool baseRotationIsZero() const { return mBaseRotation == LLQuaternion::DEFAULT; }
void resetJoint()
{
mUserSpecifiedBaseZero = false;
mRotationIsWorldLocked = false;
mBaseRotation.set(mStartingRotation);
mRotation.set(LLQuaternion::DEFAULT);
mPosition.setZero();
mScale.setZero();
}
void zeroBaseRotation() { mBaseRotation = LLQuaternion::DEFAULT; }
void revertJointToBase(LLJoint* joint) const
@ -225,21 +258,33 @@ class FSJointPose
joint->setScale(mBaseScale);
}
void updateFromJoint(LLJoint* joint)
LLQuaternion updateFromJoint(LLJoint* joint, bool zeroBase)
{
if (!joint)
return;
return LLQuaternion::DEFAULT;
LLQuaternion initalPublicRot = mRotation;
LLQuaternion invRot = mBaseRotation;
invRot.conjugate();
mRotation = joint->getRotation() * invRot;
LLQuaternion newPublicRot = joint->getRotation() * invRot;
if (zeroBase)
{
mUserSpecifiedBaseZero = zeroBase;
zeroBaseRotation();
}
mRotation.set(newPublicRot);
mPosition.set(joint->getPosition() - mBasePosition);
mScale.set(joint->getScale() - mBaseScale);
return newPublicRot *= ~initalPublicRot;
}
private:
FSJointState(FSJointState* state)
{
mStartingRotation.set(state->mStartingRotation);
mBaseRotation.set(state->mBaseRotation);
mBasePosition.set(state->mBasePosition);
mBaseScale.set(state->mBaseScale);
@ -247,14 +292,29 @@ class FSJointPose
mRotation.set(state->mRotation);
mPosition.set(state->mPosition);
mScale.set(state->mScale);
mUserSpecifiedBaseZero = state->mUserSpecifiedBaseZero;
mRotationIsWorldLocked = state->mRotationIsWorldLocked;
}
public:
LLQuaternion mRotation;
LLVector3 mPosition;
LLVector3 mScale;
bool mRotationIsWorldLocked = false;
/// <summary>
/// A value indicating whether the user has explicitly set the base rotation to zero.
/// </summary>
/// <remarks>
/// The base-rotation, representing any 'current animation' state when posing starts, may become zero for several reasons.
/// Loading a Pose, editing a rotation intended to save to BVH, or setting to 'T-Pose' being examples.
/// If a user intends on creating a BVH, zero-rotation has a special meaning upon upload: the joint is free (is not animated by that BVH).
/// This value represents the explicit intent to have that joint be 'free' in BVH (which is sometimes undesireable).
/// </remarks>
bool mUserSpecifiedBaseZero = false;
private:
LLQuaternion mStartingRotation;
LLQuaternion mBaseRotation;
LLVector3 mBasePosition;
LLVector3 mBaseScale;

View File

@ -2744,7 +2744,7 @@ void FSPanelFace::refreshMedia()
} func;
// check if all faces have media (or, all don't have media)
LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo = selected_objects->getSelectedTEValue(&func, bool_has_media);
bool identical_has_media_info = selected_objects->getSelectedTEValue(&func, bool_has_media);
const LLMediaEntry default_media_data;
@ -2766,7 +2766,8 @@ void FSPanelFace::refreshMedia()
} func_media_data(default_media_data);
LLMediaEntry media_data_get;
LLFloaterMediaSettings::getInstance()->mMultipleMedia = !(selected_objects->getSelectedTEValue(&func_media_data, media_data_get));
bool multiple_media = !(selected_objects->getSelectedTEValue(&func_media_data, media_data_get));
bool multiple_valid_media = false;
std::string multi_media_info_str = LLTrans::getString("Multiple Media");
std::string media_title = "";
@ -2777,13 +2778,13 @@ void FSPanelFace::refreshMedia()
mBtnAddMedia->setEnabled(editable);
// IF all the faces have media (or all don't have media)
if (LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo)
if (identical_has_media_info)
{
// TODO: get media title and set it.
mTitleMediaText->clear();
// if identical is set, all faces are same (whether all empty or has the same media)
if (!(LLFloaterMediaSettings::getInstance()->mMultipleMedia))
if (!multiple_media)
{
// media data is valid
if (media_data_get != default_media_data)
@ -2805,9 +2806,9 @@ void FSPanelFace::refreshMedia()
else // not all faces have media but at least one does
{
// selected faces have not identical value
LLFloaterMediaSettings::getInstance()->mMultipleValidMedia = selected_objects->isMultipleTEValue(&func_media_data, default_media_data);
multiple_valid_media = selected_objects->isMultipleTEValue(&func_media_data, default_media_data);
if (LLFloaterMediaSettings::getInstance()->mMultipleValidMedia)
if (multiple_valid_media)
{
media_title = multi_media_info_str;
}
@ -2844,7 +2845,7 @@ void FSPanelFace::refreshMedia()
// load values for media settings
updateMediaSettings();
LLFloaterMediaSettings::initValues(mMediaSettings, editable);
LLFloaterMediaSettings::initValues(mMediaSettings, editable, identical_has_media_info, multiple_media, multiple_valid_media);
}
void FSPanelFace::unloadMedia()

View File

@ -95,6 +95,7 @@ void FSPoserAnimator::undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
return;
jointPose->undoLastChange();
undoOrRedoWorldLockedDescendants(joint, posingMotion, false);
if (style == NONE || style == DELTAMODE)
return;
@ -104,6 +105,10 @@ void FSPoserAnimator::undoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
return;
oppositeJointPose->undoLastChange();
auto oppositePoserJoint = getPoserJointByName(joint.mirrorJointName());
if (oppositePoserJoint)
undoOrRedoWorldLockedDescendants(*oppositePoserJoint, posingMotion, false);
}
void FSPoserAnimator::resetJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneDeflectionStyles style)
@ -122,9 +127,7 @@ void FSPoserAnimator::resetJoint(LLVOAvatar* avatar, const FSPoserJoint& joint,
if (!jointPose)
return;
jointPose->setPublicRotation(LLQuaternion());
jointPose->setPublicPosition(LLVector3());
jointPose->setPublicScale(LLVector3());
jointPose->resetJoint();
if (style == NONE || style == DELTAMODE)
return;
@ -133,9 +136,7 @@ void FSPoserAnimator::resetJoint(LLVOAvatar* avatar, const FSPoserJoint& joint,
if (!oppositeJointPose)
return;
oppositeJointPose->setPublicRotation(LLQuaternion());
oppositeJointPose->setPublicPosition(LLVector3());
oppositeJointPose->setPublicScale(LLVector3());
oppositeJointPose->resetJoint();
}
bool FSPoserAnimator::canRedoOrUndoJointChange(LLVOAvatar* avatar, const FSPoserJoint& joint, bool canUndo)
@ -177,6 +178,7 @@ void FSPoserAnimator::redoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
return;
jointPose->redoLastChange();
undoOrRedoWorldLockedDescendants(joint, posingMotion, true);
if (style == NONE || style == DELTAMODE)
return;
@ -186,6 +188,10 @@ void FSPoserAnimator::redoLastJointChange(LLVOAvatar* avatar, const FSPoserJoint
return;
oppositeJointPose->redoLastChange();
auto oppositePoserJoint = getPoserJointByName(joint.mirrorJointName());
if (oppositePoserJoint)
undoOrRedoWorldLockedDescendants(*oppositePoserJoint, posingMotion, true);
}
LLVector3 FSPoserAnimator::getJointPosition(LLVOAvatar* avatar, const FSPoserJoint& joint) const
@ -265,7 +271,7 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j
}
}
bool FSPoserAnimator::baseRotationIsZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const
bool FSPoserAnimator::getRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint) const
{
if (!isAvatarSafeToUse(avatar))
return false;
@ -278,7 +284,67 @@ bool FSPoserAnimator::baseRotationIsZero(LLVOAvatar* avatar, const FSPoserJoint&
if (!jointPose)
return false;
return jointPose->isBaseRotationZero();
return jointPose->getWorldRotationLockState();
}
void FSPoserAnimator::setRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint, bool newState)
{
if (!isAvatarSafeToUse(avatar))
return;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
jointPose->setWorldRotationLockState(newState);
}
bool FSPoserAnimator::exportRotationWillLockJoint(LLVOAvatar* avatar, const FSPoserJoint& joint) const
{
const F32 ROTATION_KEYFRAME_THRESHOLD = 0.01f; // this is a guestimate: see BVH loader
if (!isAvatarSafeToUse(avatar))
return false;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return false;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return false;
if (!jointPose->userHasSetBaseRotationToZero())
return false;
F32 rot_threshold = ROTATION_KEYFRAME_THRESHOLD / llmax((F32)getChildJointDepth(&joint, 0) * 0.33f, 1.f);
LLQuaternion rotToExport = jointPose->getPublicRotation();
F32 x_delta = dist_vec(LLVector3::x_axis * LLQuaternion::DEFAULT, LLVector3::x_axis * rotToExport); // when exporting multiple frames this will need to compare frames.
F32 y_delta = dist_vec(LLVector3::y_axis * LLQuaternion::DEFAULT, LLVector3::y_axis * rotToExport);
F32 rot_test = x_delta + y_delta;
return rot_test > rot_threshold;
}
bool FSPoserAnimator::userSetBaseRotationToZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const
{
if (!isAvatarSafeToUse(avatar))
return false;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return false;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return false;
return jointPose->userHasSetBaseRotationToZero();
}
bool FSPoserAnimator::allBaseRotationsAreZero(LLVOAvatar* avatar) const
@ -307,6 +373,21 @@ void FSPoserAnimator::setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar)
return;
posingMotion->setAllRotationsToZeroAndClearUndo();
for (size_t index = 0; index != PoserJoints.size(); ++index)
{
auto boneType = PoserJoints[index].boneType();
bool setBvhToLock = boneType == BODY || boneType == WHOLEAVATAR;
if (setBvhToLock)
continue; // setAllRotationsToZeroAndClearUndo specified this is the default behaviour
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(PoserJoints[index].jointName());
if (!jointPose)
continue;
posingMotion->setJointBvhLock(jointPose, false);
}
}
void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation)
@ -326,7 +407,8 @@ void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joi
setPosingAvatarJoint(avatar, joint, true);
}
void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, E_BoneDeflectionStyles style)
void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero,
E_BoneDeflectionStyles style)
{
if (!isAvatarSafeToUse(avatar))
return;
@ -339,7 +421,9 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi
if (!jointPose)
return;
jointPose->recaptureJointAsDelta();
LLQuaternion deltaRot = jointPose->recaptureJointAsDelta(resetBaseRotationToZero);
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
if (style == NONE || style == DELTAMODE)
return;
@ -365,6 +449,35 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi
}
}
LLVector3 FSPoserAnimator::getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, bool lockWholeAvatar) const
{
auto rotation = getJointRotation(avatar, joint, SWAP_NOTHING, NEGATE_NOTHING);
if (exportRotationWillLockJoint(avatar, joint))
return rotation;
LLVector3 vec3;
if (!isAvatarSafeToUse(avatar))
return vec3;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return vec3;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return vec3;
if (!jointPose->userHasSetBaseRotationToZero())
return vec3;
if (lockWholeAvatar && joint.boneType() == WHOLEAVATAR)
return LLVector3(DEG_TO_RAD * 0.295f, 0.f, 0.f);
F32 minimumRotation = DEG_TO_RAD * 0.65f / llmax((F32)getChildJointDepth(&joint, 0) * 0.33f, 1.f);
return LLVector3(minimumRotation, 0.f, 0.f);
}
LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) const
{
LLVector3 vec3;
@ -399,9 +512,6 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
if (!jointPose)
return;
if (resetBaseRotationToZero)
jointPose->zeroBaseRotation();
LLQuaternion absRot = translateRotationToQuaternion(translation, negation, absRotation);
LLQuaternion deltaRot = translateRotationToQuaternion(translation, negation, deltaRotation);
switch (deflectionStyle)
@ -409,53 +519,65 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
case SYMPATHETIC:
case MIRROR:
if (rotationStyle == DELTAIC_ROT)
jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation());
jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
else
jointPose->setPublicRotation(absRot);
jointPose->setPublicRotation(resetBaseRotationToZero, absRot);
break;
case SYMPATHETIC_DELTA:
case MIRROR_DELTA:
jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation());
jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
break;
case DELTAMODE:
jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation());
jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
return;
case NONE:
default:
if (rotationStyle == DELTAIC_ROT)
jointPose->setPublicRotation(deltaRot * jointPose->getPublicRotation());
jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
else
jointPose->setPublicRotation(absRot);
jointPose->setPublicRotation(resetBaseRotationToZero, absRot);
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
return;
}
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot);
auto oppositePoserJoint = getPoserJointByName(joint->mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
if (!oppositeJointPose)
return;
LLQuaternion inv_quat;
LLQuaternion inv_quat = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]);
switch (deflectionStyle)
{
case SYMPATHETIC:
oppositeJointPose->cloneRotationFrom(jointPose);
if (oppositePoserJoint)
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, deltaRot);
break;
case SYMPATHETIC_DELTA:
oppositeJointPose->setPublicRotation(deltaRot * oppositeJointPose->getPublicRotation());
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * oppositeJointPose->getPublicRotation());
if (oppositePoserJoint)
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, deltaRot);
break;
case MIRROR:
oppositeJointPose->mirrorRotationFrom(jointPose);
if (oppositePoserJoint)
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, inv_quat);
break;
case MIRROR_DELTA:
inv_quat = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]);
oppositeJointPose->setPublicRotation(inv_quat * oppositeJointPose->getPublicRotation());
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, inv_quat * oppositeJointPose->getPublicRotation());
if (oppositePoserJoint)
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, inv_quat);
break;
default:
@ -757,11 +879,10 @@ void FSPoserAnimator::loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint*
if (!jointPose)
return;
if (setBaseToZero)
jointPose->zeroBaseRotation();
jointPose->purgeUndoQueue();
LLQuaternion rot = translateRotationToQuaternion(SWAP_NOTHING, NEGATE_NOTHING, rotation);
jointPose->setPublicRotation(rot);
jointPose->setPublicRotation(setBaseToZero, rot);
}
void FSPoserAnimator::loadJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, bool loadPositionAsDelta, LLVector3 position)
@ -802,7 +923,7 @@ void FSPoserAnimator::loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joi
jointPose->setPublicScale(scale);
}
const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName)
const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName) const
{
for (size_t index = 0; index != PoserJoints.size(); ++index)
{
@ -899,3 +1020,118 @@ bool FSPoserAnimator::isAvatarSafeToUse(LLVOAvatar* avatar) const
return true;
}
int FSPoserAnimator::getChildJointDepth(const FSPoserJoint* joint, int depth) const
{
size_t numberOfBvhChildNodes = joint->bvhChildren().size();
if (numberOfBvhChildNodes < 1)
return depth;
depth++;
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{
auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]);
if (!nextJoint)
continue;
depth = llmax(depth, getChildJointDepth(nextJoint, depth));
}
return depth;
}
void FSPoserAnimator::deRotateWorldLockedDescendants(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion rotationChange)
{
size_t numberOfBvhChildNodes = joint->bvhChildren().size();
if (numberOfBvhChildNodes < 1)
return;
FSJointPose* parentJoint = posingMotion->getJointPoseByJointName(joint->jointName());
if (!parentJoint)
return;
LLJoint* pJoint = parentJoint->getJointState()->getJoint();
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{
auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]);
if (!nextJoint)
continue;
deRotateJointOrFirstLockedChild(nextJoint, posingMotion, pJoint->getWorldRotation(), rotationChange);
}
}
void FSPoserAnimator::deRotateJointOrFirstLockedChild(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion rotatedParentWorldRot, LLQuaternion rotationChange)
{
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
if (!jointPose)
return;
if (jointPose->getWorldRotationLockState())
{
LLQuaternion worldRotOfThisJoint = jointPose->getJointState()->getJoint()->getWorldRotation();
LLQuaternion differenceInWorldRot = worldRotOfThisJoint * ~rotatedParentWorldRot;
LLQuaternion rotDiffInChildFrame = differenceInWorldRot * rotationChange * ~differenceInWorldRot;
rotDiffInChildFrame.conjugate();
jointPose->setPublicRotation(false, rotDiffInChildFrame * jointPose->getPublicRotation());
return;
}
size_t numberOfBvhChildNodes = joint->bvhChildren().size();
if (numberOfBvhChildNodes < 1)
return;
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{
auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]);
if (!nextJoint)
continue;
deRotateJointOrFirstLockedChild(nextJoint, posingMotion, rotatedParentWorldRot, rotationChange);
}
}
void FSPoserAnimator::undoOrRedoWorldLockedDescendants(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo)
{
size_t numberOfBvhChildNodes = joint.bvhChildren().size();
if (numberOfBvhChildNodes < 1)
return;
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{
auto nextJoint = getPoserJointByName(joint.bvhChildren()[index]);
if (!nextJoint)
continue;
undoOrRedoJointOrFirstLockedChild(*nextJoint, posingMotion, redo);
}
}
void FSPoserAnimator::undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo)
{
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return;
if (jointPose->getWorldRotationLockState())
{
redo ? jointPose->redoLastChange() : jointPose->undoLastChange();
return;
}
size_t numberOfBvhChildNodes = joint.bvhChildren().size();
if (numberOfBvhChildNodes < 1)
return;
for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{
auto nextJoint = getPoserJointByName(joint.bvhChildren()[index]);
if (!nextJoint)
continue;
undoOrRedoJointOrFirstLockedChild(*nextJoint, posingMotion, redo);
}
}

View File

@ -392,7 +392,7 @@ public:
/// </summary>
/// <param name="jointName">The name of the joint to match.</param>
/// <returns>The matching joint if found, otherwise nullptr</returns>
const FSPoserJoint* getPoserJointByName(const std::string& jointName);
const FSPoserJoint* getPoserJointByName(const std::string& jointName) const;
/// <summary>
/// Tries to start posing the supplied avatar.
@ -492,6 +492,19 @@ public:
/// <returns>The rotation of the requested joint, if determinable, otherwise a default vector.</returns>
LLVector3 getJointRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) const;
/// <summary>
/// Gets the rotation of a joint for the supplied avatar for export.
/// </summary>
/// <param name="avatar">The avatar whose joint is being queried.</param>
/// <param name="joint">The joint to determine the rotation for.</param>
/// <param name="lockWholeAvatar">Whether the whole avatar should be rotation/position locked in the BVH export.</param>
/// <returns>The rotation of the requested joint for export.</returns>
/// <remarks>
/// The BVH export format requires some minimal amount of rotation so it animates the joint on upload.
/// The WHOLEAVATAR joint (mPelvis) never exports as 'free'.
/// </remarks>
LLVector3 getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, bool lockWholeAvatar) const;
/// <summary>
/// Sets the rotation of a joint for the supplied avatar.
/// </summary>
@ -559,8 +572,9 @@ public:
/// </summary>
/// <param name="avatar">The avatar whose joint is to be recaptured.</param>
/// <param name="joint">The joint to recapture.</param>
/// <param name="resetBaseRotationToZero">Whether to set the base rotation to zero on setting the rotation.</param>
/// <param name="style">Any ancilliary action to be taken with the change to be made.</param>
void recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, E_BoneDeflectionStyles style);
void recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero, E_BoneDeflectionStyles style);
/// <summary>
/// Sets all of the joint rotations of the supplied avatar to zero.
@ -574,7 +588,34 @@ public:
/// <param name="avatar">The avatar owning the supplied joint.</param>
/// <param name="joint">The joint to query.</param>
/// <returns>True if the supplied joint has a 'base' rotation of zero (thus user-supplied change only), otherwise false.</returns>
bool baseRotationIsZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
bool userSetBaseRotationToZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
/// <summary>
/// Gets whether the supplied joints position will be set in an export.
/// </summary>
/// <param name="avatar">The avatar owning the supplied joint.</param>
/// <param name="joint">The joint to query.</param>
/// <returns>True if the export will 'lock' the joint, otherwise false.</returns>
/// <remarks>
/// BVH import leaves a joint 'free' if its rotation is less than something arbitrary.
/// </remarks>
bool exportRotationWillLockJoint(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
/// <summary>
/// Gets whether the supplied joint for the supplied avatar is rotationally locked to the world.
/// </summary>
/// <param name="avatar">The avatar owning the supplied joint.</param>
/// <param name="joint">The joint to query.</param>
/// <returns>True if the joint is maintaining a fixed-rotation in world, otherwise false.</returns>
bool getRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint) const;
/// <summary>
/// Sets the world-rotation-lock status for supplied joint for the supplied avatar.
/// </summary>
/// <param name="avatar">The avatar owning the supplied joint.</param>
/// <param name="joint">The joint to query.</param>
/// <param name="newState">The lock state to apply.</param>
void setRotationIsWorldLocked(LLVOAvatar* avatar, const FSPoserJoint& joint, bool newState);
/// <summary>
/// Determines if the kind of save to perform should be a 'delta' save, or a complete save.
@ -692,6 +733,53 @@ public:
/// <returns>True if the avatar is safe to manipulate, otherwise false.</returns>
bool isAvatarSafeToUse(LLVOAvatar* avatar) const;
/// <summary>
/// Gets the depth of descendant joints for the supplied joint.
/// </summary>
/// <param name="joint">The joint to determine the depth for.</param>
/// <param name="depth">The depth of the supplied joint.</param>
/// <returns>The number of generations of descendents the joint has, if none, then zero.</returns>
int getChildJointDepth(const FSPoserJoint* joint, int depth) const;
/// <summary>
/// Derotates the first world-locked child joint to the supplied joint.
/// </summary>
/// <param name="joint">The edited joint, whose children may be world-locked.</param>
/// <param name="posingMotion">The posing motion.</param>
/// <param name="rotation">The rotation the supplied joint was/is being changed by.</param>
/// <remarks>
/// There are two ways to resolve this problem: before the rotation is applied in the PosingMotion (the animation) or after.
/// If performed after, a feedback loop is created, because you're noting the world-rotation in one frame, then correcting it back to that in another.
/// This implementation works by applying an opposing-rotation to the locked child joint which is corrected for the relative world-rotations of parent and child.
/// </remarks>
void deRotateWorldLockedDescendants(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion rotation);
/// <summary>
/// Recursively tests the supplied joint and all its children for their world-locked status, and applies a de-rotation if it is world-locked.
/// </summary>
/// <param name="joint">The edited joint, whose children may be world-locked.</param>
/// <param name="posingMotion">The posing motion.</param>
/// <param name="parentWorldRot">The world-rotation of the joint that was edited.</param>
/// <param name="rotation">The rotation the joint was edit is being changed by.</param>
void deRotateJointOrFirstLockedChild(const FSPoserJoint* joint, FSPosingMotion* posingMotion, LLQuaternion parentWorldRot,
LLQuaternion rotation);
/// <summary>
/// Performs an undo or redo of an edit to the supplied joints world-locked descendants.
/// </summary>
/// <param name="joint">The edited joint, whose children may be world-locked.</param>
/// <param name="posingMotion">The posing motion.</param>
/// <param name="redo">Whether to redo the edit, otherwise the edit is undone.</param>
void undoOrRedoWorldLockedDescendants(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo);
/// <summary>
/// Recursively tests the supplied joint and all its children for their world-locked status, and applies an undo or redo if it is world-locked.
/// </summary>
/// <param name="joint">The joint which will have the undo or redo performed, if it is world locked.</param>
/// <param name="posingMotion">The posing motion.</param>
/// <param name="redo">Whether to redo the edit, otherwise the edit is undone.</param>
void undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo);
/// <summary>
/// Maps the avatar's ID to the animation registered to them.
/// Thus we start/stop the same animation, and get/set the same rotations etc.

View File

@ -259,11 +259,16 @@ void FSPosingMotion::setAllRotationsToZeroAndClearUndo()
{
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
{
poserJoint_iter->zeroBaseRotation();
poserJoint_iter->setPublicRotation(LLQuaternion::DEFAULT);
poserJoint_iter->purgeUndoQueue();
poserJoint_iter->setPublicRotation(true, LLQuaternion::DEFAULT);
}
}
void FSPosingMotion::setJointBvhLock(FSJointPose* joint, bool lockInBvh)
{
joint->zeroBaseRotation(lockInBvh);
}
bool FSPosingMotion::vectorsNotQuiteEqual(LLVector3 v1, LLVector3 v2) const
{
if (vectorAxesAlmostEqual(v1.mV[VX], v2.mV[VX]) &&

View File

@ -121,8 +121,17 @@ public:
/// <summary>
/// Sets all of the non-Collision Volume base-and-delta rotations to zero, and clears the undo/redo queue.
/// </summary>
/// <remarks>
/// By default, sets the joint to lock in BVH export.
/// </remarks>
void setAllRotationsToZeroAndClearUndo();
/// <summary>
/// Sets the BVH export state for the supplied joint.
/// </summary>
/// <param name="lockInBvh">Whether the joint should be locked if exported to BVH.</param>
void setJointBvhLock(FSJointPose* joint, bool lockInBvh);
private:
/// <summary>
/// The axial difference considered close enough to be the same.

View File

@ -146,6 +146,12 @@ namespace LL
dst.set(src[0], src[1]);
}
template<>
inline void copyVec3<F32, LLVector2>(F32* src, LLVector2& dst)
{
dst.set(src[0], src[1]);
}
template<>
inline void copyVec3<F32, vec3>(F32* src, vec3& dst)
{
@ -375,12 +381,18 @@ namespace LL
template<class T>
inline void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst)
{
if (accessor.mBufferView == INVALID_INDEX)
if (accessor.mBufferView == INVALID_INDEX
|| accessor.mBufferView >= asset.mBufferViews.size())
{
LL_WARNS("GLTF") << "Invalid buffer" << LL_ENDL;
return;
}
const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView];
if (bufferView.mBuffer >= asset.mBuffers.size())
{
LL_WARNS("GLTF") << "Invalid buffer view" << LL_ENDL;
return;
}
const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset;

View File

@ -220,6 +220,7 @@ void GLTFSceneManager::uploadSelection()
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost,
LLUUID::null,
false,
finish,
failure));
@ -283,6 +284,7 @@ void GLTFSceneManager::uploadSelection()
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost,
LLUUID::null,
false,
finish,
failure));
@ -559,6 +561,7 @@ void GLTFSceneManager::update()
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost,
LLUUID::null,
false,
finish,
failure));

View File

@ -140,8 +140,8 @@ const F32 MIN_FIDGET_TIME = 8.f; // seconds
const F32 MAX_FIDGET_TIME = 20.f; // seconds
const S32 UI_FEATURE_VERSION = 1;
// For version 1: 1 - inventory, 2 - gltf
const S32 UI_FEATURE_FLAGS = 3;
// For version 1, flag holds: 1 - inventory thumbnails, 2 - gltf, 4 - inventory favorites
const S32 UI_FEATURE_FLAGS = 7;
// The agent instance.
LLAgent gAgent;
@ -242,7 +242,6 @@ private:
LLVector3d mPosGlobal;
};
class LLTeleportRequestViaLocationLookAt : public LLTeleportRequestViaLocation
{
public:
@ -690,7 +689,7 @@ void LLAgent::getFeatureVersionAndFlags(S32& version, S32& flags)
if (feature_version.isInteger())
{
version = feature_version.asInteger();
flags = 1; // inventory flag
flags = 3; // show 'favorites' notification
}
else if (feature_version.isMap())
{
@ -716,13 +715,8 @@ void LLAgent::showLatestFeatureNotification(const std::string key)
if (key == "inventory")
{
// Notify user about new thumbnail support
flag = 1;
}
if (key == "gltf")
{
flag = 2;
// Notify user about new favorites support
flag = 4;
}
if ((flags & flag) == 0)
@ -962,7 +956,6 @@ void LLAgent::movePitch(F32 mag)
}
}
// Does this parcel allow you to fly?
bool LLAgent::canFly()
{
@ -1061,7 +1054,6 @@ void LLAgent::setFlying(bool fly, bool fail_sound)
LLFloaterMove::setFlyingMode(fly);
}
// UI based mechanism of setting fly state
//-----------------------------------------------------------------------------
// toggleFlying()
@ -1309,7 +1301,6 @@ void LLAgent::setRegion(LLViewerRegion *regionp)
mRegionChangedSignal();
}
//-----------------------------------------------------------------------------
// getRegion()
//-----------------------------------------------------------------------------
@ -1318,7 +1309,6 @@ LLViewerRegion *LLAgent::getRegion() const
return mRegionp;
}
LLHost LLAgent::getRegionHost() const
{
if (mRegionp)
@ -1349,7 +1339,6 @@ bool LLAgent::inPrelude()
return mRegionp && mRegionp->isPrelude();
}
std::string LLAgent::getRegionCapability(const std::string &name)
{
if (!mRegionp)
@ -1358,7 +1347,6 @@ std::string LLAgent::getRegionCapability(const std::string &name)
return mRegionp->getCapability(name);
}
//-----------------------------------------------------------------------------
// canManageEstate()
//-----------------------------------------------------------------------------
@ -1386,7 +1374,6 @@ void LLAgent::sendMessage()
gMessageSystem->sendMessage(mRegionp->getHost());
}
//-----------------------------------------------------------------------------
// sendReliableMessage()
//-----------------------------------------------------------------------------
@ -1420,7 +1407,6 @@ LLVector3 LLAgent::getVelocity() const
}
}
//-----------------------------------------------------------------------------
// setPositionAgent()
//-----------------------------------------------------------------------------
@ -1494,7 +1480,6 @@ const LLVector3 &LLAgent::getPositionAgent()
}
}
return mFrameAgent.getOrigin();
}
@ -1503,7 +1488,6 @@ boost::signals2::connection LLAgent::whenPositionChanged(position_signal_t::slot
return mOnPositionChanged.connect(fn);
}
//-----------------------------------------------------------------------------
// getRegionsVisited()
//-----------------------------------------------------------------------------
@ -1520,7 +1504,6 @@ F64 LLAgent::getDistanceTraveled() const
return mDistanceTraveled;
}
//-----------------------------------------------------------------------------
// getPosAgentFromGlobal()
//-----------------------------------------------------------------------------
@ -1531,7 +1514,6 @@ LLVector3 LLAgent::getPosAgentFromGlobal(const LLVector3d &pos_global) const
return pos_agent;
}
//-----------------------------------------------------------------------------
// getPosGlobalFromAgent()
//-----------------------------------------------------------------------------
@ -1552,7 +1534,6 @@ void LLAgent::sitDown()
setControlFlags(AGENT_CONTROL_SIT_ON_GROUND);
}
//-----------------------------------------------------------------------------
// resetAxes()
//-----------------------------------------------------------------------------
@ -1561,7 +1542,6 @@ void LLAgent::resetAxes()
mFrameAgent.resetAxes();
}
// Copied from LLCamera::setOriginAndLookAt
// Look_at must be unit vector
//-----------------------------------------------------------------------------
@ -1590,7 +1570,6 @@ void LLAgent::resetAxes(const LLVector3 &look_at)
mFrameAgent.setAxes(look_at, left, up);
}
//-----------------------------------------------------------------------------
// rotate()
//-----------------------------------------------------------------------------
@ -1599,7 +1578,6 @@ void LLAgent::rotate(F32 angle, const LLVector3 &axis)
mFrameAgent.rotate(angle, axis);
}
//-----------------------------------------------------------------------------
// rotate()
//-----------------------------------------------------------------------------
@ -1608,7 +1586,6 @@ void LLAgent::rotate(F32 angle, F32 x, F32 y, F32 z)
mFrameAgent.rotate(angle, x, y, z);
}
//-----------------------------------------------------------------------------
// rotate()
//-----------------------------------------------------------------------------
@ -1617,7 +1594,6 @@ void LLAgent::rotate(const LLMatrix3 &matrix)
mFrameAgent.rotate(matrix);
}
//-----------------------------------------------------------------------------
// rotate()
//-----------------------------------------------------------------------------
@ -1626,7 +1602,6 @@ void LLAgent::rotate(const LLQuaternion &quaternion)
mFrameAgent.rotate(quaternion);
}
//-----------------------------------------------------------------------------
// getReferenceUpVector()
//-----------------------------------------------------------------------------
@ -1655,7 +1630,6 @@ LLVector3 LLAgent::getReferenceUpVector()
return up_vector;
}
// Radians, positive is forward into ground
//-----------------------------------------------------------------------------
// pitch()
@ -1699,7 +1673,6 @@ void LLAgent::pitch(F32 angle)
}
}
//-----------------------------------------------------------------------------
// roll()
//-----------------------------------------------------------------------------
@ -1708,7 +1681,6 @@ void LLAgent::roll(F32 angle)
mFrameAgent.roll(angle);
}
//-----------------------------------------------------------------------------
// yaw()
//-----------------------------------------------------------------------------
@ -1720,7 +1692,6 @@ void LLAgent::yaw(F32 angle)
}
}
// Returns a quat that represents the rotation of the agent in the absolute frame
//-----------------------------------------------------------------------------
// getQuat()
@ -1756,7 +1727,6 @@ void LLAgent::setControlFlags(U32 mask)
mControlFlags |= mask;
}
//-----------------------------------------------------------------------------
// clearControlFlags()
//-----------------------------------------------------------------------------
@ -2179,7 +2149,6 @@ void LLAgent::startAutoPilotGlobal(
mAutoPilotNoProgressFrameCount = 0;
}
//-----------------------------------------------------------------------------
// setAutoPilotTargetGlobal
//-----------------------------------------------------------------------------
@ -2233,7 +2202,6 @@ void LLAgent::startFollowPilot(const LLUUID &leader_id, bool allow_flying, F32 s
allow_flying);
}
//-----------------------------------------------------------------------------
// stopAutoPilot()
//-----------------------------------------------------------------------------
@ -2275,7 +2243,6 @@ void LLAgent::stopAutoPilot(bool user_cancel)
}
}
// Returns necessary agent pitch and yaw changes, radians.
//-----------------------------------------------------------------------------
// autoPilot()
@ -2464,7 +2431,6 @@ void LLAgent::autoPilot(F32 *delta_yaw)
}
}
//-----------------------------------------------------------------------------
// propagate()
//-----------------------------------------------------------------------------
@ -2485,18 +2451,19 @@ void LLAgent::propagate(const F32 dt)
}
// handle rotation based on keyboard levels
constexpr F32 YAW_RATE = 90.f * DEG_TO_RAD; // radians per second
F32 angle = YAW_RATE * gAgentCamera.getYawKey() * dt;
if (fabs(angle) > 0.0f)
if (fabs(dt) > 1e-6)
{
yaw(angle);
}
if (fabs(gAgentCamera.getYawKey()) > 1e-6)
{
static const F32 YAW_RATE = 90.f * DEG_TO_RAD; // radians per second
yaw(YAW_RATE * gAgentCamera.getYawKey() * dt);
}
constexpr F32 PITCH_RATE = 90.f * DEG_TO_RAD; // radians per second
angle = PITCH_RATE * gAgentCamera.getPitchKey() * dt;
if (fabs(angle) > 0.0f)
{
pitch(angle);
if (fabs(gAgentCamera.getPitchKey()) > 1e-6)
{
static const F32 PITCH_RATE = 90.f * DEG_TO_RAD; // radians per second
pitch(PITCH_RATE * gAgentCamera.getPitchKey() * dt);
}
}
// handle auto-land behavior
@ -2677,7 +2644,6 @@ void LLAgent::clearRenderState(U8 clearstate)
mRenderState &= ~clearstate;
}
//-----------------------------------------------------------------------------
// getRenderState()
//-----------------------------------------------------------------------------
@ -2719,6 +2685,7 @@ void LLAgent::endAnimationUpdateUI()
{
return;
}
if (gAgentCamera.getCameraMode() == gAgentCamera.getLastCameraMode())
{
// We're already done endAnimationUpdateUI for this transition.
@ -2835,7 +2802,6 @@ void LLAgent::endAnimationUpdateUI()
}
// </FS:PP>
gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR);
if( gMorphView )
{
@ -3557,7 +3523,6 @@ void LLAgent::sendMaturityPreferenceToServer(U8 pPreferredMaturity)
}
}
void LLAgent::processMaturityPreferenceFromServer(const LLSD &result, U8 perferredMaturity)
{
U8 maturity = SIM_ACCESS_MIN;
@ -3633,7 +3598,6 @@ void LLAgent::changeInterestListMode(const std::string &new_mode)
}
}
bool LLAgent::requestPostCapability(const std::string &capName, LLSD &postData, httpCallback_t cbSuccess, httpCallback_t cbFailure)
{
if (getRegion())
@ -3988,7 +3952,6 @@ void LLAgent::sendAnimationStateReset()
sendReliableMessage();
}
// Send a message to the region to revoke sepecified permissions on ALL scripts in the region
// If the target is an object in the region, permissions in scripts on that object are cleared.
// If it is the region ID, all scripts clear the permissions for this agent
@ -5104,7 +5067,6 @@ void LLAgent::onCapabilitiesReceivedAfterTeleport()
check_merchant_status();
}
//void LLAgent::teleportRequest(
// const U64& region_handle,
// const LLVector3& pos_local,
@ -5244,7 +5206,6 @@ void LLAgent::doTeleportViaLure(const LLUUID& lure_id, bool godlike)
}
}
// James Cook, July 28, 2005
void LLAgent::teleportCancel()
{
@ -5555,7 +5516,6 @@ LLAgent::ETeleportState LLAgent::getTeleportState() const
// teleportRequest(region_handle, pos_local, getTeleportKeepsLookAt());
//}
void LLAgent::setTeleportState(ETeleportState state)
{
if (mTeleportRequest && (state != TELEPORT_NONE) && (mTeleportRequest->getStatus() == LLTeleportRequest::kFailed))
@ -5751,7 +5711,6 @@ void LLAgent::stopFidget()
gAgent.sendAnimationRequests(anims, ANIM_REQUEST_STOP);
}
void LLAgent::requestEnterGodMode()
{
LLMessageSystem* msg = gMessageSystem;
@ -5887,7 +5846,6 @@ void LLAgent::sendAgentUpdateUserInfo(bool im_via_email, const std::string& dire
}
}
// <FS:Ansariel> Keep this for OpenSim
//void LLAgent::updateAgentUserInfoCoro(std::string capurl, std::string directory_visibility)
void LLAgent::updateAgentUserInfoCoro(std::string capurl, bool im_via_email, std::string directory_visibility)

View File

@ -1592,13 +1592,12 @@ void LLAgentCamera::updateCamera()
// LL_INFOS() << "Current FOV Zoom: " << mCameraCurrentFOVZoomFactor << " Target FOV Zoom: " << mCameraFOVZoomFactor << " Object penetration: " << mFocusObjectDist << LL_ENDL;
LLVector3 focus_agent = gAgent.getPosAgentFromGlobal(mFocusGlobal);
LLVector3 position_agent = gAgent.getPosAgentFromGlobal(camera_pos_global);
mCameraPositionAgent = gAgent.getPosAgentFromGlobal(camera_pos_global);
// Try to move the camera
// Move the camera
LLViewerCamera::getInstance()->updateCameraLocation(mCameraPositionAgent, mCameraUpVector, focus_agent);
//LLViewerCamera::getInstance()->updateCameraLocation(mCameraPositionAgent, camera_skyward, focus_agent);
if (!LLViewerCamera::getInstance()->updateCameraLocation(position_agent, mCameraUpVector, focus_agent))
return;
// Change FOV
LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / (1.f + mCameraCurrentFOVZoomFactor));
@ -1606,7 +1605,7 @@ void LLAgentCamera::updateCamera()
// follow camera when in customize mode
if (cameraCustomizeAvatar())
{
setLookAt(LOOKAT_TARGET_FOCUS, NULL, mCameraPositionAgent);
setLookAt(LOOKAT_TARGET_FOCUS, NULL, position_agent);
}
// update the travel distance stat
@ -1625,8 +1624,8 @@ void LLAgentCamera::updateCamera()
LLVector3 head_pos = gAgentAvatarp->mHeadp->getWorldPosition() +
LLVector3(0.08f, 0.f, 0.05f) * gAgentAvatarp->mHeadp->getWorldRotation() +
LLVector3(0.1f, 0.f, 0.f) * gAgentAvatarp->mPelvisp->getWorldRotation();
LLVector3 diff = mCameraPositionAgent - head_pos;
diff = diff * ~gAgentAvatarp->mRoot->getWorldRotation();
LLVector3 diff = position_agent - head_pos;
diff *= ~gAgentAvatarp->mRoot->getWorldRotation();
LLJoint* torso_joint = gAgentAvatarp->mTorsop;
LLJoint* chest_joint = gAgentAvatarp->mChestp;
@ -2570,7 +2569,8 @@ void LLAgentCamera::changeCameraToFollow(bool animate)
AOEngine::getInstance()->inMouselook(false);
// bang-in the current focus, position, and up vector of the follow cam
mFollowCam.reset(mCameraPositionAgent, LLViewerCamera::getInstance()->getPointOfInterest(), LLVector3::z_axis);
const LLViewerCamera& camera = LLViewerCamera::instance();
mFollowCam.reset(camera.getOrigin(), camera.getPointOfInterest(), LLVector3::z_axis);
if (gBasicToolset)
{

View File

@ -121,11 +121,8 @@ private:
// Preset
//--------------------------------------------------------------------
public:
// [RLVa:KB] - @setcam family
/** Determines default camera offset scale depending on the current camera preset */
ECameraPreset getCameraPreset() const { return mCameraPreset; }
// [/RLVa:KB]
void switchCameraPreset(ECameraPreset preset);
ECameraPreset getCameraPreset() const { return mCameraPreset; }
/** Determines default camera offset depending on the current camera preset */
LLVector3 getCameraOffsetInitial();
// [RLVa:KB] - @setcam_eyeoffsetscale
@ -177,8 +174,9 @@ public:
F32 getCameraMinOffGround(); // Minimum height off ground for this mode, meters
void setCameraCollidePlane(const LLVector4 &plane) { mCameraCollidePlane = plane; }
bool calcCameraMinDistance(F32 &obj_min_distance);
F32 getCurrentCameraBuildOffset() { return (F32)mCameraFocusOffset.length(); }
F32 getCurrentCameraBuildOffset() const { return (F32)mCameraFocusOffset.length(); }
void clearCameraLag() { mCameraLag.clearVec(); }
const LLVector3& getCameraUpVector() const { return mCameraUpVector; }
private:
LLVector3 getAvatarRootPosition();
@ -188,7 +186,6 @@ private:
F32 mCameraCurrentFOVZoomFactor; // Interpolated fov zoom
LLVector4 mCameraCollidePlane; // Colliding plane for camera
F32 mCameraZoomFraction; // Mousewheel driven fraction of zoom
LLVector3 mCameraPositionAgent; // Camera position in agent coordinates
LLVector3 mCameraVirtualPositionAgent; // Camera virtual position (target) before performing FOV zoom
LLVector3d mCameraSmoothingLastPositionGlobal;
LLVector3d mCameraSmoothingLastPositionAgent;
@ -320,7 +317,7 @@ public:
F32 getAgentHUDTargetZoom();
void resetCameraZoomFraction();
F32 getCurrentCameraZoomFraction() { return mCameraZoomFraction; }
F32 getCurrentCameraZoomFraction() const { return mCameraZoomFraction; }
//--------------------------------------------------------------------
// Pan

View File

@ -322,9 +322,7 @@ void LLAgentPilot::moveCamera()
LLViewerCamera::getInstance()->setView(view);
LLViewerCamera::getInstance()->setOrigin(origin);
LLViewerCamera::getInstance()->mXAxis = LLVector3(mat.mMatrix[0]);
LLViewerCamera::getInstance()->mYAxis = LLVector3(mat.mMatrix[1]);
LLViewerCamera::getInstance()->mZAxis = LLVector3(mat.mMatrix[2]);
LLViewerCamera::getInstance()->setAxes(mat);
}
}

View File

@ -1230,12 +1230,12 @@ void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& it
{
gAgentAvatarp->setCompositeUpdatesEnabled(true);
// If we have not yet declouded, we may want to use
// If we have not yet loaded core parts, we may want to use
// baked texture UUIDs sent from the first objectUpdate message
// don't overwrite these. If we have already declouded, we've saved
// these ids as the last known good textures and can invalidate without
// re-clouding.
if (!gAgentAvatarp->getIsCloud())
// don't overwrite these. If we have parts already, we've saved
// these texture ids as the last known good textures and can
// invalidate without having to recloud avatar.
if (!gAgentAvatarp->getHasMissingParts())
{
gAgentAvatarp->invalidateAll();
}

View File

@ -844,7 +844,7 @@ void AISAPI::onUpdateReceived(const LLSD& update, COMMAND_TYPE type, const LLSD&
if ( (type == UPDATECATEGORY || type == UPDATEITEM)
&& gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
{
dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update);
dump_sequential_xml(gAgentAvatarp->getDebugName() + "_ais_update", update);
}
AISUpdate ais_update(update, type, request_body);

View File

@ -868,7 +868,7 @@ void LLWearableHoldingPattern::checkMissingWearables()
// was requested but none was found, create a default asset as a replacement.
// In all other cases, don't do anything.
// For critical types (shape/hair/skin/eyes), this will keep the avatar as a cloud
// due to logic in LLVOAvatarSelf::getIsCloud().
// due to logic in LLVOAvatarSelf::getHasMissingParts().
// For non-critical types (tatoo, socks, etc.) the wearable will just be missing.
(requested_by_type[type] > 0) &&
((type == LLWearableType::WT_PANTS) || (type == LLWearableType::WT_SHIRT) || (type == LLWearableType::WT_SKIRT)))
@ -2152,7 +2152,7 @@ bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id)
}
// Moved from LLWearableList::ContextMenu for wider utility.
bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids) const
bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids, bool warn_on_type_mismatch) const
{
// TODO: investigate wearables may not be loaded at this point EXT-8231
@ -2182,7 +2182,10 @@ bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids) const
}
else
{
if (warn_on_type_mismatch)
{
LL_WARNS() << "Unexpected wearable type" << LL_ENDL;
}
return false;
}
}
@ -2464,7 +2467,7 @@ void LLAppearanceMgr::updateCOF(LLInventoryModel::item_array_t& body_items_new,
}
if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
{
dump_sequential_xml(gAgentAvatarp->getFullname() + "_slam_request", contents);
dump_sequential_xml(gAgentAvatarp->getDebugName() + "_slam_request", contents);
}
slam_inventory_folder(getCOF(), contents, link_waiter);
@ -4381,7 +4384,7 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd
LL_DEBUGS("Avatar") << "succeeded" << LL_ENDL;
if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage"))
{
dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_ok", result);
dump_sequential_xml(gAgentAvatarp->getDebugName() + "_appearance_request_ok", result);
}
} while (bRetry);
@ -4489,7 +4492,7 @@ void LLAppearanceMgr::syncCofVersionAndRefreshCoro()
/*static*/
void LLAppearanceMgr::debugAppearanceUpdateCOF(const LLSD& content)
{
dump_sequential_xml(gAgentAvatarp->getFullname() + "_appearance_request_error", content);
dump_sequential_xml(gAgentAvatarp->getDebugName() + "_appearance_request_error", content);
LL_INFOS("Avatar") << "AIS COF, version received: " << content["expected"].asInteger()
<< " ================================= " << LL_ENDL;

View File

@ -108,7 +108,7 @@ public:
bool getCanReplaceCOF(const LLUUID& outfit_cat_id);
// Can we add all referenced items to the avatar?
bool canAddWearables(const uuid_vec_t& item_ids) const;
bool canAddWearables(const uuid_vec_t& item_ids, bool warn_on_type_mismatch = true) const;
// Copy all items in a category.
void shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id,

View File

@ -301,6 +301,16 @@ using namespace LL;
#include "glib.h"
#endif // (LL_LINUX) && LL_GTK
#ifdef LL_DISCORD
#define DISCORDPP_IMPLEMENTATION
#include <discordpp.h>
static std::shared_ptr<discordpp::Client> gDiscordClient;
static uint64_t gDiscordTimestampsStart;
static std::string gDiscordActivityDetails;
static int32_t gDiscordPartyCurrentSize;
static int32_t gDiscordPartyMaxSize;
#endif
const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml"; // <FS:ND/> We need this filename defined here.
static LLAppViewerListener sAppViewerListener(LLAppViewer::instance);
@ -506,6 +516,12 @@ static bool app_metrics_qa_mode = false;
void idle_afk_check()
{
// Don't check AFK status during startup states
if (LLStartUp::getStartupState() < STATE_STARTED)
{
return;
}
// check idle timers
F32 current_idle = gAwayTriggerTimer.getElapsedTimeF32();
// <FS:CR> Cache frequently hit location
@ -516,11 +532,20 @@ void idle_afk_check()
// [/RLVa:KB]
// static LLCachedControl<S32> afk_timeout(gSavedSettings, "AFKTimeout", 300);
// <FS:CR> Explicit conversions just cos.
//if (afk_timeout() && (current_idle > (F32)afk_timeout()) && !gAgent.getAFK())
if (static_cast<S32>(afk_timeout) && (current_idle > static_cast<F32>(afk_timeout)) && ! gAgent.getAFK())
//if (afk_timeout() && (current_idle > afk_timeout()) && !gAgent.getAFK())
if (static_cast<S32>(afk_timeout) && (current_idle > static_cast<F32>(afk_timeout)))
{
LL_INFOS("IdleAway") << "Idle more than " << afk_timeout << " seconds: automatically changing to Away status" << LL_ENDL;
gAgent.setAFK();
if (!gAgent.getAFK())
{
LL_INFOS("IdleAway") << "Idle more than " << afk_timeout << " seconds: automatically changing to Away status" << LL_ENDL;
gAgent.setAFK();
}
else
{
// Refresh timer so that random one click or hover won't clear the status.
// But expanding the window still should lift afk status
gAwayTimer.reset();
}
}
}
@ -1546,6 +1571,13 @@ bool LLAppViewer::frame()
bool LLAppViewer::doFrame()
{
#ifdef LL_DISCORD
{
LL_PROFILE_ZONE_NAMED("discord_callbacks");
discordpp::RunCallbacks();
}
#endif
LL_RECORD_BLOCK_TIME(FTM_FRAME);
{
// and now adjust the visuals from previous frame.
@ -2185,36 +2217,6 @@ bool LLAppViewer::cleanup()
// Clean up before GL is shut down because we might be holding on to objects with texture references
LLSelectMgr::cleanupGlobals();
LL_INFOS() << "Shutting down OpenGL" << LL_ENDL;
// Shut down OpenGL
if( gViewerWindow)
{
gViewerWindow->shutdownGL();
// Destroy window, and make sure we're not fullscreen
// This may generate window reshape and activation events.
// Therefore must do this before destroying the message system.
delete gViewerWindow;
gViewerWindow = NULL;
LL_INFOS() << "ViewerWindow deleted" << LL_ENDL;
}
LLSplashScreen::show();
LLSplashScreen::update(LLTrans::getString("ShuttingDown"));
LL_INFOS() << "Cleaning up Keyboard & Joystick" << LL_ENDL;
// viewer UI relies on keyboard so keep it aound until viewer UI isa gone
delete gKeyboard;
gKeyboard = NULL;
if (LLViewerJoystick::instanceExists())
{
// Turn off Space Navigator and similar devices
LLViewerJoystick::getInstance()->terminate();
}
LL_INFOS() << "Cleaning up Objects" << LL_ENDL;
LLViewerObject::cleanupVOClasses();
@ -2413,6 +2415,36 @@ bool LLAppViewer::cleanup()
sTextureFetch->shutDownTextureCacheThread() ;
LLLFSThread::sLocal->shutdown();
LL_INFOS() << "Shutting down OpenGL" << LL_ENDL;
// Shut down OpenGL
if (gViewerWindow)
{
gViewerWindow->shutdownGL();
// Destroy window, and make sure we're not fullscreen
// This may generate window reshape and activation events.
// Therefore must do this before destroying the message system.
delete gViewerWindow;
gViewerWindow = NULL;
LL_INFOS() << "ViewerWindow deleted" << LL_ENDL;
}
LLSplashScreen::show();
LLSplashScreen::update(LLTrans::getString("ShuttingDown"));
LL_INFOS() << "Cleaning up Keyboard & Joystick" << LL_ENDL;
// viewer UI relies on keyboard so keep it aound until viewer UI isa gone
delete gKeyboard;
gKeyboard = NULL;
if (LLViewerJoystick::instanceExists())
{
// Turn off Space Navigator and similar devices
LLViewerJoystick::getInstance()->terminate();
}
LL_INFOS() << "Shutting down message system" << LL_ENDL;
end_messaging_system();
@ -3668,15 +3700,15 @@ bool LLAppViewer::initWindow()
.height(gSavedSettings.getU32("WindowHeight"))
.min_width(gSavedSettings.getU32("MinWindowWidth"))
.min_height(gSavedSettings.getU32("MinWindowHeight"))
/// <FS:CR> Since the 3.6.5 merge, setting fullscreen does terrible bad things on macs like opening
/// all floaters and menus off the left side of the screen. Let's not do that right now...
/// Hardcoding full screen OFF until it's fixed. On 10.7+ we have native full screen support anyway.
#ifndef LL_DARWIN
.fullscreen(gSavedSettings.getBOOL("FullScreen"))
#else // !LL_DARWIN
#ifdef LL_DARWIN
// Setting it to true causes black screen with no UI displayed.
// Given that it's a DEBUG settings and application goes fullscreen
// on mac simply by expanding it, it was decided to not support/use
// this setting on mac.
.fullscreen(false)
#endif // !LL_DARWIN
// </FS:CR>
#else // LL_DARWIN
.fullscreen(gSavedSettings.getBOOL("FullScreen"))
#endif
.ignore_pixel_depth(ignorePixelDepth)
.first_run(mIsFirstRun);
@ -6883,3 +6915,180 @@ void LLAppViewer::metricsSend(bool enable_reporting)
gViewerAssetStats->restart();
}
#ifdef LL_DISCORD
void LLAppViewer::initDiscordSocial()
{
gDiscordPartyCurrentSize = 1;
gDiscordPartyMaxSize = 0;
gDiscordTimestampsStart = time(nullptr);
gDiscordClient = std::make_shared<discordpp::Client>();
gDiscordClient->SetStatusChangedCallback([](discordpp::Client::Status status, discordpp::Client::Error, int32_t) {
if (status == discordpp::Client::Status::Ready)
{
updateDiscordActivity();
}
});
if (gSavedSettings.getBOOL("EnableDiscord"))
{
auto credential = gSecAPIHandler->loadCredential("Discord");
if (credential.notNull())
{
gDiscordClient->UpdateToken(discordpp::AuthorizationTokenType::Bearer, credential->getAuthenticator()["token"].asString(), [](discordpp::ClientResult result) {
if (result.Successful())
gDiscordClient->Connect();
else
LL_WARNS("Discord") << result.Error() << LL_ENDL;
});
}
else
{
LL_WARNS("Discord") << "Integration was enabled, but no credentials. Disabling integration." << LL_ENDL;
gSavedSettings.setBOOL("EnableDiscord", false);
}
}
}
void LLAppViewer::toggleDiscordIntegration(const LLSD& value)
{
static const uint64_t APPLICATION_ID = 1394782217405862001;
if (value.asBoolean())
{
discordpp::AuthorizationArgs args{};
args.SetClientId(APPLICATION_ID);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
auto codeVerifier = gDiscordClient->CreateAuthorizationCodeVerifier();
args.SetCodeChallenge(codeVerifier.Challenge());
gDiscordClient->Authorize(args, [codeVerifier](auto result, auto code, auto redirectUri) {
if (result.Successful())
{
gDiscordClient->GetToken(APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri, [](discordpp::ClientResult result, std::string accessToken, std::string, discordpp::AuthorizationTokenType, int32_t, std::string) {
if (result.Successful())
{
gDiscordClient->UpdateToken(discordpp::AuthorizationTokenType::Bearer, accessToken, [accessToken](discordpp::ClientResult result) {
if (result.Successful())
{
LLSD authenticator = LLSD::emptyMap();
authenticator["token"] = accessToken;
gSecAPIHandler->saveCredential(gSecAPIHandler->createCredential("Discord", LLSD::emptyMap(), authenticator), true);
gDiscordClient->Connect();
}
else
{
LL_WARNS("Discord") << result.Error() << LL_ENDL;
}
});
}
else
{
LL_WARNS("Discord") << result.Error() << LL_ENDL;
}
});
}
else
{
LL_WARNS("Discord") << result.Error() << LL_ENDL;
gSavedSettings.setBOOL("EnableDiscord", false);
}
});
}
else
{
gDiscordClient->Disconnect();
auto credential = gSecAPIHandler->loadCredential("Discord");
if (credential.notNull())
{
gDiscordClient->RevokeToken(APPLICATION_ID, credential->getAuthenticator()["token"].asString(), [](discordpp::ClientResult result) {
if (result.Successful())
LL_INFOS("Discord") << "Access token successfully revoked." << LL_ENDL;
else
LL_WARNS("Discord") << "No access token to revoke." << LL_ENDL;
});
auto cred = new LLCredential("Discord");
gSecAPIHandler->deleteCredential(cred);
}
else
{
LL_WARNS("Discord") << "Credentials are already nonexistent." << LL_ENDL;
}
}
}
void LLAppViewer::updateDiscordActivity()
{
LL_PROFILE_ZONE_SCOPED;
discordpp::Activity activity;
activity.SetType(discordpp::ActivityTypes::Playing);
discordpp::ActivityTimestamps timestamps;
timestamps.SetStart(gDiscordTimestampsStart);
activity.SetTimestamps(timestamps);
if (gAgent.getID() == LLUUID::null)
{
gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {});
return;
}
static LLCachedControl<bool> show_details(gSavedSettings, "ShowDiscordActivityDetails", false);
if (show_details)
{
if (gDiscordActivityDetails.empty())
{
LLAvatarName av_name;
LLAvatarNameCache::get(gAgent.getID(), &av_name);
gDiscordActivityDetails = av_name.getUserName();
auto displayName = av_name.getDisplayName();
if (gDiscordActivityDetails != displayName)
gDiscordActivityDetails = displayName + " (" + gDiscordActivityDetails + ")";
}
activity.SetDetails(gDiscordActivityDetails);
}
static LLCachedControl<bool> show_state(gSavedSettings, "ShowDiscordActivityState", false);
if (show_state)
{
auto agent_pos_region = gAgent.getPositionAgent();
S32 pos_x = S32(agent_pos_region.mV[VX] + 0.5f);
S32 pos_y = S32(agent_pos_region.mV[VY] + 0.5f);
S32 pos_z = S32(agent_pos_region.mV[VZ] + 0.5f);
F32 velocity_mag_sq = gAgent.getVelocity().magVecSquared();
const F32 FLY_CUTOFF = 6.f;
const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF;
const F32 WALK_CUTOFF = 1.5f;
const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF;
if (velocity_mag_sq > FLY_CUTOFF_SQ)
{
pos_x -= pos_x % 4;
pos_y -= pos_y % 4;
}
else if (velocity_mag_sq > WALK_CUTOFF_SQ)
{
pos_x -= pos_x % 2;
pos_y -= pos_y % 2;
}
auto location = llformat("%s (%d, %d, %d)", gAgent.getRegion()->getName().c_str(), pos_x, pos_y, pos_z);
activity.SetState(location);
discordpp::ActivityParty party;
party.SetId(location);
party.SetCurrentSize(gDiscordPartyCurrentSize);
party.SetMaxSize(gDiscordPartyMaxSize);
activity.SetParty(party);
}
gDiscordClient->UpdateRichPresence(activity, [](discordpp::ClientResult) {});
}
void LLAppViewer::updateDiscordPartyCurrentSize(int32_t size)
{
gDiscordPartyCurrentSize = size;
updateDiscordActivity();
}
void LLAppViewer::updateDiscordPartyMaxSize(int32_t size)
{
gDiscordPartyMaxSize = size;
updateDiscordActivity();
}
#endif

View File

@ -252,6 +252,14 @@ public:
// Note: mQuitRequested can be aborted by user.
void outOfMemorySoftQuit();
#ifdef LL_DISCORD
static void initDiscordSocial();
static void toggleDiscordIntegration(const LLSD& value);
static void updateDiscordActivity();
static void updateDiscordPartyCurrentSize(int32_t size);
static void updateDiscordPartyMaxSize(int32_t size);
#endif
protected:
virtual bool initWindow(); // Initialize the viewer's window.
virtual void initLoggingAndGetLastDuration(); // Initialize log files, logging system

View File

@ -514,6 +514,7 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
// *FIX: global
gIconResource = MAKEINTRESOURCE(IDI_LL_ICON);
gIconSmallResource = MAKEINTRESOURCE(IDI_LL_ICON_SMALL);
LLAppViewerWin32* viewer_app_ptr = new LLAppViewerWin32(ll_convert_wide_to_string(pCmdLine).c_str());

View File

@ -537,11 +537,12 @@ LLAutoReplaceSettings::AddListResult LLAutoReplaceSettings::replaceList(const LL
S32 search_index;
LLSD targetList;
// The following is working around the fact that LLSD arrays containing maps also seem to have undefined entries... see LLSD-30
for ( search_index = 0, targetList = mLists[0];
for ( search_index = 0;
!listFound && search_index < mLists.size();
search_index += 1, targetList = mLists[search_index]
search_index += 1
)
{
targetList = mLists[search_index];
if ( targetList.isMap() )
{
if ( listNameMatches( targetList, listName) )

View File

@ -989,7 +989,7 @@ protected:
menu->setItemEnabled("Voice Call", false);
menu->setItemEnabled("Chat History", false);
menu->setItemEnabled("Invite Group", false);
menu->setItemEnabled("Zoom In", false);
menu->setItemEnabled("Zoom In", true);
menu->setItemEnabled("Share", false);
menu->setItemEnabled("Pay", false);
menu->setItemEnabled("Block Unblock", false);

View File

@ -101,6 +101,12 @@ LLVOAvatar *LLControlAvatar::getAttachedAvatar()
void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_scale_fixup) const
{
static LLCachedControl<F32> anim_max_legal_offset(gSavedSettings, "AnimatedObjectsMaxLegalOffset", MAX_LEGAL_OFFSET);
F32 max_legal_offset = llmax(anim_max_legal_offset(), 0.f);
static LLCachedControl<F32> anim_max_legal_size(gSavedSettings, "AnimatedObjectsMaxLegalSize", MAX_LEGAL_SIZE);
F32 max_legal_size = llmax(anim_max_legal_size(), 1.f);
new_pos_fixup = LLVector3();
new_scale_fixup = 1.0f;
LLVector3 vol_pos = mRootVolp->getRenderPosition();
@ -125,14 +131,14 @@ void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_
{
LLVector3 pos_box_offset = point_to_box_offset(vol_pos, unshift_extents);
F32 offset_dist = pos_box_offset.length();
if (offset_dist > MAX_LEGAL_OFFSET && offset_dist > 0.f)
if (offset_dist > max_legal_offset && offset_dist > 0.f)
{
F32 target_dist = (offset_dist - MAX_LEGAL_OFFSET);
F32 target_dist = (offset_dist - max_legal_offset);
new_pos_fixup = (target_dist/offset_dist)*pos_box_offset;
}
if (new_pos_fixup != mPositionConstraintFixup)
{
LL_DEBUGS("ConstraintFix") << getFullname() << " pos fix, offset_dist " << offset_dist << " pos fixup "
LL_DEBUGS("ConstraintFix") << getDebugName() << " pos fix, offset_dist " << offset_dist << " pos fixup "
<< new_pos_fixup << " was " << mPositionConstraintFixup << LL_ENDL;
LL_DEBUGS("ConstraintFix") << "vol_pos " << vol_pos << LL_ENDL;
LL_DEBUGS("ConstraintFix") << "extents " << extents[0] << " " << extents[1] << LL_ENDL;
@ -140,11 +146,11 @@ void LLControlAvatar::getNewConstraintFixups(LLVector3& new_pos_fixup, F32& new_
}
}
if (box_size/mScaleConstraintFixup > MAX_LEGAL_SIZE)
if (box_size/mScaleConstraintFixup > max_legal_size)
{
new_scale_fixup = mScaleConstraintFixup* MAX_LEGAL_SIZE /box_size;
LL_DEBUGS("ConstraintFix") << getFullname() << " scale fix, box_size " << box_size << " fixup "
<< mScaleConstraintFixup << " max legal " << MAX_LEGAL_SIZE
new_scale_fixup = mScaleConstraintFixup*max_legal_size/box_size;
LL_DEBUGS("ConstraintFix") << getDebugName() << " scale fix, box_size " << box_size << " fixup "
<< mScaleConstraintFixup << " max legal " << max_legal_size
<< " -> new scale " << new_scale_fixup << LL_ENDL;
}
}
@ -227,7 +233,7 @@ void LLControlAvatar::matchVolumeTransform()
const LLMeshSkinInfo* skin_info = mRootVolp->getSkinInfo();
if (skin_info)
{
LL_DEBUGS("BindShape") << getFullname() << " bind shape " << skin_info->mBindShapeMatrix << LL_ENDL;
LL_DEBUGS("BindShape") << getDebugName() << " bind shape " << skin_info->mBindShapeMatrix << LL_ENDL;
bind_rot = LLSkinningUtil::getUnscaledQuaternion(LLMatrix4(skin_info->mBindShapeMatrix));
}
#endif

View File

@ -691,7 +691,7 @@ void LLConversationLog::onClearLogResponse(const LLSD& notification, const LLSD&
{
mConversations.clear();
notifyObservers();
cache();
saveToFile(getFileName());
deleteBackupLogs();
}
}

View File

@ -302,7 +302,7 @@ void LLConversationItemSession::updateName(LLConversationItemParticipant* partic
for (auto itemp : mChildren)
{
LLConversationItem* current_participant = dynamic_cast<LLConversationItem*>(itemp);
LLConversationItem* current_participant = dynamic_cast<LLConversationItem*>(itemp.get());
// Add the avatar uuid to the list (except if it's the own agent uuid)
if (current_participant->getUUID() != gAgentID)
{
@ -331,6 +331,7 @@ void LLConversationItemSession::updateName(LLConversationItemParticipant* partic
void LLConversationItemSession::removeParticipant(LLConversationItemParticipant* participant)
{
LLPointer<LLFolderViewModelItem> holder(participant);
removeChild(participant);
mNeedsRefresh = true;
updateName(participant);
@ -362,15 +363,10 @@ void LLConversationItemSession::clearAndDeparentModels()
for (child_list_t::iterator it = mChildren.begin(); it != mChildren.end();)
{
LLFolderViewModelItem* child = *it;
if (child->getNumRefs() == 0)
// Note that model might still be assigned to some view/widget
// and have a different parent
if (child->getParent() == this)
{
// LLConversationItemParticipant can be created but not assigned to any view,
// it was waiting for an "add_participant" event to be processed
delete child;
}
else
{
// Model is still assigned to some view/widget
child->setParent(NULL);
}
it = mChildren.erase(it);
@ -385,7 +381,7 @@ LLConversationItemParticipant* LLConversationItemSession::findParticipant(const
child_list_t::iterator iter;
for (iter = mChildren.begin(); iter != mChildren.end(); iter++)
{
participant = dynamic_cast<LLConversationItemParticipant*>(*iter);
participant = dynamic_cast<LLConversationItemParticipant*>((*iter).get());
if (participant && participant->hasSameValue(participant_id))
{
break;
@ -495,7 +491,7 @@ const bool LLConversationItemSession::getTime(F64& time) const
child_list_t::const_iterator iter;
for (iter = mChildren.begin(); iter != mChildren.end(); iter++)
{
participant = dynamic_cast<LLConversationItemParticipant*>(*iter);
participant = dynamic_cast<LLConversationItemParticipant*>((*iter).get());
F64 participant_time;
if (participant && participant->getTime(participant_time))
{
@ -519,7 +515,7 @@ void LLConversationItemSession::dumpDebugData(bool dump_children)
{
for (child_list_t::iterator iter = mChildren.begin(); iter != mChildren.end(); iter++)
{
LLConversationItemParticipant* participant = dynamic_cast<LLConversationItemParticipant*>(*iter);
LLConversationItemParticipant* participant = dynamic_cast<LLConversationItemParticipant*>((*iter).get());
if (participant)
{
participant->dumpDebugData();

View File

@ -82,6 +82,9 @@ public:
virtual LLPointer<LLUIImage> getOpenIcon() const { return getIcon(); }
virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; }
virtual std::string getLabelSuffix() const { return LLStringUtil::null; }
virtual bool isFavorite() const { return false; }
virtual bool isAgentInventory() const { return false; }
virtual bool isAgentInventoryRoot() const { return false; }
virtual bool isItemRenameable() const { return true; }
virtual bool renameItem(const std::string& new_name) { mName = new_name; mNeedsRefresh = true; return true; }
virtual bool isItemMovable( void ) const { return false; }

View File

@ -1455,6 +1455,20 @@ static std::string add_collada_filter_to_gtkchooser(GtkWindow *picker)
LLTrans::getString("collada_files") + " (*.dae)");
}
// <FS:Beq> migrate to GLTF support
static std::string add_model_filter_to_gtkchooser(GtkWindow *picker)
{
// "Model files (*.dae, *.gltf, *.glb)"
GtkFileFilter *gfilter = gtk_file_filter_new();
gtk_file_filter_add_pattern(gfilter, "*.dae");
gtk_file_filter_add_pattern(gfilter, "*.gltf");
gtk_file_filter_add_pattern(gfilter, "*.glb");
std::string filtername = LLTrans::getString("model_files") + " (*.dae; *.gltf; *.glb)";
add_common_filters_to_gtkchooser(gfilter, picker, filtername);
return filtername;
}
// </FS:Beq>
static std::string add_imageload_filter_to_gtkchooser(GtkWindow *picker)
{
GtkFileFilter *gfilter = gtk_file_filter_new();
@ -1676,7 +1690,7 @@ bool LLFilePicker::getOpenFile( ELoadFilter filter, bool blocking )
filtername = dead_code_should_blow_up_here(picker);
break;
case FFLOAD_COLLADA:
filtername = add_collada_filter_to_gtkchooser(picker);
filtername = add_model_filter_to_gtkchooser(picker);
break;
case FFLOAD_IMAGE:
filtername = add_imageload_filter_to_gtkchooser(picker);
@ -1936,6 +1950,9 @@ bool LLFilePicker::openFileDialog( int32_t filter, bool blocking, EType aType )
file_dialog_filter = "*.raw";
break;
case FFLOAD_MODEL:
file_type = "model_files";
file_dialog_filter = "*.{dae,gltf,glb}";
break;
case FFLOAD_COLLADA:
file_type = "collada_files";
file_dialog_filter = "*.dae";

View File

@ -420,17 +420,15 @@ bool LLFloaterAutoReplaceSettings::callbackNewListName(const LLSD& notification,
{
LL_DEBUGS("AutoReplace")<<"called"<<LL_ENDL;
// <FS:Ansariel> FIRE-31256: Add Cancel button for new auto-replace list dialog
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if (option != 0)
{
return false;
}
// </FS:Ansariel>
LLSD newList = notification["payload"]["list"];
if ( response.has("listname") && response["listname"].isString() )
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if (option != 1) // Must also match RenameAutoReplaceList
{
// user cancelled
return false;
}
else if (response.has("listname") && response["listname"].isString() )
{
std::string newName = response["listname"].asString();
LLAutoReplaceSettings::setListName(newList, newName);
@ -516,12 +514,53 @@ bool LLFloaterAutoReplaceSettings::callbackListNameConflict(const LLSD& notifica
return false;
}
bool LLFloaterAutoReplaceSettings::callbackRemoveList(const LLSD& notification, const LLSD& response)
{
std::string listName = notification["payload"]["list"];
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
switch (option)
{
case 1:
if (mSettings.removeReplacementList(listName))
{
LL_INFOS("AutoReplace") << "deleted list '" << listName << "'" << LL_ENDL;
mReplacementsList->deleteSelectedItems(); // remove from the scrolling list
mSelectedListName.clear();
updateListNames();
updateListNamesControls();
updateReplacementsList();
}
break;
case 0:
break;
default:
LL_ERRS("AutoReplace") << "invalid selected option " << option << LL_ENDL;
}
return false;
}
void LLFloaterAutoReplaceSettings::onDeleteList()
{
std::string listName = mListNames->getSelectedValue().asString();
if ( ! listName.empty() )
{
if ( mSettings.removeReplacementList(listName) )
const LLSD* mappings = mSettings.getListEntries(mSelectedListName);
if (mappings->size() > 0)
{
LLSD payload;
payload["list"] = listName;
LLSD args;
args["MAP_SIZE"] = llformat("%d",mappings->size());
args["LIST_NAME"] = listName;
LLNotificationsUtil::add("RemoveAutoReplaceList", args, payload,
boost::bind(&LLFloaterAutoReplaceSettings::callbackRemoveList, this, _1, _2));
}
else if ( mSettings.removeReplacementList(listName) )
{
LL_INFOS("AutoReplace")<<"deleted list '"<<listName<<"'"<<LL_ENDL;
mReplacementsList->deleteSelectedItems(); // remove from the scrolling list

View File

@ -105,6 +105,8 @@ private:
bool callbackNewListName(const LLSD& notification, const LLSD& response);
/// called from the RenameAutoReplaceList notification dialog
bool callbackListNameConflict(const LLSD& notification, const LLSD& response);
/// called from the RemoveAutoReplaceList notification dialog
bool callbackRemoveList(const LLSD& notification, const LLSD& response);
bool selectedListIsFirst();
bool selectedListIsLast();

View File

@ -128,8 +128,8 @@ std::string STATUS[] =
//-----------------------------------------------------------------------------
// LLFloaterBvhPreview()
//-----------------------------------------------------------------------------
LLFloaterBvhPreview::LLFloaterBvhPreview(const std::string& filename) :
LLFloaterNameDesc(filename)
LLFloaterBvhPreview::LLFloaterBvhPreview(const LLSD& args) :
LLFloaterNameDesc(args)
{
mLastMouseX = 0;
mLastMouseY = 0;
@ -1447,7 +1447,8 @@ void LLFloaterBvhPreview::onBtnOK(void* userdata)
LLFloaterPerms::getNextOwnerPerms("Uploads"),
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost));
expected_upload_cost,
floaterp->mDestinationFolderId));
upload_new_resource(assetUploadInfo);
}

View File

@ -77,7 +77,7 @@ class LLFloaterBvhPreview : public LLFloaterNameDesc
friend class LLPreviewAnimation;
public:
LLFloaterBvhPreview(const std::string& filename);
LLFloaterBvhPreview(const LLSD& args);
virtual ~LLFloaterBvhPreview();
bool postBuild();

View File

@ -94,7 +94,7 @@ uuid_vec_t LLFloaterChatMentionPicker::getParticipantIds()
//LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd();
//while (current_participant_model != end_participant_model)
//{
// LLConversationItem* participant_model = dynamic_cast<LLConversationItem*>(*current_participant_model);
// LLConversationItem* participant_model = dynamic_cast<LLConversationItem*>((*current_participant_model).get());
// if (participant_model)
// {
// avatar_ids.push_back(participant_model->getUUID());

View File

@ -133,7 +133,8 @@ protected:
LLSettingsEditPanel() :
LLPanel(),
mIsDirty(false),
mOnDirtyChanged()
mOnDirtyChanged(),
mCanEdit(false)
{}
private:

View File

@ -32,6 +32,7 @@
#include "llimagetga.h"
#include "llimagejpeg.h"
#include "llimagepng.h"
#include "llimagej2c.h"
#include "llagent.h"
#include "llagentbenefits.h"
@ -43,6 +44,10 @@
#include "llrender.h"
#include "llface.h"
#include "llfocusmgr.h"
#include "llfilesystem.h"
#include "llfloaterperms.h"
#include "llnotificationsutil.h"
#include "llstatusbar.h" // can_afford_transaction()
#include "lltextbox.h"
#include "lltoolmgr.h"
#include "llui.h"
@ -52,6 +57,7 @@
#include "llvoavatar.h"
#include "pipeline.h"
#include "lluictrlfactory.h"
#include "llviewermenufile.h" // upload_new_resource()
#include "llviewershadermgr.h"
#include "llviewertexturelist.h"
#include "llviewercontrol.h"
@ -85,8 +91,8 @@ constexpr F32 ALPHA_EMPTY_THRESHOLD_RATIO = 0.999f;
//-----------------------------------------------------------------------------
// LLFloaterImagePreview()
//-----------------------------------------------------------------------------
LLFloaterImagePreview::LLFloaterImagePreview(const std::string& filename) :
LLFloaterNameDesc(filename),
LLFloaterImagePreview::LLFloaterImagePreview(const LLSD& args) :
LLFloaterNameDesc(args),
mAvatarPreview(NULL),
mSculptedPreview(NULL),
@ -236,7 +242,7 @@ bool LLFloaterImagePreview::postBuild()
}
// <FS:Zi> detect and strip empty alpha layers from images on upload
// getChild<LLUICtrl>("ok_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnOK, this));
// getChild<LLUICtrl>("ok_btn")->setCommitCallback(boost::bind(&LLFloaterImagePreview::onBtnOK, this));
return true;
}
@ -392,6 +398,61 @@ void LLFloaterImagePreview::clearAllPreviewTextures()
}
}
//-----------------------------------------------------------------------------
// onBtnOK()
//-----------------------------------------------------------------------------
void LLFloaterImagePreview::onBtnOK()
{
getChildView("ok_btn")->setEnabled(false); // don't allow inadvertent extra uploads
S32 expected_upload_cost = getExpectedUploadCost();
if (can_afford_transaction(expected_upload_cost))
{
LL_INFOS() << "saving texture: " << mRawImagep->getWidth() << "x" << mRawImagep->getHeight() << LL_ENDL;
// gen a new uuid for this asset
LLTransactionID tid;
tid.generate();
LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
if (formatted->encode(mRawImagep, 0.0f))
{
LLFileSystem fmt_file(new_asset_id, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE);
fmt_file.write(formatted->getData(), formatted->getDataSize());
LLResourceUploadInfo::ptr_t assetUploadInfo(new LLResourceUploadInfo(
tid, LLAssetType::AT_TEXTURE,
getChild<LLUICtrl>("name_form")->getValue().asString(),
getChild<LLUICtrl>("description_form")->getValue().asString(),
0,
LLFolderType::FT_NONE, LLInventoryType::IT_NONE,
LLFloaterPerms::getNextOwnerPerms("Uploads"),
LLFloaterPerms::getGroupPerms("Uploads"),
LLFloaterPerms::getEveryonePerms("Uploads"),
expected_upload_cost
));
upload_new_resource(assetUploadInfo);
}
else
{
LLSD args;
args["REASON"] = LLImage::getLastThreadError();
LLNotificationsUtil::add("ErrorEncodingImage", args);
LL_WARNS() << "Error encoding image" << LL_ENDL;
}
}
else
{
LLSD args;
args["COST"] = llformat("%d", expected_upload_cost);
LLNotificationsUtil::add("ErrorCannotAffordUpload", args);
}
closeFloater(false);
}
//-----------------------------------------------------------------------------
// draw()
//-----------------------------------------------------------------------------
@ -545,16 +606,15 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename)
return false;
}
S32 max_width = gSavedSettings.getS32("max_texture_dimension_X");
S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y");
// raw image is limited to 256MB so need at least some upper limit that fits into that
constexpr S32 MAX_IMAGE_AREA = 8096 * 8096;
if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height))
if (image_info.getWidth() * image_info.getHeight() > MAX_IMAGE_AREA)
{
LLStringUtil::format_map_t args;
args["WIDTH"] = llformat("%d", max_width);
args["HEIGHT"] = llformat("%d", max_height);
args["PIXELS"] = llformat("%dM", (S32)(MAX_IMAGE_AREA / 1000000));
mImageLoadError = LLTrans::getString("texture_load_dimensions_error", args);
mImageLoadError = LLTrans::getString("texture_load_area_error", args);
return false;
}
@ -580,6 +640,46 @@ bool LLFloaterImagePreview::loadImage(const std::string& src_filename)
image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
return false;
}
// Downscale images to fit the max_texture_dimensions_*
S32 max_width = gSavedSettings.getS32("max_texture_dimension_X");
S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y");
S32 orig_width = raw_image->getWidth();
S32 orig_height = raw_image->getHeight();
if (orig_width > max_width || orig_height > max_height)
{
// Calculate scale factors
F32 width_scale = (F32)max_width / (F32)orig_width;
F32 height_scale = (F32)max_height / (F32)orig_height;
F32 scale = llmin(width_scale, height_scale);
// Calculate new dimensions, preserving aspect ratio
S32 new_width = LLImageRaw::contractDimToPowerOfTwo(
llclamp((S32)llroundf(orig_width * scale), 4, max_width)
);
S32 new_height = LLImageRaw::contractDimToPowerOfTwo(
llclamp((S32)llroundf(orig_height * scale), 4, max_height)
);
if (!raw_image->scale(new_width, new_height))
{
LL_WARNS() << "Failed to scale image from "
<< orig_width << "x" << orig_height
<< " to " << new_width << "x" << new_height << LL_ENDL;
return false;
}
// Inform the resident about the resized image
LLSD subs;
subs["[ORIGINAL_WIDTH]"] = orig_width;
subs["[ORIGINAL_HEIGHT]"] = orig_height;
subs["[NEW_WIDTH]"] = new_width;
subs["[NEW_HEIGHT]"] = new_height;
subs["[MAX_WIDTH]"] = max_width;
subs["[MAX_HEIGHT]"] = max_height;
LLNotificationsUtil::add("ImageUploadResized", subs);
}
raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT);
mRawImagep = raw_image;

View File

@ -111,7 +111,7 @@ protected:
class LLFloaterImagePreview : public LLFloaterNameDesc
{
public:
LLFloaterImagePreview(const std::string& filename);
LLFloaterImagePreview(const LLSD& args);
virtual ~LLFloaterImagePreview();
bool postBuild() override;
@ -127,6 +127,8 @@ public:
void clearAllPreviewTextures();
void onBtnOK();
protected:
static void onPreviewTypeCommit(LLUICtrl*,void*);
void draw() override;

Some files were not shown because too many files have changed in this diff Show More