Ansariel 2025-12-03 18:39:36 +01:00
commit b49c3a083b
135 changed files with 3119 additions and 867 deletions

View File

@ -86,7 +86,7 @@ jobs:
variants: ${{ matrix.configuration }} variants: ${{ matrix.configuration }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
ref: ${{ github.event.pull_request.head.sha || github.sha }} ref: ${{ github.event.pull_request.head.sha || github.sha }}
@ -95,14 +95,14 @@ jobs:
with: with:
python-version: "3.11" python-version: "3.11"
- name: Checkout build variables - name: Checkout build variables
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
repository: secondlife/build-variables repository: secondlife/build-variables
ref: master ref: master
path: .build-variables path: .build-variables
- name: Checkout master-message-template - name: Checkout master-message-template
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
repository: secondlife/master-message-template repository: secondlife/master-message-template
path: .master-message-template path: .master-message-template

View File

@ -16,8 +16,6 @@ on:
- "*alpha" - "*alpha"
- "*nightly" - "*nightly"
- "*preview" - "*preview"
schedule:
- cron: '00 03 * * *' # Run every day at 3am UTC
env: env:
AUTOBUILD_VARIABLES_FILE: ${{github.workspace}}/build-variables/variables AUTOBUILD_VARIABLES_FILE: ${{github.workspace}}/build-variables/variables
EXTRA_ARGS: -DUSE_FMODSTUDIO=ON -DUSE_KDU=ON --crashreporting EXTRA_ARGS: -DUSE_FMODSTUDIO=ON -DUSE_KDU=ON --crashreporting
@ -58,7 +56,7 @@ jobs:
echo "/usr/local/bin" >> $GITHUB_PATH echo "/usr/local/bin" >> $GITHUB_PATH
echo "$(brew --prefix)/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH echo "$(brew --prefix)/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
- uses: actions/checkout@v5 - uses: actions/checkout@v6
# Use apt-based Python when inside the Ubuntu 22.04 container # Use apt-based Python when inside the Ubuntu 22.04 container
- name: Install Python 3.11 (container case) - name: Install Python 3.11 (container case)
if: matrix.container_image == 'ubuntu:22.04' if: matrix.container_image == 'ubuntu:22.04'
@ -185,12 +183,12 @@ jobs:
shell: bash shell: bash
- name: Get the code - name: Get the code
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Checkout build vars (after the main code) - name: Checkout build vars (after the main code)
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
repository: FirestormViewer/fs-build-variables repository: FirestormViewer/fs-build-variables
path: build-variables path: build-variables
@ -521,7 +519,7 @@ jobs:
if: always() if: always()
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
sparse-checkout: | sparse-checkout: |
fsutils/download_list.py fsutils/download_list.py

View File

@ -58,7 +58,7 @@ jobs:
echo "/usr/local/bin" >> $GITHUB_PATH echo "/usr/local/bin" >> $GITHUB_PATH
echo "$(brew --prefix)/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH echo "$(brew --prefix)/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
- uses: actions/checkout@v5 - uses: actions/checkout@v6
# Use apt-based Python when inside the Ubuntu 22.04 container # Use apt-based Python when inside the Ubuntu 22.04 container
- name: Install Python 3.11 (container case) - name: Install Python 3.11 (container case)
if: matrix.container_image == 'ubuntu:22.04' if: matrix.container_image == 'ubuntu:22.04'
@ -185,12 +185,12 @@ jobs:
shell: bash shell: bash
- name: Get the code - name: Get the code
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Checkout build vars (after the main code) - name: Checkout build vars (after the main code)
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
repository: FirestormViewer/fs-build-variables repository: FirestormViewer/fs-build-variables
path: build-variables path: build-variables
@ -521,7 +521,7 @@ jobs:
if: always() if: always()
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
sparse-checkout: | sparse-checkout: |
fsutils/download_list.py fsutils/download_list.py

View File

@ -31,7 +31,7 @@ jobs:
contents: read contents: read
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
sparse-checkout: | sparse-checkout: |
fsutils/download_list.py fsutils/download_list.py

View File

@ -11,7 +11,7 @@ jobs:
pre-commit: pre-commit:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-python@v6 - uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x

View File

@ -50,7 +50,7 @@ jobs:
steps: steps:
# Checkout the Repository # Checkout the Repository
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
fetch-depth: 0 # Necessary to fetch all history for tagging fetch-depth: 0 # Necessary to fetch all history for tagging

View File

@ -278,3 +278,18 @@ void LLChatEntry::paste()
} }
} }
// </FS:Ansariel> // </FS:Ansariel>
// <FS:Zi> Add menu items to copy and/or insert mention URIs into chat
void LLChatEntry::insertMentionAtCursor(const std::string& str)
{
S32 cursor_from_end = getLength() - getCursorPos();
insertText(str);
std::string new_text(wstring_to_utf8str(getConvertedText()));
clear();
appendTextImpl(new_text, LLStyle::Params(), true);
setCursorPos(getLength() - cursor_from_end);
}
// </FS:Zi>

View File

@ -71,6 +71,9 @@ public:
// <FS:Ansariel> Changed to public so we can update history when using modifier keys // <FS:Ansariel> Changed to public so we can update history when using modifier keys
void updateHistory(); void updateHistory();
// <FS:Zi> Add menu items to copy and/or insert mention URIs into chat
void insertMentionAtCursor(const std::string& str);
// <FS:Ansariel> Fix linefeed pasting // <FS:Ansariel> Fix linefeed pasting
/*virtual*/ void paste(); /*virtual*/ void paste();

View File

@ -1268,6 +1268,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel)
p.button_flash_count(LLUI::getInstance()->mSettingGroups["config"]->getS32("FlashCount")); p.button_flash_count(LLUI::getInstance()->mSettingGroups["config"]->getS32("FlashCount"));
p.button_flash_rate(LLUI::getInstance()->mSettingGroups["config"]->getF32("FlashPeriod")); p.button_flash_rate(LLUI::getInstance()->mSettingGroups["config"]->getF32("FlashPeriod"));
// </FS:Ansariel> // </FS:Ansariel>
p.handle_right_mouse = false; // <FS:Zi> Fix tab rearranging when active tab was right clicked before
// *TODO : It seems wrong not to use p in both cases considering the way p is initialized // *TODO : It seems wrong not to use p in both cases considering the way p is initialized
if (mCustomIconCtrlUsed) if (mCustomIconCtrlUsed)

View File

@ -2482,6 +2482,11 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
enable_registrar.add("FS.EnableViewLog", std::bind(&FSRegistrarUtils::checkIsEnabled, gFSRegistrarUtils, target_id, EFSRegistrarFunctionActionType::FS_RGSTR_ACT_VIEW_TRANSCRIPT)); enable_registrar.add("FS.EnableViewLog", std::bind(&FSRegistrarUtils::checkIsEnabled, gFSRegistrarUtils, target_id, EFSRegistrarFunctionActionType::FS_RGSTR_ACT_VIEW_TRANSCRIPT));
// </FS:Ansariel> // </FS:Ansariel>
// <FS:Zi> Add menu items to copy and/or insert mention URIs into chat
registrar.add("Mention.CopyURI", boost::bind(&LLUrlAction::copyURLToClipboard, "secondlife:///app/agent/" + target_id_str + "/mention"));
registrar.add("Mention.Chat", boost::bind(&LLTextBase::insertMentionAtCursor, this, "secondlife:///app/agent/" + target_id_str + "/mention"));
// </FS:Zi>
// <FS:Zi> FIRE-30725 - Add more group functions to group URL context menu // <FS:Zi> FIRE-30725 - Add more group functions to group URL context menu
registrar.add("FS.JoinGroup", std::bind(&LLUrlAction::executeSLURL, "secondlife:///app/firestorm/" + target_id_str + "/groupjoin", true)); registrar.add("FS.JoinGroup", std::bind(&LLUrlAction::executeSLURL, "secondlife:///app/firestorm/" + target_id_str + "/groupjoin", true));
registrar.add("FS.LeaveGroup", std::bind(&LLUrlAction::executeSLURL, "secondlife:///app/firestorm/" + target_id_str + "/groupleave", true)); registrar.add("FS.LeaveGroup", std::bind(&LLUrlAction::executeSLURL, "secondlife:///app/firestorm/" + target_id_str + "/groupleave", true));
@ -2561,6 +2566,14 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url)
} }
// </FS:Zi> // </FS:Zi>
// <FS:Zi> Add menu items to copy and/or insert mention URIs into chat
if (!parent_floater || (parent_floater->getName() != "panel_im" && parent_floater->getName() != "nearby_chat"))
{
menu->getChild<LLView>("MentionURISeparator")->setVisible(false);
menu->getChild<LLView>("mention_in_chat")->setVisible(false);
}
// </FS:Zi>
menu->show(x, y); menu->show(x, y);
LLMenuGL::showPopup(this, menu, x, y); LLMenuGL::showPopup(this, menu, x, y);
} }
@ -4627,3 +4640,10 @@ void LLTextBase::setWordWrap(bool wrap)
{ {
mWordWrap = wrap; mWordWrap = wrap;
} }
// <FS:Zi> Add menu items to copy and/or insert mention URIs into chat
// virtual
void LLTextBase::insertMentionAtCursor(const std::string& str)
{
}
// </FS:Zi>

View File

@ -731,6 +731,9 @@ protected:
void appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, e_underline underline_link = e_underline::UNDERLINE_ALWAYS); void appendAndHighlightTextImpl(const std::string &new_text, S32 highlight_part, const LLStyle::Params& style_params, e_underline underline_link = e_underline::UNDERLINE_ALWAYS);
S32 normalizeUri(std::string& uri); S32 normalizeUri(std::string& uri);
// <FS:Zi> Add menu items to copy and/or insert mention URIs into chat
virtual void insertMentionAtCursor(const std::string& str);
protected: protected:
// virtual // virtual
std::string _getSearchText() const override std::string _getSearchText() const override

View File

@ -1268,9 +1268,11 @@ bool LLWindowMacOSX::setCursorPosition(const LLCoordWindow position)
// trigger mouse move callback // trigger mouse move callback
LLCoordGL gl_pos; LLCoordGL gl_pos;
convertCoords(position, &gl_pos); convertCoords(position, &gl_pos);
float scale = getSystemUISize(); // <FS:TJ> gl_pos is not meant to be scaled and breaks our pie menu and possibly other things
gl_pos.mX *= scale; //float scale = getSystemUISize();
gl_pos.mY *= scale; //gl_pos.mX *= scale;
//gl_pos.mY *= scale;
// </FS:TJ>
mCallbacks->handleMouseMove(this, gl_pos, (MASK)0); mCallbacks->handleMouseMove(this, gl_pos, (MASK)0);
return result; return result;

View File

@ -1108,7 +1108,6 @@ void MediaPluginCEF::keyEvent(dullahan::EKeyEvent key_event, LLSD native_key_dat
// <FS:ND> Keyboard handling for Linux. // <FS:ND> Keyboard handling for Linux.
#if LL_LINUX #if LL_LINUX
#if LL_SDL2
uint32_t native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger()); // this is actually the SDL event.key.keysym.sym; uint32_t native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger()); // this is actually the SDL event.key.keysym.sym;
uint32_t native_virtual_key_win = (uint32_t)(native_key_data["virtual_key_win"].asInteger()); uint32_t native_virtual_key_win = (uint32_t)(native_key_data["virtual_key_win"].asInteger());
@ -1127,16 +1126,6 @@ void MediaPluginCEF::keyEvent(dullahan::EKeyEvent key_event, LLSD native_key_dat
mCEFLib->nativeKeyboardEventSDL2(key_event, native_virtual_key_win, native_modifiers, keypad); mCEFLib->nativeKeyboardEventSDL2(key_event, native_virtual_key_win, native_modifiers, keypad);
} }
#else
uint32_t native_scan_code = (uint32_t)(native_key_data["sdl_sym"].asInteger());
uint32_t native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger());
uint32_t native_modifiers = (uint32_t)(native_key_data["cef_modifiers"].asInteger());
if( native_scan_code == '\n' )
native_scan_code = '\r';
mCEFLib->nativeKeyboardEvent(key_event, native_scan_code, native_virtual_key, native_modifiers);
#endif // LL_SDL2
#endif // LL_LINUX #endif // LL_LINUX
// </FS:ND> // </FS:ND>
}; };
@ -1171,7 +1160,6 @@ void MediaPluginCEF::unicodeInput(std::string event, LLSD native_key_data = LLSD
#endif #endif
#if LL_LINUX #if LL_LINUX
# if LL_SDL2
uint32_t native_scan_code = (uint32_t)(native_key_data["sdl_sym"].asInteger()); uint32_t native_scan_code = (uint32_t)(native_key_data["sdl_sym"].asInteger());
uint32_t native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger()); uint32_t native_virtual_key = (uint32_t)(native_key_data["virtual_key"].asInteger());
@ -1179,7 +1167,6 @@ void MediaPluginCEF::unicodeInput(std::string event, LLSD native_key_data = LLSD
mCEFLib->nativeKeyboardEvent(dullahan::KE_KEY_DOWN, native_scan_code, native_virtual_key, native_modifiers); mCEFLib->nativeKeyboardEvent(dullahan::KE_KEY_DOWN, native_scan_code, native_virtual_key, native_modifiers);
#endif // LL_SDL2
#endif // LL_LINUX #endif // LL_LINUX
}; };

View File

@ -133,6 +133,7 @@ set(viewer_SOURCE_FILES
fsfloaterprotectedfolders.cpp fsfloaterprotectedfolders.cpp
fsfloaterradar.cpp fsfloaterradar.cpp
fsfloatersearch.cpp fsfloatersearch.cpp
fsfloatersplashscreensettings.cpp
fsfloaterstatistics.cpp fsfloaterstatistics.cpp
fsfloaterstreamtitle.cpp fsfloaterstreamtitle.cpp
fsfloaterteleporthistory.cpp fsfloaterteleporthistory.cpp
@ -974,6 +975,7 @@ set(viewer_HEADER_FILES
fsfloaterprotectedfolders.h fsfloaterprotectedfolders.h
fsfloaterradar.h fsfloaterradar.h
fsfloatersearch.h fsfloatersearch.h
fsfloatersplashscreensettings.h
fsfloaterstatistics.h fsfloaterstatistics.h
fsfloaterstreamtitle.h fsfloaterstreamtitle.h
fsfloaterteleporthistory.h fsfloaterteleporthistory.h
@ -2514,6 +2516,7 @@ elseif (DARWIN)
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP" XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${CMAKE_CURRENT_BINARY_DIR}/llphysicsextensions/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensions -Xlinker -map -Xlinker ${CMAKE_CURRENT_BINARY_DIR}/${VIEWER_BINARY_NAME}.MAP"
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_BINARY_DIR}/llphysicsextensionsos/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsos" XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_BINARY_DIR}/llphysicsextensionsos/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsos"
) )
add_dependencies(${VIEWER_BINARY_NAME} llphysicsextensionsos)
elseif(HAVOK_TPV) elseif(HAVOK_TPV)
set_target_properties(${VIEWER_BINARY_NAME} set_target_properties(${VIEWER_BINARY_NAME}
PROPERTIES PROPERTIES
@ -2524,6 +2527,7 @@ elseif (DARWIN)
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${ARCH_PREBUILT_DIRS}/ -lllphysicsextensions_tpv" XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=x86_64] "$(inherited) -L${ARCH_PREBUILT_DIRS}/ -lllphysicsextensions_tpv"
XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_BINARY_DIR}/llphysicsextensionsos/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsos" XCODE_ATTRIBUTE_OTHER_LDFLAGS[arch=arm64] "$(inherited) -L${CMAKE_BINARY_DIR}/llphysicsextensionsos/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,${CMAKE_CFG_INTDIR}>/ -lllphysicsextensionsos"
) )
add_dependencies(${VIEWER_BINARY_NAME} llphysicsextensionsos)
else() else()
target_link_libraries(${VIEWER_BINARY_NAME} llphysicsextensionsos) target_link_libraries(${VIEWER_BINARY_NAME} llphysicsextensionsos)
endif() endif()

View File

@ -152,11 +152,12 @@ void FloaterAO::updateList()
mCurrentBoldItem = nullptr; mCurrentBoldItem = nullptr;
reloading(false); reloading(false);
static std::string no_sets_label = getString("ao_no_sets_loaded");
if (mSetList.empty()) if (mSetList.empty())
{ {
LL_DEBUGS("AOEngine") << "empty set list" << LL_ENDL; LL_DEBUGS("AOEngine") << "empty set list" << LL_ENDL;
mSetSelector->add(getString("ao_no_sets_loaded")); mSetSelector->add(no_sets_label);
mSetSelectorSmall->add(getString("ao_no_sets_loaded")); mSetSelectorSmall->add(no_sets_label);
mSetSelector->selectNthItem(0); mSetSelector->selectNthItem(0);
mSetSelectorSmall->selectNthItem(0); mSetSelectorSmall->selectNthItem(0);
enableSetControls(false); enableSetControls(false);
@ -164,13 +165,13 @@ void FloaterAO::updateList()
} }
// make sure we have an animation set name to display // make sure we have an animation set name to display
if (currentSetName.empty()) if (currentSetName.empty() || currentSetName == no_sets_label)
{ {
// selected animation set was empty, get the currently active animation set from the engine // selected animation set was empty, get the currently active animation set from the engine
currentSetName = AOEngine::instance().getCurrentSetName(); currentSetName = AOEngine::instance().getCurrentSetName();
LL_DEBUGS("AOEngine") << "Current set name was empty, fetched name \"" << currentSetName << "\" from AOEngine" << LL_ENDL; LL_DEBUGS("AOEngine") << "Current set name was empty, fetched name \"" << currentSetName << "\" from AOEngine" << LL_ENDL;
if(currentSetName.empty()) if (currentSetName.empty())
{ {
// selected animation set was empty, get the name of the first animation set in the list // selected animation set was empty, get the name of the first animation set in the list
currentSetName = mSetList[0]->getName(); currentSetName = mSetList[0]->getName();
@ -374,7 +375,7 @@ void FloaterAO::onSelectSet()
// only update the interface when we actually selected a different set - FIRE-29542 // only update the interface when we actually selected a different set - FIRE-29542
if (mSelectedSet != set) if (mSelectedSet != set)
{ {
mSelectedSet=set; mSelectedSet = set;
updateSetParameters(); updateSetParameters();
updateAnimationList(); updateAnimationList();

View File

@ -40,10 +40,11 @@
#include "llnotificationsutil.h" #include "llnotificationsutil.h"
#include "llstring.h" #include "llstring.h"
#include "llviewercontrol.h" #include "llviewercontrol.h"
#include "llxorcipher.h"
#define ROOT_AO_FOLDER "#AO" #define ROOT_AO_FOLDER "#AO"
#include <boost/graph/graph_concepts.hpp>
static const LLUUID ENCRYPTION_MAGIC_ID("4b552ff5-fd63-408c-8288-cd09429852ba");
constexpr F32 INVENTORY_POLLING_INTERVAL = 5.0f; constexpr F32 INVENTORY_POLLING_INTERVAL = 5.0f;
AOEngine::AOEngine() : AOEngine::AOEngine() :
@ -68,6 +69,31 @@ AOEngine::AOEngine() :
AOEngine::~AOEngine() AOEngine::~AOEngine()
{ {
LLSD currentState = LLSD::emptyMap();
if (mCurrentSet)
{
currentState = LLSD().with("CurrentSet", mCurrentSet->getName());
LLSD currentAnimations;
for (S32 index = 0; index < AOSet::AOSTATES_MAX; ++index)
{
if (auto state = mCurrentSet->getState(index); state && !state->mAnimations.empty())
{
LL_DEBUGS("AOEngine") << "Storing current animation for state " << index << ": Animation index " << state->mCurrentAnimation << LL_ENDL;
LLUUID shadow_id{ state->mAnimations[state->mCurrentAnimation].mAssetUUID };
LLXORCipher cipher(ENCRYPTION_MAGIC_ID.mData, UUID_BYTES);
cipher.encrypt(shadow_id.mData, UUID_BYTES);
currentAnimations.insert(state->mName, shadow_id);
}
else
{
LL_DEBUGS("AOEngine") << "No state " << index << " or no animations defined for this state" << LL_ENDL;
}
}
currentState.insert("CurrentStateAnimations", currentAnimations);
}
LL_DEBUGS("AOEngine") << "Stored AO state: " << currentState << LL_ENDL;
gSavedPerAccountSettings.setLLSD("FSCurrentAOState", currentState);
clear(false); clear(false);
if (mRegionChangeConnection.connected()) if (mRegionChangeConnection.connected())
@ -458,11 +484,9 @@ void AOEngine::enable(bool enable)
// stop all overriders, catch leftovers // stop all overriders, catch leftovers
for (S32 index = 0; index < AOSet::AOSTATES_MAX; ++index) for (S32 index = 0; index < AOSet::AOSTATES_MAX; ++index)
{ {
state = mCurrentSet->getState(index); if (auto state = mCurrentSet->getState(index))
if (state)
{ {
LLUUID animation = state->mCurrentAnimationID; if (LLUUID animation = state->mCurrentAnimationID; animation.notNull())
if (animation.notNull())
{ {
LL_DEBUGS("AOEngine") << "Stopping leftover animation from state " << state->mName << LL_ENDL; LL_DEBUGS("AOEngine") << "Stopping leftover animation from state " << state->mName << LL_ENDL;
gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP); gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
@ -488,7 +512,7 @@ void AOEngine::enable(bool enable)
void AOEngine::setStateCycleTimer(const AOSet::AOState* state) void AOEngine::setStateCycleTimer(const AOSet::AOState* state)
{ {
F32 timeout = (F32)state->mCycleTime; F32 timeout = state->mCycleTime;
LL_DEBUGS("AOEngine") << "Setting cycle timeout for state " << state->mName << " of " << timeout << LL_ENDL; LL_DEBUGS("AOEngine") << "Setting cycle timeout for state " << state->mName << " of " << timeout << LL_ENDL;
if (timeout > 0.0f) if (timeout > 0.0f)
{ {
@ -583,7 +607,7 @@ const LLUUID AOEngine::override(const LLUUID& motion, bool start)
// case, as it plays at the same time as other motions // case, as it plays at the same time as other motions
if (motion != ANIM_AGENT_TYPE) if (motion != ANIM_AGENT_TYPE)
{ {
constexpr S32 cleanupStates[]= constexpr S32 cleanupStates[] =
{ {
AOSet::Standing, AOSet::Standing,
AOSet::Walking, AOSet::Walking,
@ -615,8 +639,7 @@ const LLUUID AOEngine::override(const LLUUID& motion, bool start)
while ((stateNum = cleanupStates[index]) != AOSet::AOSTATES_MAX) while ((stateNum = cleanupStates[index]) != AOSet::AOSTATES_MAX)
{ {
// check if the next state is the one we are currently animating and skip that // check if the next state is the one we are currently animating and skip that
AOSet::AOState* stateToCheck = mCurrentSet->getState(stateNum); if (AOSet::AOState* stateToCheck = mCurrentSet->getState(stateNum); stateToCheck != state)
if (stateToCheck != state)
{ {
// check if there is an animation left over for that state // check if there is an animation left over for that state
if (!stateToCheck->mCurrentAnimationID.isNull()) if (!stateToCheck->mCurrentAnimationID.isNull())
@ -803,12 +826,12 @@ const LLUUID AOEngine::override(const LLUUID& motion, bool start)
void AOEngine::checkSitCancel() void AOEngine::checkSitCancel()
{ {
if (foreignAnimations()) if (!foreignAnimations())
return;
if (AOSet::AOState* aoState = mCurrentSet->getStateByRemapID(ANIM_AGENT_SIT))
{ {
if (AOSet::AOState* aoState = mCurrentSet->getStateByRemapID(ANIM_AGENT_SIT); aoState) if (LLUUID animation = aoState->mCurrentAnimationID; animation.notNull())
{
LLUUID animation = aoState->mCurrentAnimationID;
if (animation.notNull())
{ {
LL_DEBUGS("AOEngine") << "Stopping sit animation due to foreign animations running" << LL_ENDL; LL_DEBUGS("AOEngine") << "Stopping sit animation due to foreign animations running" << LL_ENDL;
gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP); gAgent.sendAnimationRequest(animation, ANIM_REQUEST_STOP);
@ -820,7 +843,6 @@ void AOEngine::checkSitCancel()
mCurrentSet->stopTimer(); mCurrentSet->stopTimer();
} }
} }
}
} }
void AOEngine::cycleTimeout(const AOSet* set) void AOEngine::cycleTimeout(const AOSet* set)
@ -918,7 +940,7 @@ void AOEngine::cycle(eCycleMode cycleMode)
{ {
LL_DEBUGS("AOEngine") << "Asset UUID for cycled animation " << anim.mName << " not yet known, try to find it." << LL_ENDL; LL_DEBUGS("AOEngine") << "Asset UUID for cycled animation " << anim.mName << " not yet known, try to find it." << LL_ENDL;
if(LLViewerInventoryItem* item = gInventory.getItem(anim.mOriginalUUID) ; item) if (LLViewerInventoryItem* item = gInventory.getItem(anim.mOriginalUUID); item)
{ {
LL_DEBUGS("AOEngine") << "Found asset UUID for cycled animation: " << item->getAssetUUID() << " - Updating AOAnimation.mAssetUUID" << LL_ENDL; LL_DEBUGS("AOEngine") << "Found asset UUID for cycled animation: " << item->getAssetUUID() << " - Updating AOAnimation.mAssetUUID" << LL_ENDL;
anim.mAssetUUID = item->getAssetUUID(); anim.mAssetUUID = item->getAssetUUID();
@ -992,14 +1014,13 @@ void AOEngine::playAnimation(const LLUUID& animation)
return; return;
} }
if (!state->mAnimations.size()) if (state->mAnimations.empty())
{ {
LL_DEBUGS("AOEngine") << "cycle without animations in state." << LL_ENDL; LL_DEBUGS("AOEngine") << "cycle without animations in state." << LL_ENDL;
return; return;
} }
LLViewerInventoryItem* item = gInventory.getItem(animation); LLViewerInventoryItem* item = gInventory.getItem(animation);
if (!item) if (!item)
{ {
LL_WARNS("AOEngine") << "Inventory item for animation " << animation << " not found." << LL_ENDL; LL_WARNS("AOEngine") << "Inventory item for animation " << animation << " not found." << LL_ENDL;
@ -1007,10 +1028,10 @@ void AOEngine::playAnimation(const LLUUID& animation)
} }
AOSet::AOAnimation anim; AOSet::AOAnimation anim;
anim.mName = item->LLInventoryItem::getName(); anim.mName = item->getName();
anim.mInventoryUUID = item->getUUID(); anim.mInventoryUUID = item->getUUID();
anim.mOriginalUUID = item->getLinkedUUID(); anim.mOriginalUUID = item->getLinkedUUID();
anim.mAssetUUID = LLUUID::null; anim.mAssetUUID.setNull();
// if we can find the original animation already right here, save its asset ID, otherwise this will // if we can find the original animation already right here, save its asset ID, otherwise this will
// be tried again in AOSet::getAnimationForState() and/or AOEngine::cycle() // be tried again in AOSet::getAnimationForState() and/or AOEngine::cycle()
@ -1111,8 +1132,8 @@ void AOEngine::updateSortOrder(AOSet::AOState* state)
LL_WARNS("AOEngine") << "NULL inventory item found while trying to copy " << state->mAnimations[index].mInventoryUUID << LL_ENDL; LL_WARNS("AOEngine") << "NULL inventory item found while trying to copy " << state->mAnimations[index].mInventoryUUID << LL_ENDL;
continue; continue;
} }
LLPointer<LLViewerInventoryItem> newItem = new LLViewerInventoryItem(item);
LLPointer<LLViewerInventoryItem> newItem = new LLViewerInventoryItem(item);
newItem->setDescription(numStr.str()); newItem->setDescription(numStr.str());
newItem->setComplete(true); newItem->setComplete(true);
newItem->updateServer(false); newItem->updateServer(false);
@ -1181,7 +1202,7 @@ void AOEngine::addAnimation(const AOSet* set, AOSet::AOState* state, const LLInv
bool success = createAnimationLink(state, item); bool success = createAnimationLink(state, item);
gSavedPerAccountSettings.setBOOL("LockAOFolders", wasProtected); gSavedPerAccountSettings.setBOOL("LockAOFolders", wasProtected);
if(success) if (success)
{ {
if (reload) if (reload)
{ {
@ -1197,7 +1218,7 @@ void AOEngine::addAnimation(const AOSet* set, AOSet::AOState* state, const LLInv
state->mAddQueue.push_back(item); state->mAddQueue.push_back(item);
// if this is the first queued animation for this state, create the folder asyncronously // if this is the first queued animation for this state, create the folder asyncronously
if(state->mAddQueue.size() == 1) if (state->mAddQueue.size() == 1)
{ {
gInventory.createNewCategory(set->getInventoryUUID(), LLFolderType::FT_NONE, state->mName, [this, state, reload, wasProtected](const LLUUID &new_cat_id) gInventory.createNewCategory(set->getInventoryUUID(), LLFolderType::FT_NONE, state->mName, [this, state, reload, wasProtected](const LLUUID &new_cat_id)
{ {
@ -1440,7 +1461,7 @@ bool AOEngine::swapWithNext(AOSet::AOState* state, S32 index)
return true; return true;
} }
void AOEngine::reloadStateAnimations(AOSet::AOState* state) void AOEngine::reloadStateAnimations(AOSet* set, AOSet::AOState* state)
{ {
LLInventoryModel::item_array_t* items; LLInventoryModel::item_array_t* items;
LLInventoryModel::cat_array_t* dummy; LLInventoryModel::cat_array_t* dummy;
@ -1458,11 +1479,10 @@ void AOEngine::reloadStateAnimations(AOSet::AOState* state)
<< " asset " << item->getAssetUUID() << LL_ENDL; << " asset " << item->getAssetUUID() << LL_ENDL;
AOSet::AOAnimation anim; AOSet::AOAnimation anim;
anim.mName = item->LLInventoryItem::getName(); anim.mName = item->getName();
anim.mInventoryUUID = item->getUUID(); anim.mInventoryUUID = item->getUUID();
anim.mOriginalUUID = item->getLinkedUUID(); anim.mOriginalUUID = item->getLinkedUUID();
anim.mAssetUUID.setNull();
anim.mAssetUUID = LLUUID::null;
// if we can find the original animation already right here, save its asset ID, otherwise this will // if we can find the original animation already right here, save its asset ID, otherwise this will
// be tried again in AOSet::getAnimationForState() and/or AOEngine::cycle() // be tried again in AOSet::getAnimationForState() and/or AOEngine::cycle()
@ -1509,6 +1529,24 @@ void AOEngine::reloadStateAnimations(AOSet::AOState* state)
} }
updateSortOrder(state); updateSortOrder(state);
if (auto currentState = gSavedPerAccountSettings.getLLSD("FSCurrentAOState");
currentState.has("CurrentSet") && currentState["CurrentSet"].asString() == set->getName() &&
currentState.has("CurrentStateAnimations") && currentState["CurrentStateAnimations"].has(state->mName))
{
auto currentStateAnimId{ currentState["CurrentStateAnimations"][state->mName].asUUID() };
LLXORCipher cipher(ENCRYPTION_MAGIC_ID.mData, UUID_BYTES);
cipher.decrypt(currentStateAnimId.mData, UUID_BYTES);
for (const auto& animation : state->mAnimations)
{
if (animation.mAssetUUID == currentStateAnimId)
{
state->mCurrentAnimation = animation.mSortOrder;
break;
}
}
}
} }
void AOEngine::update() void AOEngine::update()
@ -1559,19 +1597,27 @@ void AOEngine::update()
continue; continue;
} }
AOSet* newSet = getSetByName(params[0]); auto setName{ params[0] };
AOSet* newSet = getSetByName(setName);
if (!newSet) if (!newSet)
{ {
LL_DEBUGS("AOEngine") << "Adding set " << setFolderName << " to AO." << LL_ENDL; LL_DEBUGS("AOEngine") << "Adding set " << setFolderName << " to AO." << LL_ENDL;
newSet = new AOSet(currentCategory->getUUID()); newSet = new AOSet(currentCategory->getUUID());
newSet->setName(params[0]); newSet->setName(setName);
mSets.emplace_back(newSet); mSets.emplace_back(newSet);
if (auto currentState = gSavedPerAccountSettings.getLLSD("FSCurrentAOState");
currentState.has("CurrentSet") && currentState["CurrentSet"].asString() == setName)
{
LL_DEBUGS("AOEngine") << "Selecting current set from settings: " << setName << LL_ENDL;
mCurrentSet = newSet;
}
} }
else else
{ {
if (newSet->getComplete()) if (newSet->getComplete())
{ {
LL_DEBUGS("AOEngine") << "Set " << params[0] << " already complete. Skipping." << LL_ENDL; LL_DEBUGS("AOEngine") << "Set " << setName << " already complete. Skipping." << LL_ENDL;
continue; continue;
} }
LL_DEBUGS("AOEngine") << "Updating set " << setFolderName << " in AO." << LL_ENDL; LL_DEBUGS("AOEngine") << "Updating set " << setFolderName << " in AO." << LL_ENDL;
@ -1599,8 +1645,12 @@ void AOEngine::update()
else if (params[num] == "**") else if (params[num] == "**")
{ {
mDefaultSet = newSet; mDefaultSet = newSet;
if (!mCurrentSet)
{
LL_DEBUGS("AOEngine") << "No set selected as current yet - setting default set as current: " << setName << LL_ENDL;
mCurrentSet = newSet; mCurrentSet = newSet;
} }
}
else else
{ {
LL_WARNS("AOEngine") << "Unknown AO set option " << params[num] << LL_ENDL; LL_WARNS("AOEngine") << "Unknown AO set option " << params[num] << LL_ENDL;
@ -1666,12 +1716,12 @@ void AOEngine::update()
newSet->setComplete(false); newSet->setComplete(false);
continue; continue;
} }
reloadStateAnimations(state); reloadStateAnimations(newSet, state);
} }
} }
else else
{ {
LL_DEBUGS("AOEngine") << "Set " << params[0] << " is incomplete, fetching descendents" << LL_ENDL; LL_DEBUGS("AOEngine") << "Set " << setName << " is incomplete, fetching descendents" << LL_ENDL;
gInventory.fetchDescendentsOf(currentCategory->getUUID()); gInventory.fetchDescendentsOf(currentCategory->getUUID());
} }
} }
@ -1683,7 +1733,7 @@ void AOEngine::update()
if (!mCurrentSet && !mSets.empty()) if (!mCurrentSet && !mSets.empty())
{ {
LL_DEBUGS("AOEngine") << "No default set defined, choosing the first in the list." << LL_ENDL; LL_DEBUGS("AOEngine") << "No set currently selected, choosing the first in the list." << LL_ENDL;
selectSet(mSets[0]); selectSet(mSets[0]);
} }
@ -1723,16 +1773,15 @@ void AOEngine::reload(bool aFromTimer)
AOSet* AOEngine::getSetByName(const std::string& name) const AOSet* AOEngine::getSetByName(const std::string& name) const
{ {
AOSet* found = nullptr;
for (auto set : mSets) for (auto set : mSets)
{ {
if (set->getName().compare(name) == 0) if (set->getName() == name)
{ {
found = set; return set;
break;
} }
} }
return found;
return nullptr;
} }
const std::string AOEngine::getCurrentSetName() const const std::string AOEngine::getCurrentSetName() const
@ -1753,8 +1802,7 @@ void AOEngine::selectSet(AOSet* set)
{ {
if (mEnabled && mCurrentSet) if (mEnabled && mCurrentSet)
{ {
AOSet::AOState* state = mCurrentSet->getStateByRemapID(mLastOverriddenMotion); if (AOSet::AOState* state = mCurrentSet->getStateByRemapID(mLastOverriddenMotion))
if (state)
{ {
gAgent.sendAnimationRequest(state->mCurrentAnimationID, ANIM_REQUEST_STOP); gAgent.sendAnimationRequest(state->mCurrentAnimationID, ANIM_REQUEST_STOP);
state->mCurrentAnimationID.setNull(); state->mCurrentAnimationID.setNull();
@ -1773,8 +1821,7 @@ void AOEngine::selectSet(AOSet* set)
AOSet* AOEngine::selectSetByName(const std::string& name) AOSet* AOEngine::selectSetByName(const std::string& name)
{ {
AOSet* set = getSetByName(name); if (AOSet* set = getSetByName(name))
if (set)
{ {
selectSet(set); selectSet(set);
return set; return set;
@ -2176,8 +2223,7 @@ void AOEngine::onNotecardLoadComplete(const LLUUID& assetUUID, LLAssetType::ETyp
char* buffer = new char[notecardSize + 1]; char* buffer = new char[notecardSize + 1];
buffer[notecardSize] = 0; buffer[notecardSize] = 0;
bool ret = file.read((U8*)buffer, notecardSize); if (file.read((U8*)buffer, notecardSize))
if (ret)
{ {
AOEngine::instance().parseNotecard(buffer); AOEngine::instance().parseNotecard(buffer);
} }
@ -2478,6 +2524,11 @@ void AOEngine::onRegionChange()
gAgent.sendAnimationRequest(mLastMotion, ANIM_REQUEST_START); gAgent.sendAnimationRequest(mLastMotion, ANIM_REQUEST_START);
} }
void AOEngine::updatePersistedStateAnimations()
{
}
// ---------------------------------------------------- // ----------------------------------------------------
AOSitCancelTimer::AOSitCancelTimer() AOSitCancelTimer::AOSitCancelTimer()
@ -2487,10 +2538,6 @@ AOSitCancelTimer::AOSitCancelTimer()
mEventTimer.stop(); mEventTimer.stop();
} }
AOSitCancelTimer::~AOSitCancelTimer()
{
}
void AOSitCancelTimer::oneShot() void AOSitCancelTimer::oneShot()
{ {
mTickCount = 0; mTickCount = 0;
@ -2525,10 +2572,6 @@ AOTimerCollection::AOTimerCollection()
updateTimers(); updateTimers();
} }
AOTimerCollection::~AOTimerCollection()
{
}
bool AOTimerCollection::tick() bool AOTimerCollection::tick()
{ {
if (mInventoryTimer) if (mInventoryTimer)

View File

@ -39,7 +39,7 @@ class AOTimerCollection
{ {
public: public:
AOTimerCollection(); AOTimerCollection();
~AOTimerCollection(); ~AOTimerCollection() = default;
virtual bool tick(); virtual bool tick();
@ -64,7 +64,7 @@ class AOSitCancelTimer
{ {
public: public:
AOSitCancelTimer(); AOSitCancelTimer();
~AOSitCancelTimer(); ~AOSitCancelTimer() = default;
void oneShot(); void oneShot();
void stop(); void stop();
@ -101,7 +101,7 @@ class AOEngine
void tick(); void tick();
void update(); void update();
void reload(bool); void reload(bool);
void reloadStateAnimations(AOSet::AOState* state); void reloadStateAnimations(AOSet* set, AOSet::AOState* state);
void clear(bool from_timer); void clear(bool from_timer);
const LLUUID& getAOFolder() const; const LLUUID& getAOFolder() const;
@ -196,6 +196,8 @@ class AOEngine
void* userdata, S32 status, LLExtStat extStatus); void* userdata, S32 status, LLExtStat extStatus);
void parseNotecard(const char* buffer); void parseNotecard(const char* buffer);
void updatePersistedStateAnimations();
updated_signal_t mUpdatedSignal; updated_signal_t mUpdatedSignal;
animation_changed_signal_t mAnimationChangedSignal; animation_changed_signal_t mAnimationChangedSignal;

View File

@ -171,8 +171,7 @@ const LLUUID& AOSet::getAnimationForState(AOState* state) const
{ {
if (state) if (state)
{ {
auto numOfAnimations = state->mAnimations.size(); if (auto numOfAnimations = state->mAnimations.size(); numOfAnimations > 0)
if (numOfAnimations)
{ {
if (state->mCycle) if (state->mCycle)
{ {
@ -198,7 +197,7 @@ const LLUUID& AOSet::getAnimationForState(AOState* state) const
{ {
LL_DEBUGS("AOEngine") << "Asset UUID for chosen animation " << anim.mName << " not yet known, try to find it." << LL_ENDL; LL_DEBUGS("AOEngine") << "Asset UUID for chosen animation " << anim.mName << " not yet known, try to find it." << LL_ENDL;
if(LLViewerInventoryItem* item = gInventory.getItem(anim.mInventoryUUID) ; item) if (LLViewerInventoryItem* item = gInventory.getItem(anim.mInventoryUUID))
{ {
LL_DEBUGS("AOEngine") << "Found asset UUID for chosen animation: " << item->getAssetUUID() << " - Updating AOAnimation.mAssetUUID" << LL_ENDL; LL_DEBUGS("AOEngine") << "Found asset UUID for chosen animation: " << item->getAssetUUID() << " - Updating AOAnimation.mAssetUUID" << LL_ENDL;
anim.mAssetUUID = item->getAssetUUID(); anim.mAssetUUID = item->getAssetUUID();
@ -256,7 +255,7 @@ const std::string& AOSet::getName() const
void AOSet::setName(const std::string& name) void AOSet::setName(const std::string& name)
{ {
mName=name; mName = name;
} }
bool AOSet::getSitOverride() const bool AOSet::getSitOverride() const

View File

@ -8177,6 +8177,17 @@
<key>Value</key> <key>Value</key>
<integer>0</integer> <integer>0</integer>
</map> </map>
<key>FSPoserShowBoneHighlights</key>
<map>
<key>Comment</key>
<string>Whether to highlight a bone with the debug beacon on selection from the UI.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>FSPoserStopPosingWhenClosed</key> <key>FSPoserStopPosingWhenClosed</key>
<map> <map>
<key>Comment</key> <key>Comment</key>
@ -25399,6 +25410,94 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>HideFromEditor</key> <key>HideFromEditor</key>
<integer>1</integer> <integer>1</integer>
</map> </map>
<key>FSSplashScreenHideTopBar</key>
<map>
<key>Comment</key>
<string>Hide the top bar section on the splash screen</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSSplashScreenHideBlogs</key>
<map>
<key>Comment</key>
<string>Hide the blogs section on the splash screen</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSSplashScreenHideDestinations</key>
<map>
<key>Comment</key>
<string>Hide the destinations section on the splash screen</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSSplashScreenUseGrayMode</key>
<map>
<key>Comment</key>
<string>Enable grayscale mode on the splash screen</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSSplashScreenUseHighContrast</key>
<map>
<key>Comment</key>
<string>Enable high contrast mode on the splash screen</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSSplashScreenUseAllCaps</key>
<map>
<key>Comment</key>
<string>Enable all caps mode on the splash screen</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSSplashScreenUseLargerFonts</key>
<map>
<key>Comment</key>
<string>Use larger fonts on the splash screen</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSSplashScreenNoTransparency</key>
<map>
<key>Comment</key>
<string>Disable transparency effects on the splash screen</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>FSllOwnerSayToScriptDebugWindowRouting</key> <key>FSllOwnerSayToScriptDebugWindowRouting</key>
<map> <map>
<key>Comment</key> <key>Comment</key>

View File

@ -365,6 +365,19 @@
<key>Value</key> <key>Value</key>
<integer>1</integer> <integer>1</integer>
</map> </map>
<key>GlobalOnlineStatusCurrentlyReverting</key>
<map>
<key>Comment</key>
<string>Flag to prevent infinite loop when reverting the setting on cancel</string>
<key>HideFromEditor</key>
<integer>1</integer>
<key>Persist</key>
<integer>0</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>0</integer>
</map>
<key>InstantMessageLogPath</key> <key>InstantMessageLogPath</key>
<map> <map>
<key>Comment</key> <key>Comment</key>
@ -1504,5 +1517,16 @@
<key>Value</key> <key>Value</key>
<integer>1</integer> <integer>1</integer>
</map> </map>
<key>FSCurrentAOState</key>
<map>
<key>Comment</key>
<string>The currently active AO set and animation per state</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>LLSD</string>
<key>Value</key>
<map/>
</map>
</map> </map>
</llsd> </llsd>

View File

@ -128,6 +128,8 @@ LLObjectIMHandler gObjectIMHandler;
class FSChatHistoryHeader: public LLPanel class FSChatHistoryHeader: public LLPanel
{ {
public: public:
typedef boost::function<void (const std::string& speaker_id)> insert_mention_callback_t;
FSChatHistoryHeader() FSChatHistoryHeader()
: LLPanel(), : LLPanel(),
mInfoCtrl(NULL), mInfoCtrl(NULL),
@ -150,6 +152,7 @@ public:
mTimeBoxTextBox(NULL), mTimeBoxTextBox(NULL),
mHeaderLayoutStack(NULL), mHeaderLayoutStack(NULL),
mAvatarNameCacheConnection(), mAvatarNameCacheConnection(),
mInsertMentionCallback(NULL),
mTime(0) mTime(0)
{} {}
@ -180,6 +183,24 @@ public:
} }
} }
void setInsertMentionCallback(insert_mention_callback_t cb)
{
mInsertMentionCallback = cb;
}
void copyURLToClipboard()
{
LLUrlAction::copyURLToClipboard("secondlife:///app/agent/" + mAvatarID.asString() + "/mention");
}
void insertMentionAtCursor()
{
if (mInsertMentionCallback)
{
mInsertMentionCallback("secondlife:///app/agent/" + mAvatarID.asString() + "/mention");
}
}
bool handleMouseUp(S32 x, S32 y, MASK mask) bool handleMouseUp(S32 x, S32 y, MASK mask)
{ {
return LLPanel::handleMouseUp(x,y,mask); return LLPanel::handleMouseUp(x,y,mask);
@ -1070,6 +1091,9 @@ protected:
registrar_enable.add("AvatarIcon.Enable", boost::bind(&FSChatHistoryHeader::onAvatarIconContextMenuItemEnabled, this, _2)); registrar_enable.add("AvatarIcon.Enable", boost::bind(&FSChatHistoryHeader::onAvatarIconContextMenuItemEnabled, this, _2));
registrar_enable.add("AvatarIcon.Visible", boost::bind(&FSChatHistoryHeader::onAvatarIconContextMenuItemVisible, this, _2)); registrar_enable.add("AvatarIcon.Visible", boost::bind(&FSChatHistoryHeader::onAvatarIconContextMenuItemVisible, this, _2));
registrar.add("Mention.CopyURI", boost::bind(&FSChatHistoryHeader::copyURLToClipboard, this));
registrar.add("Mention.Chat", boost::bind(&FSChatHistoryHeader::insertMentionAtCursor, this));
menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_avatar_icon.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_avatar_icon.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
if (menu) if (menu)
{ {
@ -1257,6 +1281,8 @@ protected:
private: private:
boost::signals2::connection mAvatarNameCacheConnection; boost::signals2::connection mAvatarNameCacheConnection;
insert_mention_callback_t mInsertMentionCallback;
}; };
FSChatHistory::FSChatHistory(const FSChatHistory::Params& p) FSChatHistory::FSChatHistory(const FSChatHistory::Params& p)
@ -1359,6 +1385,17 @@ void FSChatHistory::initFromParams(const FSChatHistory::Params& p)
setShowContextMenu(true); setShowContextMenu(true);
} }
// virtual
void FSChatHistory::insertMentionAtCursor(const std::string& str)
{
updateChatInputLine();
if (mChatInputLine)
{
mChatInputLine->insertMentionAtCursor(str);
mChatInputLine->setFocus(true);
}
}
LLView* FSChatHistory::getSeparator() LLView* FSChatHistory::getSeparator()
{ {
LLPanel* separator = LLUICtrlFactory::getInstance()->createFromFile<LLPanel>(mMessageSeparatorFilename, NULL, LLPanel::child_registry_t::instance()); LLPanel* separator = LLUICtrlFactory::getInstance()->createFromFile<LLPanel>(mMessageSeparatorFilename, NULL, LLPanel::child_registry_t::instance());
@ -1371,6 +1408,7 @@ LLView* FSChatHistory::getHeader(const LLChat& chat,const LLStyle::Params& style
if (header) if (header)
{ {
header->setup(chat, style_params, args); header->setup(chat, style_params, args);
header->setInsertMentionCallback(boost::bind(&FSChatHistory::insertMentionAtCursor, this, _1));
} }
return header; return header;
} }

View File

@ -110,6 +110,8 @@ class FSChatHistory : public LLTextEditor // <FS:Zi> FIRE-8600: TAB out of cha
LLSD getValue() const; LLSD getValue() const;
void initFromParams(const Params&); void initFromParams(const Params&);
virtual void insertMentionAtCursor(const std::string& str);
/** /**
* Appends a widget message. * Appends a widget message.
* If last user appended message, concurs with current user, * If last user appended message, concurs with current user,

View File

@ -61,6 +61,7 @@ LLContextMenu* FSContactsFriendsMenu::createMenu()
registrar.add("Contacts.Friends.CopyLabel", boost::bind(&FSContactsFriendsMenu::copyNameToClipboard, this, id)); registrar.add("Contacts.Friends.CopyLabel", boost::bind(&FSContactsFriendsMenu::copyNameToClipboard, this, id));
registrar.add("Contacts.Friends.CopyUrl", boost::bind(&FSContactsFriendsMenu::copySLURLToClipboard, this, id)); registrar.add("Contacts.Friends.CopyUrl", boost::bind(&FSContactsFriendsMenu::copySLURLToClipboard, this, id));
registrar.add("Contacts.Friends.SelectOption", boost::bind(&FSContactsFriendsMenu::selectOption, this, _2)); registrar.add("Contacts.Friends.SelectOption", boost::bind(&FSContactsFriendsMenu::selectOption, this, _2));
registrar.add("Mention.CopyURI", boost::bind(&FSContactsFriendsMenu::copyURLToClipboard, this));
enable_registrar.add("Contacts.Friends.EnableItem", boost::bind(&FSContactsFriendsMenu::enableContextMenuItem, this, _2)); enable_registrar.add("Contacts.Friends.EnableItem", boost::bind(&FSContactsFriendsMenu::enableContextMenuItem, this, _2));
enable_registrar.add("Contacts.Friends.EnableZoomIn", boost::bind(&LLAvatarActions::canZoomIn, id)); enable_registrar.add("Contacts.Friends.EnableZoomIn", boost::bind(&LLAvatarActions::canZoomIn, id));
@ -237,3 +238,8 @@ bool FSContactsFriendsMenu::checkOption(const LLSD& userdata)
return false; return false;
} }
void FSContactsFriendsMenu::copyURLToClipboard()
{
LLUrlAction::copyURLToClipboard("secondlife:///app/agent/" + mUUIDs.front().asString() + "/mention");
}

View File

@ -44,6 +44,7 @@ private:
void copySLURLToClipboard(const LLUUID& id); void copySLURLToClipboard(const LLUUID& id);
void selectOption(const LLSD& userdata); void selectOption(const LLSD& userdata);
bool checkOption(const LLSD& userdata); bool checkOption(const LLSD& userdata);
void copyURLToClipboard();
}; };
extern FSContactsFriendsMenu gFSContactsFriendsMenu; extern FSContactsFriendsMenu gFSContactsFriendsMenu;

View File

@ -271,7 +271,7 @@ void FSFloaterContacts::onOpen(const LLSD& key)
LLFloater::onOpen(key); LLFloater::onOpen(key);
} }
void FSFloaterContacts::openTab(const std::string& name) void FSFloaterContacts::openTab(std::string_view name)
{ {
if (name == "friends") if (name == "friends")
{ {
@ -291,8 +291,7 @@ void FSFloaterContacts::openTab(const std::string& name)
return; return;
} }
FSFloaterIMContainer* floater_container = dynamic_cast<FSFloaterIMContainer*>(getHost()); if (auto floater_container = dynamic_cast<FSFloaterIMContainer*>(getHost()))
if (floater_container)
{ {
floater_container->setVisible(true); floater_container->setVisible(true);
floater_container->showFloater(this); floater_container->showFloater(this);
@ -391,7 +390,7 @@ void FSFloaterContacts::onAvatarPicked(const uuid_vec_t& ids, const std::vector<
{ {
if (!names.empty() && !ids.empty()) if (!names.empty() && !ids.empty())
{ {
LLAvatarActions::requestFriendshipDialog(ids[0], names[0].getCompleteName()); LLAvatarActions::requestFriendshipDialog(ids.front(), names.front().getCompleteName());
} }
} }
@ -405,8 +404,7 @@ void FSFloaterContacts::onAddFriendWizButtonClicked(LLUICtrl* ctrl)
picker->setOkBtnEnableCb(boost::bind(&FSFloaterContacts::isItemsFreeOfFriends, this, _1)); picker->setOkBtnEnableCb(boost::bind(&FSFloaterContacts::isItemsFreeOfFriends, this, _1));
} }
LLFloater* root_floater = gFloaterView->getParentFloater(this); if (auto root_floater = gFloaterView->getParentFloater(this))
if (root_floater)
{ {
root_floater->addDependentFloater(picker); root_floater->addDependentFloater(picker);
} }
@ -474,7 +472,7 @@ std::string FSFloaterContacts::getActiveTabName() const
return mTabContainer->getCurrentPanel()->getName(); return mTabContainer->getCurrentPanel()->getName();
} }
LLPanel* FSFloaterContacts::getPanelByName(const std::string& panel_name) LLPanel* FSFloaterContacts::getPanelByName(std::string_view panel_name)
{ {
return mTabContainer->getPanelByName(panel_name); return mTabContainer->getPanelByName(panel_name);
} }
@ -518,10 +516,9 @@ void FSFloaterContacts::getCurrentItemIDs(uuid_vec_t& selected_uuids) const
void FSFloaterContacts::getCurrentFriendItemIDs(uuid_vec_t& selected_uuids) const void FSFloaterContacts::getCurrentFriendItemIDs(uuid_vec_t& selected_uuids) const
{ {
listitem_vec_t selected = mFriendsList->getAllSelected(); for (auto list_item : mFriendsList->getAllSelected())
for (listitem_vec_t::iterator itr = selected.begin(); itr != selected.end(); ++itr)
{ {
selected_uuids.push_back((*itr)->getUUID()); selected_uuids.push_back(list_item->getUUID());
} }
} }
@ -786,7 +783,7 @@ void FSFloaterContacts::refreshRightsChangeList()
bool can_offer_teleport = num_selected >= 1; bool can_offer_teleport = num_selected >= 1;
bool selected_friends_online = true; bool selected_friends_online = true;
const LLRelationship* friend_status = NULL; const LLRelationship* friend_status = nullptr;
for (const auto& id : friends) for (const auto& id : friends)
{ {
friend_status = LLAvatarTracker::instance().getBuddyInfo(id); friend_status = LLAvatarTracker::instance().getBuddyInfo(id);
@ -1064,8 +1061,8 @@ void FSFloaterContacts::sendRightsGrant(rights_map_t& ids)
// setup message header // setup message header
msg->newMessageFast(_PREHASH_GrantUserRights); msg->newMessageFast(_PREHASH_GrantUserRights);
msg->nextBlockFast(_PREHASH_AgentData); msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUID(_PREHASH_AgentID, gAgent.getID()); msg->addUUID(_PREHASH_AgentID, gAgentID);
msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID()); msg->addUUID(_PREHASH_SessionID, gAgentSessionID);
for (const auto& [id, rights] : ids) for (const auto& [id, rights] : ids)
{ {
@ -1078,7 +1075,7 @@ void FSFloaterContacts::sendRightsGrant(rights_map_t& ids)
gAgent.sendReliableMessage(); gAgent.sendReliableMessage();
} }
void FSFloaterContacts::childShowTab(const std::string& id, const std::string& tabname) void FSFloaterContacts::childShowTab(std::string_view id, std::string_view tabname)
{ {
if (LLTabContainer* child = findChild<LLTabContainer>(id)) if (LLTabContainer* child = findChild<LLTabContainer>(id))
{ {

View File

@ -63,8 +63,8 @@ public:
static FSFloaterContacts* getInstance(); static FSFloaterContacts* getInstance();
static FSFloaterContacts* findInstance(); static FSFloaterContacts* findInstance();
void openTab(const std::string& name); void openTab(std::string_view name);
LLPanel* getPanelByName(const std::string& panel_name); LLPanel* getPanelByName(std::string_view panel_name);
void sortFriendList(); void sortFriendList();
void onDisplayNameChanged(); void onDisplayNameChanged();
@ -189,7 +189,7 @@ private:
std::string mFriendFilterSubString{ LLStringUtil::null }; std::string mFriendFilterSubString{ LLStringUtil::null };
std::string mFriendFilterSubStringOrig{ LLStringUtil::null }; std::string mFriendFilterSubStringOrig{ LLStringUtil::null };
void childShowTab(const std::string& id, const std::string& tabname); void childShowTab(std::string_view id, std::string_view tabname);
void updateRlvRestrictions(ERlvBehaviour behavior); void updateRlvRestrictions(ERlvBehaviour behavior);
boost::signals2::connection mRlvBehaviorCallbackConnection{}; boost::signals2::connection mRlvBehaviorCallbackConnection{};

View File

@ -46,6 +46,8 @@
#include "llinventoryfunctions.h" #include "llinventoryfunctions.h"
#include "lltoolcomp.h" #include "lltoolcomp.h"
#include "llloadingindicator.h" #include "llloadingindicator.h"
#include "llmutelist.h"
#include "llappviewer.h"
namespace namespace
{ {
@ -57,6 +59,7 @@ constexpr std::string_view POSE_PRESETS_HANDS_SUBDIRECTORY = "hand_presets";
constexpr char XML_LIST_HEADER_STRING_PREFIX[] = "header_"; constexpr char XML_LIST_HEADER_STRING_PREFIX[] = "header_";
constexpr char XML_LIST_TITLE_STRING_PREFIX[] = "title_"; constexpr char XML_LIST_TITLE_STRING_PREFIX[] = "title_";
constexpr char XML_JOINT_TRANSFORM_STRING_PREFIX[] = "joint_transform_"; constexpr char XML_JOINT_TRANSFORM_STRING_PREFIX[] = "joint_transform_";
constexpr char XML_JOINT_FRAME_TRANSFORM_PREFIX[] = "joint_frame_";
constexpr char XML_JOINT_DELTAROT_STRING_PREFIX[] = "joint_delta_rotate_"; constexpr char XML_JOINT_DELTAROT_STRING_PREFIX[] = "joint_delta_rotate_";
constexpr char BVH_JOINT_TRANSFORM_STRING_PREFIX[] = "bvh_joint_transform_"; 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_TRACKPAD_SENSITIVITY_SAVE_KEY = "FSPoserTrackpadSensitivity";
@ -64,6 +67,7 @@ constexpr std::string_view POSER_STOPPOSINGWHENCLOSED_SAVE_KEY = "FSPoserStopPos
constexpr std::string_view POSER_SAVEEXTERNALFORMAT_SAVE_KEY = "FSPoserSaveExternalFileAlso"; constexpr std::string_view POSER_SAVEEXTERNALFORMAT_SAVE_KEY = "FSPoserSaveExternalFileAlso";
constexpr std::string_view POSER_SAVECONFIRMREQUIRED_SAVE_KEY = "FSPoserOnSaveConfirmOverwrite"; constexpr std::string_view POSER_SAVECONFIRMREQUIRED_SAVE_KEY = "FSPoserOnSaveConfirmOverwrite";
constexpr std::string_view POSER_UNLOCKPELVISINBVH_SAVE_KEY = "FSPoserPelvisUnlockedForBvhSave"; constexpr std::string_view POSER_UNLOCKPELVISINBVH_SAVE_KEY = "FSPoserPelvisUnlockedForBvhSave";
constexpr std::string_view POSER_SHOWBONEHIGHLIGHTS_SAVE_KEY = "FSManipShowJointMarkers";
constexpr char ICON_SAVE_OK[] = "icon_rotation_is_own_work"; constexpr char ICON_SAVE_OK[] = "icon_rotation_is_own_work";
constexpr char ICON_SAVE_FAILED[] = "icon_save_failed_button"; constexpr char ICON_SAVE_FAILED[] = "icon_save_failed_button";
@ -82,6 +86,7 @@ FSFloaterPoser::FSFloaterPoser(const LLSD& key) : LLFloater(key)
mCommitCallbackRegistrar.add("Poser.StartStopAnimating", [this](LLUICtrl*, const LLSD&) { onPoseStartStop(); }); mCommitCallbackRegistrar.add("Poser.StartStopAnimating", [this](LLUICtrl*, const LLSD&) { onPoseStartStop(); });
mCommitCallbackRegistrar.add("Poser.ToggleLoadSavePanel", [this](LLUICtrl*, const LLSD&) { onToggleLoadSavePanel(); }); mCommitCallbackRegistrar.add("Poser.ToggleLoadSavePanel", [this](LLUICtrl*, const LLSD&) { onToggleLoadSavePanel(); });
mCommitCallbackRegistrar.add("Poser.ToggleVisualManipulators", [this](LLUICtrl*, const LLSD&) { onToggleVisualManipulators(); }); mCommitCallbackRegistrar.add("Poser.ToggleVisualManipulators", [this](LLUICtrl*, const LLSD&) { onToggleVisualManipulators(); });
mCommitCallbackRegistrar.add("Poser.ToggleRotationFrame", [this](LLUICtrl* button, const LLSD&) { onToggleRotationFrameButton(button); });
mCommitCallbackRegistrar.add("Poser.UndoLastRotation", [this](LLUICtrl*, const LLSD&) { onUndoLastChange(); }); mCommitCallbackRegistrar.add("Poser.UndoLastRotation", [this](LLUICtrl*, const LLSD&) { onUndoLastChange(); });
mCommitCallbackRegistrar.add("Poser.RedoLastRotation", [this](LLUICtrl*, const LLSD&) { onRedoLastChange(); }); mCommitCallbackRegistrar.add("Poser.RedoLastRotation", [this](LLUICtrl*, const LLSD&) { onRedoLastChange(); });
@ -205,9 +210,17 @@ bool FSFloaterPoser::postBuild()
mRedoChangeBtn = getChild<LLButton>("button_redo_change"); mRedoChangeBtn = getChild<LLButton>("button_redo_change");
mUndoChangeBtn = getChild<LLButton>("undo_change"); mUndoChangeBtn = getChild<LLButton>("undo_change");
mSetToTposeButton = getChild<LLButton>("set_t_pose_button"); mSetToTposeButton = getChild<LLButton>("set_t_pose_button");
mBtnJointReset = getChild<LLButton>("poser_joint_reset");
mBtnWorldFrame = getChild<LLButton>("poser_world_frame_toggle");
mBtnAvatarFrame = getChild<LLButton>("poser_avatar_frame_toggle");
mBtnScreenFrame = getChild<LLButton>("poser_screen_frame_toggle");
mJointsParentPnl = getChild<LLPanel>("joints_parent_panel"); mJointsParentPnl = getChild<LLPanel>("joints_parent_panel");
mTrackballPnl = getChild<LLPanel>("trackball_panel"); mTrackballPnl = getChild<LLPanel>("trackball_panel");
mPositionPnl = getChild<LLPanel>("position_panel");
mMoveTabPnl = getChild<LLPanel>("move_tab_panel");
mTrackballButtonPnl = getChild<LLPanel>("trackball_button_panel");
mPositionRotationPnl = getChild<LLPanel>("positionRotation_panel"); mPositionRotationPnl = getChild<LLPanel>("positionRotation_panel");
mBodyJointsPnl = getChild<LLPanel>("body_joints_panel"); mBodyJointsPnl = getChild<LLPanel>("body_joints_panel");
mFaceJointsPnl = getChild<LLPanel>("face_joints_panel"); mFaceJointsPnl = getChild<LLPanel>("face_joints_panel");
@ -269,6 +282,76 @@ void FSFloaterPoser::onFocusLost()
} }
} }
void FSFloaterPoser::draw()
{
LLFloater::draw();
drawOnHoverJointHint();
}
void FSFloaterPoser::markSelectedJointsToHighlight()
{
bool toolsEnabled = mToggleVisualManipulators->getValue().asBoolean();
if (toolsEnabled)
return;
bool showHighlights = gSavedSettings.getBOOL(POSER_SHOWBONEHIGHLIGHTS_SAVE_KEY);
if (!showHighlights)
return;
auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.empty())
return;
std::string jointName = selectedJoints[0]->jointName();
bool isRightLimb = jointName.find("Right") != std::string::npos;
bool isLeftLimb = jointName.find("Left") != std::string::npos;
if (!(isRightLimb || isLeftLimb))
return;
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
mLastSelectedJoint = selectedJoints[0];
timeFadeStartedMicrosec = gFrameTime;
}
void FSFloaterPoser::drawOnHoverJointHint()
{
if (!mLastSelectedJoint)
return;
constexpr U64 GLOW_TIME_US = 300000;
U64 fadeTimeUs = gFrameTime - timeFadeStartedMicrosec;
if (fadeTimeUs > GLOW_TIME_US)
return;
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return;
if (!mPoserAnimator.isPosingAvatar(avatar))
return;
LLJoint* joint = avatar->getJoint(std::string(mLastSelectedJoint->jointName()));
if (!joint)
return;
F32 alphaFade = 1.f * (GLOW_TIME_US - fadeTimeUs) / GLOW_TIME_US;
static LLUIColor mBeaconColor = LLUIColorTable::getInstance()->getColor("AreaSearchBeaconColor");
LLColor4 beaconColour = mBeaconColor.get();
beaconColour.setAlpha(alphaFade);
LLVector3 joint_world_position = joint->getWorldPosition();
static LLCachedControl<S32> beacon_line_width(gSavedSettings, "DebugBeaconLineWidth");
gObjectList.addDebugBeacon(joint_world_position, "", beaconColour, beaconColour, beacon_line_width);
}
void FSFloaterPoser::enableVisualManipulators() void FSFloaterPoser::enableVisualManipulators()
{ {
if (!gAgentAvatarp || gAgentAvatarp.isNull()) if (!gAgentAvatarp || gAgentAvatarp.isNull())
@ -359,6 +442,9 @@ void FSFloaterPoser::onPoseFileSelect()
if (!avatar) if (!avatar)
return; return;
if (!havePermissionToAnimateAvatar(avatar) && !havePermissionToAnimateOtherAvatar(avatar))
return;
bool enableButtons = mPoserAnimator.isPosingAvatar(avatar); bool enableButtons = mPoserAnimator.isPosingAvatar(avatar);
mLoadPosesBtn->setEnabled(enableButtons); mLoadPosesBtn->setEnabled(enableButtons);
mSavePosesBtn->setEnabled(enableButtons); mSavePosesBtn->setEnabled(enableButtons);
@ -411,8 +497,11 @@ void FSFloaterPoser::onClickPoseSave()
} }
LLVOAvatar* avatar = getUiSelectedAvatar(); LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar) if (!havePermissionToAnimateAvatar(avatar) && !havePermissionToAnimateOtherAvatar(avatar))
{
mSavePosesBtn->setImageOverlay(tryGetString(ICON_SAVE_FAILED), mSavePosesBtn->getImageOverlayHAlign());
return; return;
}
// if prompts are disabled or file doesn't exist, do the save immediately: // if prompts are disabled or file doesn't exist, do the save immediately:
const bool prompt = gSavedSettings.getBOOL(POSER_SAVECONFIRMREQUIRED_SAVE_KEY); const bool prompt = gSavedSettings.getBOOL(POSER_SAVECONFIRMREQUIRED_SAVE_KEY);
@ -534,7 +623,7 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
record["startFromTeePose"]["value"] = !savingDiff; record["startFromTeePose"]["value"] = !savingDiff;
if (savingDiff) if (savingDiff)
mPoserAnimator.savePosingState(avatar, &record); mPoserAnimator.savePosingState(avatar, false, &record);
LLVector3 rotation, position, scale, zeroVector; LLVector3 rotation, position, scale, zeroVector;
bool baseRotationIsZero; bool baseRotationIsZero;
@ -593,7 +682,7 @@ bool FSFloaterPoser::savePoseToXml(LLVOAvatar* avatar, const std::string& poseFi
void FSFloaterPoser::onClickToggleSelectedBoneEnabled() void FSFloaterPoser::onClickToggleSelectedBoneEnabled()
{ {
auto selectedJoints = getUiSelectedPoserJoints(); auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1) if (selectedJoints.empty())
return; return;
LLVOAvatar *avatar = getUiSelectedAvatar(); LLVOAvatar *avatar = getUiSelectedAvatar();
@ -617,7 +706,7 @@ void FSFloaterPoser::onClickToggleSelectedBoneEnabled()
void FSFloaterPoser::onClickFlipSelectedJoints() void FSFloaterPoser::onClickFlipSelectedJoints()
{ {
auto selectedJoints = getUiSelectedPoserJoints(); auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1) if (selectedJoints.empty())
return; return;
LLVOAvatar *avatar = getUiSelectedAvatar(); LLVOAvatar *avatar = getUiSelectedAvatar();
@ -676,7 +765,7 @@ void FSFloaterPoser::onClickFlipPose()
void FSFloaterPoser::onClickRecaptureSelectedBones() void FSFloaterPoser::onClickRecaptureSelectedBones()
{ {
auto selectedJoints = getUiSelectedPoserJoints(); auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1) if (selectedJoints.empty())
return; return;
LLVOAvatar *avatar = getUiSelectedAvatar(); LLVOAvatar *avatar = getUiSelectedAvatar();
@ -694,31 +783,40 @@ void FSFloaterPoser::onClickRecaptureSelectedBones()
if (currentlyPosing) if (currentlyPosing)
continue; continue;
mPoserAnimator.recaptureJoint(avatar, *item, getJointTranslation(item->jointName()), getJointNegation(item->jointName())); mPoserAnimator.recaptureJoint(avatar, *item);
} }
setSavePosesButtonText(true); setSavePosesButtonText(true);
refreshRotationSlidersAndSpinners(); refreshRotationSlidersAndSpinners();
refreshPositionSlidersAndSpinners();
refreshScaleSlidersAndSpinners();
refreshTrackpadCursor(); refreshTrackpadCursor();
refreshTextHighlightingOnJointScrollLists(); refreshTextHighlightingOnJointScrollLists();
enableOrDisableRedoAndUndoButton(); enableOrDisableRedoAndUndoButton();
} }
void FSFloaterPoser::updatePosedBones(const std::string& jointName) void FSFloaterPoser::updatePosedBones(const std::string& jointName, const LLQuaternion& rotation, const LLVector3& position, const LLVector3& scale)
{ {
LLVOAvatar *avatar = getUiSelectedAvatar(); LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar) if (!avatar)
return; return;
if (!mPoserAnimator.isPosingAvatar(avatar)) if (!mPoserAnimator.isPosingAvatar(avatar))
return; return;
bool haveImplicitPermission = havePermissionToAnimateAvatar(avatar); // self & control avatars you own
bool iCanPoseThem = havePermissionToAnimateOtherAvatar(avatar);
if (!haveImplicitPermission && !iCanPoseThem)
return;
const FSPoserAnimator::FSPoserJoint* poserJoint = mPoserAnimator.getPoserJointByName(jointName); const FSPoserAnimator::FSPoserJoint* poserJoint = mPoserAnimator.getPoserJointByName(jointName);
if (!poserJoint) if (!poserJoint)
return; return;
bool savingToExternal = getSavingToBvh(); bool savingToExternal = getSavingToBvh();
mPoserAnimator.recaptureJointAsDelta(avatar, poserJoint, savingToExternal, getUiSelectedBoneDeflectionStyle()); E_PoserReferenceFrame frame = getReferenceFrame();
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
mPoserAnimator.updateJointFromManip(avatar, poserJoint, savingToExternal, defl, frame, rotation, position, scale);
refreshRotationSlidersAndSpinners(); refreshRotationSlidersAndSpinners();
refreshPositionSlidersAndSpinners(); refreshPositionSlidersAndSpinners();
@ -728,6 +826,24 @@ void FSFloaterPoser::updatePosedBones(const std::string& jointName)
refreshTextHighlightingOnJointScrollLists(); refreshTextHighlightingOnJointScrollLists();
} }
LLQuaternion FSFloaterPoser::getManipGimbalRotation(const std::string& jointName)
{
LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar)
return LLQuaternion();
if (!mPoserAnimator.isPosingAvatar(avatar))
return LLQuaternion();
const FSPoserAnimator::FSPoserJoint* poserJoint = mPoserAnimator.getPoserJointByName(jointName);
if (!poserJoint)
return LLQuaternion();
E_PoserReferenceFrame frame = getReferenceFrame();
return mPoserAnimator.getManipGimbalRotation(avatar, poserJoint, frame);
}
void FSFloaterPoser::onClickBrowsePoseCache() void FSFloaterPoser::onClickBrowsePoseCache()
{ {
createUserPoseDirectoryIfNeeded(); createUserPoseDirectoryIfNeeded();
@ -930,6 +1046,9 @@ void FSFloaterPoser::timedReload()
if (!avatar) if (!avatar)
return; return;
if (!havePermissionToAnimateAvatar(avatar) && !havePermissionToAnimateOtherAvatar(avatar))
return;
if (loadPoseFromXml(avatar, mLoadPoseTimer->getPosePath(), mLoadPoseTimer->getLoadMethod())) if (loadPoseFromXml(avatar, mLoadPoseTimer->getPosePath(), mLoadPoseTimer->getLoadMethod()))
{ {
mLoadPoseTimer->completeLoading(); mLoadPoseTimer->completeLoading();
@ -1024,6 +1143,8 @@ void FSFloaterPoser::onClickLoadHandPose(bool isRightHand)
LLVOAvatar* avatar = getUiSelectedAvatar(); LLVOAvatar* avatar = getUiSelectedAvatar();
if (!avatar) if (!avatar)
return; return;
if (!havePermissionToAnimateAvatar(avatar) && !havePermissionToAnimateOtherAvatar(avatar))
return;
if (!mPoserAnimator.isPosingAvatar(avatar)) if (!mPoserAnimator.isPosingAvatar(avatar))
return; return;
@ -1233,8 +1354,8 @@ bool FSFloaterPoser::loadPoseFromXml(LLVOAvatar* avatar, const std::string& pose
mPoserAnimator.setRotationIsMirrored(avatar, *poserJoint, mirroredJoint); mPoserAnimator.setRotationIsMirrored(avatar, *poserJoint, mirroredJoint);
} }
if (version > 6 && !startFromZeroRot) if (version > 6 && !startFromZeroRot && !loadSelective)
loadSuccess = mPoserAnimator.loadPosingState(avatar, pose); loadSuccess = mPoserAnimator.loadPosingState(avatar, true, pose);
} }
} }
catch ( const std::exception & e ) catch ( const std::exception & e )
@ -1310,7 +1431,7 @@ void FSFloaterPoser::onPoseStartStop()
onAvatarSelect(); onAvatarSelect();
} }
bool FSFloaterPoser::couldAnimateAvatar(LLVOAvatar *avatar) const bool FSFloaterPoser::couldAnimateAvatar(LLVOAvatar* avatar) const
{ {
if (!avatar || avatar->isDead()) if (!avatar || avatar->isDead())
return false; return false;
@ -1320,14 +1441,31 @@ bool FSFloaterPoser::couldAnimateAvatar(LLVOAvatar *avatar) const
return true; return true;
} }
bool FSFloaterPoser::havePermissionToAnimateAvatar(LLVOAvatar *avatar) const bool FSFloaterPoser::havePermissionToAnimateAvatar(LLVOAvatar* avatar) const
{ {
if (!avatar || avatar->isDead()) if (!avatar || avatar->isDead())
return false; return false;
if (avatar->isSelf()) if (avatar->isSelf())
return true; return true;
if (avatar->isControlAvatar()) if (avatar->isControlAvatar())
return true; {
LLControlAvatar* control_av = dynamic_cast<LLControlAvatar*>(avatar);
const LLVOVolume* rootVolume = control_av->mRootVolp;
const LLViewerObject* rootEditObject = (rootVolume) ? rootVolume->getRootEdit() : NULL;
if (!rootEditObject)
return false;
return rootEditObject->permYouOwner();
}
return false;
}
bool FSFloaterPoser::havePermissionToAnimateOtherAvatar(LLVOAvatar* avatar) const
{
if (!avatar || avatar->isDead())
return false;
return false; return false;
} }
@ -1342,6 +1480,12 @@ void FSFloaterPoser::poseControlsEnable(bool enable)
mLoadPosesBtn->setEnabled(enable); mLoadPosesBtn->setEnabled(enable);
mSavePosesBtn->setEnabled(enable); mSavePosesBtn->setEnabled(enable);
mPoseSaveNameEditor->setEnabled(enable); mPoseSaveNameEditor->setEnabled(enable);
mBtnJointReset->setEnabled(enable);
mRedoChangeBtn->setEnabled(enable);
mUndoChangeBtn->setEnabled(enable);
mPositionPnl->setEnabled(enable);
mMoveTabPnl->setEnabled(enable);
mTrackballButtonPnl->setEnabled(enable);
} }
void FSFloaterPoser::refreshJointScrollListMembers() void FSFloaterPoser::refreshJointScrollListMembers()
@ -1422,13 +1566,13 @@ void FSFloaterPoser::addHeaderRowToScrollList(const std::string& jointName, LLSc
return; return;
LLScrollListItem *hdrRow = bodyJointsScrollList->addElement(headerRow); LLScrollListItem *hdrRow = bodyJointsScrollList->addElement(headerRow);
hdrRow->setEnabled(FALSE); hdrRow->setEnabled(false);
} }
LLSD FSFloaterPoser::createRowForJoint(const std::string& jointName, bool isHeaderRow) LLSD FSFloaterPoser::createRowForJoint(const std::string& jointName, bool isHeaderRow)
{ {
if (jointName.empty()) if (jointName.empty())
return NULL; return {};
std::string headerValue = ""; std::string headerValue = "";
if (isHeaderRow) if (isHeaderRow)
@ -1439,7 +1583,7 @@ LLSD FSFloaterPoser::createRowForJoint(const std::string& jointName, bool isHead
if (hasString(parameterName)) if (hasString(parameterName))
jointValue = getString(parameterName); jointValue = getString(parameterName);
else else
return NULL; return {};
LLSD row; LLSD row;
row["columns"][COL_ICON]["column"] = "icon"; row["columns"][COL_ICON]["column"] = "icon";
@ -1500,6 +1644,22 @@ void FSFloaterPoser::setRotationChangeButtons(bool togglingMirror, bool toggling
refreshTrackpadCursor(); refreshTrackpadCursor();
} }
void FSFloaterPoser::onToggleRotationFrameButton(const LLUICtrl* toggleButton)
{
if (!toggleButton)
return;
if (bool buttonDown = toggleButton->getValue().asBoolean())
{
mBtnAvatarFrame->setValue(toggleButton == mBtnAvatarFrame);
mBtnScreenFrame->setValue(toggleButton == mBtnScreenFrame);
mBtnWorldFrame->setValue(toggleButton == mBtnWorldFrame);
}
FSToolCompPose::getInstance()->setReferenceFrame(getReferenceFrame());
refreshRotationSlidersAndSpinners();
}
void FSFloaterPoser::onUndoLastChange() void FSFloaterPoser::onUndoLastChange()
{ {
LLVOAvatar* avatar = getUiSelectedAvatar(); LLVOAvatar* avatar = getUiSelectedAvatar();
@ -1510,7 +1670,7 @@ void FSFloaterPoser::onUndoLastChange()
return; return;
auto selectedJoints = getUiSelectedPoserJoints(); auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1) if (selectedJoints.empty())
return; return;
for (auto item : selectedJoints) for (auto item : selectedJoints)
@ -1541,7 +1701,7 @@ void FSFloaterPoser::onSetAvatarToTpose()
enableOrDisableRedoAndUndoButton(); enableOrDisableRedoAndUndoButton();
} }
void FSFloaterPoser::onResetJoint(const LLSD data) void FSFloaterPoser::onResetJoint(const LLSD& data)
{ {
int resetType = data.asInteger(); int resetType = data.asInteger();
@ -1553,7 +1713,7 @@ void FSFloaterPoser::onResetJoint(const LLSD data)
return; return;
auto selectedJoints = getUiSelectedPoserJoints(); auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1) if (selectedJoints.empty())
return; return;
for (auto item : selectedJoints) for (auto item : selectedJoints)
@ -1583,7 +1743,7 @@ void FSFloaterPoser::onRedoLastChange()
return; return;
auto selectedJoints = getUiSelectedPoserJoints(); auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1) if (selectedJoints.empty())
return; return;
for (auto item : selectedJoints) for (auto item : selectedJoints)
@ -1615,7 +1775,7 @@ void FSFloaterPoser::enableOrDisableRedoAndUndoButton()
return; return;
auto selectedJoints = getUiSelectedPoserJoints(); auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1) if (selectedJoints.empty())
return; return;
bool shouldEnableRedoButton = false; bool shouldEnableRedoButton = false;
@ -1706,7 +1866,7 @@ void FSFloaterPoser::selectJointByName(const std::string& jointName)
LL_WARNS() << "Joint not found: " << jointName << LL_ENDL; LL_WARNS() << "Joint not found: " << jointName << LL_ENDL;
} }
LLScrollListCtrl* FSFloaterPoser::getScrollListForTab(LLPanel * tabPanel) const LLScrollListCtrl* FSFloaterPoser::getScrollListForTab(LLPanel* tabPanel) const
{ {
if (tabPanel == mPositionRotationPnl) if (tabPanel == mPositionRotationPnl)
{ {
@ -1733,7 +1893,6 @@ LLScrollListCtrl* FSFloaterPoser::getScrollListForTab(LLPanel * tabPanel) const
return mCollisionVolumesScrollList; return mCollisionVolumesScrollList;
} }
LL_WARNS() << "Unknown tab panel: " << tabPanel << LL_ENDL;
return nullptr; return nullptr;
} }
@ -1769,8 +1928,7 @@ std::vector<FSPoserAnimator::FSPoserJoint*> FSFloaterPoser::getUiSelectedPoserJo
for (auto item : scrollList->getAllSelected()) for (auto item : scrollList->getAllSelected())
{ {
auto* userData = static_cast<FSPoserAnimator::FSPoserJoint*>(item->getUserdata()); if (auto* userData = static_cast<FSPoserAnimator::FSPoserJoint*>(item->getUserdata()))
if (userData)
{ {
joints.push_back(userData); joints.push_back(userData);
} }
@ -1781,13 +1939,16 @@ std::vector<FSPoserAnimator::FSPoserJoint*> FSFloaterPoser::getUiSelectedPoserJo
return joints; return joints;
} }
void FSFloaterPoser::updateManipWithFirstSelectedJoint(std::vector<FSPoserAnimator::FSPoserJoint*> joints) const void FSFloaterPoser::updateManipWithFirstSelectedJoint(const std::vector<FSPoserAnimator::FSPoserJoint*>& joints) const
{ {
auto avatarp = getUiSelectedAvatar(); auto avatarp = getUiSelectedAvatar();
if (!avatarp) if (!avatarp)
return; return;
if (joints.size() >= 1) bool haveImplicitPermission = havePermissionToAnimateAvatar(avatarp);
bool iCanPoseThem = havePermissionToAnimateOtherAvatar(avatarp);
if ((joints.size() >= 1) && (haveImplicitPermission || iCanPoseThem))
FSToolCompPose::getInstance()->setJoint(avatarp->getJoint(joints[0]->jointName())); FSToolCompPose::getInstance()->setJoint(avatarp->getJoint(joints[0]->jointName()));
else else
FSToolCompPose::getInstance()->setJoint(nullptr); FSToolCompPose::getInstance()->setJoint(nullptr);
@ -2075,6 +2236,7 @@ void FSFloaterPoser::setSelectedJointsPosition(F32 x, F32 y, F32 z)
LLVector3 vec3 = LLVector3(x, y, z); LLVector3 vec3 = LLVector3(x, y, z);
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle(); E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
E_PoserReferenceFrame frame = getReferenceFrame();
for (auto item : getUiSelectedPoserJoints()) for (auto item : getUiSelectedPoserJoints())
{ {
@ -2082,7 +2244,7 @@ void FSFloaterPoser::setSelectedJointsPosition(F32 x, F32 y, F32 z)
if (!currentlyPosingJoint) if (!currentlyPosingJoint)
continue; continue;
mPoserAnimator.setJointPosition(avatar, item, vec3, defl); mPoserAnimator.setJointPosition(avatar, item, vec3, frame, defl);
} }
} }
@ -2097,7 +2259,8 @@ void FSFloaterPoser::setSelectedJointsRotation(const LLVector3& absoluteRot, con
auto selectedJoints = getUiSelectedPoserJoints(); auto selectedJoints = getUiSelectedPoserJoints();
bool savingToExternal = getSavingToBvh(); bool savingToExternal = getSavingToBvh();
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle(); E_BoneDeflectionStyles deflection = getUiSelectedBoneDeflectionStyle();
E_PoserReferenceFrame frame = getReferenceFrame();
for (auto item : selectedJoints) for (auto item : selectedJoints)
{ {
@ -2111,14 +2274,17 @@ void FSFloaterPoser::setSelectedJointsRotation(const LLVector3& absoluteRot, con
bool oppositeJointAlsoSelectedOnUi = bool oppositeJointAlsoSelectedOnUi =
std::find(selectedJoints.begin(), selectedJoints.end(), oppositeJoint) != selectedJoints.end(); std::find(selectedJoints.begin(), selectedJoints.end(), oppositeJoint) != selectedJoints.end();
bool deflectionDoesOppositeLimbs = !(defl == NONE || defl == DELTAMODE); bool deflectionDoesOppositeLimbs = !(deflection == NONE || deflection == DELTAMODE);
if (oppositeJointAlsoSelectedOnUi && deflectionDoesOppositeLimbs && item->dontFlipOnMirror()) if (oppositeJointAlsoSelectedOnUi && deflectionDoesOppositeLimbs && item->dontFlipOnMirror())
continue; continue;
} }
mPoserAnimator.setJointRotation(avatar, item, absoluteRot, deltaRot, defl, S32 jointNegation = getJointNegation(frame, item->jointName());
getJointTranslation(item->jointName()), getJointNegation(item->jointName()), savingToExternal, E_BoneAxisTranslation translation = getJointTranslation(frame, item->jointName());
getUiSelectedBoneRotationStyle(item->jointName())); E_RotationStyle style = getUiSelectedBoneRotationStyle(item->jointName());
mPoserAnimator.setJointRotation(avatar, item, absoluteRot, deltaRot, deflection, frame, translation, jointNegation,
savingToExternal, style);
} }
if (savingToExternal) if (savingToExternal)
@ -2136,6 +2302,7 @@ void FSFloaterPoser::setSelectedJointsScale(F32 x, F32 y, F32 z)
LLVector3 vec3 = LLVector3(x, y, z); LLVector3 vec3 = LLVector3(x, y, z);
E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle(); E_BoneDeflectionStyles defl = getUiSelectedBoneDeflectionStyle();
E_PoserReferenceFrame frame = getReferenceFrame();
for (auto item : getUiSelectedPoserJoints()) for (auto item : getUiSelectedPoserJoints())
{ {
@ -2143,7 +2310,7 @@ void FSFloaterPoser::setSelectedJointsScale(F32 x, F32 y, F32 z)
if (!currentlyPosingJoint) if (!currentlyPosingJoint)
continue; continue;
mPoserAnimator.setJointScale(avatar, item, vec3, defl); mPoserAnimator.setJointScale(avatar, item, vec3, frame, defl);
} }
} }
@ -2151,7 +2318,7 @@ LLVector3 FSFloaterPoser::getRotationOfFirstSelectedJoint() const
{ {
LLVector3 rotation; LLVector3 rotation;
auto selectedJoints = getUiSelectedPoserJoints(); auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1) if (selectedJoints.empty())
return rotation; return rotation;
LLVOAvatar *avatar = getUiSelectedAvatar(); LLVOAvatar *avatar = getUiSelectedAvatar();
@ -2161,8 +2328,10 @@ LLVector3 FSFloaterPoser::getRotationOfFirstSelectedJoint() const
if (!mPoserAnimator.isPosingAvatar(avatar)) if (!mPoserAnimator.isPosingAvatar(avatar))
return rotation; return rotation;
rotation = mPoserAnimator.getJointRotation(avatar, *selectedJoints.front(), getJointTranslation(selectedJoints.front()->jointName()), E_PoserReferenceFrame frame = getReferenceFrame();
getJointNegation(selectedJoints.front()->jointName()));
rotation = mPoserAnimator.getJointRotation(avatar, *selectedJoints.front(), getJointTranslation(frame, selectedJoints.front()->jointName()),
getJointNegation(frame, selectedJoints.front()->jointName()));
return rotation; return rotation;
} }
@ -2171,7 +2340,7 @@ LLVector3 FSFloaterPoser::getPositionOfFirstSelectedJoint() const
{ {
LLVector3 position; LLVector3 position;
auto selectedJoints = getUiSelectedPoserJoints(); auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1) if (selectedJoints.empty())
return position; return position;
LLVOAvatar *avatar = getUiSelectedAvatar(); LLVOAvatar *avatar = getUiSelectedAvatar();
@ -2189,7 +2358,7 @@ LLVector3 FSFloaterPoser::getScaleOfFirstSelectedJoint() const
{ {
LLVector3 scale; LLVector3 scale;
auto selectedJoints = getUiSelectedPoserJoints(); auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1) if (selectedJoints.empty())
return scale; return scale;
LLVOAvatar *avatar = getUiSelectedAvatar(); LLVOAvatar *avatar = getUiSelectedAvatar();
@ -2210,18 +2379,25 @@ void FSFloaterPoser::onJointTabSelect()
refreshTrackpadCursor(); refreshTrackpadCursor();
enableOrDisableRedoAndUndoButton(); enableOrDisableRedoAndUndoButton();
refreshScaleSlidersAndSpinners(); refreshScaleSlidersAndSpinners();
markSelectedJointsToHighlight();
} }
E_BoneAxisTranslation FSFloaterPoser::getJointTranslation(const std::string& jointName) const E_BoneAxisTranslation FSFloaterPoser::getJointTranslation(E_PoserReferenceFrame frame, const std::string& jointName) const
{ {
if (jointName.empty()) if (jointName.empty())
return SWAP_NOTHING; return SWAP_NOTHING;
bool hasTransformParameter = hasString(XML_JOINT_TRANSFORM_STRING_PREFIX + jointName); std::string paramName;
if (frame == POSER_FRAME_BONE)
paramName = XML_JOINT_TRANSFORM_STRING_PREFIX + jointName;
else
paramName = XML_JOINT_FRAME_TRANSFORM_PREFIX + jointName;
bool hasTransformParameter = hasString(paramName);
if (!hasTransformParameter) if (!hasTransformParameter)
return SWAP_NOTHING; return SWAP_NOTHING;
std::string paramValue = getString(XML_JOINT_TRANSFORM_STRING_PREFIX + jointName); std::string paramValue = getString(paramName);
if (strstr(paramValue.c_str(), "SWAP_YAW_AND_ROLL")) if (strstr(paramValue.c_str(), "SWAP_YAW_AND_ROLL"))
return SWAP_YAW_AND_ROLL; return SWAP_YAW_AND_ROLL;
@ -2237,18 +2413,24 @@ E_BoneAxisTranslation FSFloaterPoser::getJointTranslation(const std::string& joi
return SWAP_NOTHING; return SWAP_NOTHING;
} }
S32 FSFloaterPoser::getJointNegation(const std::string& jointName) const S32 FSFloaterPoser::getJointNegation(E_PoserReferenceFrame frame, const std::string& jointName) const
{ {
S32 result = NEGATE_NOTHING; S32 result = NEGATE_NOTHING;
if (jointName.empty()) if (jointName.empty())
return result; return result;
bool hasTransformParameter = hasString(XML_JOINT_TRANSFORM_STRING_PREFIX + jointName); std::string paramName;
if (!hasTransformParameter) if (frame == POSER_FRAME_BONE)
paramName = XML_JOINT_TRANSFORM_STRING_PREFIX + jointName;
else
paramName = XML_JOINT_FRAME_TRANSFORM_PREFIX + jointName;
bool hasNegationParameter = hasString(paramName);
if (!hasNegationParameter)
return result; return result;
std::string paramValue = getString(XML_JOINT_TRANSFORM_STRING_PREFIX + jointName); std::string paramValue = getString(paramName);
if (strstr(paramValue.c_str(), "NEGATE_YAW")) if (strstr(paramValue.c_str(), "NEGATE_YAW"))
result |= NEGATE_YAW; result |= NEGATE_YAW;
@ -2262,6 +2444,23 @@ S32 FSFloaterPoser::getJointNegation(const std::string& jointName) const
return result; return result;
} }
E_PoserReferenceFrame FSFloaterPoser::getReferenceFrame() const
{
bool toggleButtonValue = mBtnScreenFrame->getValue().asBoolean();
if (toggleButtonValue)
return POSER_FRAME_CAMERA;
toggleButtonValue = mBtnAvatarFrame->getValue().asBoolean();
if (toggleButtonValue)
return POSER_FRAME_AVATAR;
toggleButtonValue = mBtnWorldFrame->getValue().asBoolean();
if (toggleButtonValue)
return POSER_FRAME_WORLD;
return POSER_FRAME_BONE;
}
/// <summary> /// <summary>
/// An event handler for selecting an avatar or animesh on the POSES_AVATAR_SCROLL_LIST_NAME. /// An event handler for selecting an avatar or animesh on the POSES_AVATAR_SCROLL_LIST_NAME.
/// In general this will refresh the views for joints or their proxies, and (dis/en)able elements of the view. /// In general this will refresh the views for joints or their proxies, and (dis/en)able elements of the view.
@ -2269,16 +2468,26 @@ S32 FSFloaterPoser::getJointNegation(const std::string& jointName) const
void FSFloaterPoser::onAvatarSelect() void FSFloaterPoser::onAvatarSelect()
{ {
LLVOAvatar* avatar = getUiSelectedAvatar(); LLVOAvatar* avatar = getUiSelectedAvatar();
if(avatar) if (!avatar)
{ return;
bool isSelf = avatar->isSelf();
bool haveImplicitPermission = havePermissionToAnimateAvatar(avatar); // self & control avatars you own
bool haveExplicitPermission = havePermissionToAnimateOtherAvatar(avatar); // as permissions allow
if (haveImplicitPermission || haveExplicitPermission)
FSToolCompPose::getInstance()->setAvatar(avatar); FSToolCompPose::getInstance()->setAvatar(avatar);
} else
mStartStopPosingBtn->setEnabled(couldAnimateAvatar(avatar)); FSToolCompPose::getInstance()->setAvatar(nullptr);
bool arePosingSelected = mPoserAnimator.isPosingAvatar(avatar); bool arePosingSelected = mPoserAnimator.isPosingAvatar(avatar);
mStartStopPosingBtn->setEnabled(haveImplicitPermission);
mStartStopPosingBtn->setValue(arePosingSelected); mStartStopPosingBtn->setValue(arePosingSelected);
mSetToTposeButton->setEnabled(arePosingSelected);
poseControlsEnable(arePosingSelected); mSetToTposeButton->setEnabled(haveImplicitPermission && arePosingSelected);
poseControlsEnable(arePosingSelected && haveImplicitPermission);
refreshTextHighlightingOnAvatarScrollList(); refreshTextHighlightingOnAvatarScrollList();
refreshTextHighlightingOnJointScrollLists(); refreshTextHighlightingOnJointScrollLists();
onJointTabSelect(); onJointTabSelect();
@ -2292,7 +2501,16 @@ uuid_vec_t FSFloaterPoser::getNearbyAvatarsAndAnimeshes() const
for (LLCharacter* character : LLCharacter::sInstances) for (LLCharacter* character : LLCharacter::sInstances)
{ {
LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(character); LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(character);
if (!havePermissionToAnimateAvatar(avatar)) if (!avatarIsNearbyMe(avatar))
continue;
bool isMuted = LLMuteList::getInstance()->isMuted(avatar->getID());
if (isMuted)
continue;
bool isSelfOrCtrl = avatar->isControlAvatar() || avatar->isSelf();
if (!isSelfOrCtrl)
continue; continue;
avatar_ids.emplace_back(character->getID()); avatar_ids.emplace_back(character->getID());
@ -2301,6 +2519,16 @@ uuid_vec_t FSFloaterPoser::getNearbyAvatarsAndAnimeshes() const
return avatar_ids; return avatar_ids;
} }
bool FSFloaterPoser::avatarIsNearbyMe(LLCharacter* character) const
{
if (!gAgentAvatarp || gAgentAvatarp.isNull() || !character)
return false;
LLVector3 separationVector = character->getCharacterPosition() - gAgentAvatarp->getCharacterPosition();
return separationVector.magVec() < 50.f;
}
uuid_vec_t FSFloaterPoser::getCurrentlyListedAvatarsAndAnimeshes() const uuid_vec_t FSFloaterPoser::getCurrentlyListedAvatarsAndAnimeshes() const
{ {
uuid_vec_t avatar_ids; uuid_vec_t avatar_ids;
@ -2351,22 +2579,18 @@ void FSFloaterPoser::onAvatarsRefresh()
mAvatarSelectionScrollList->deleteSingleItem(indexToRemove); mAvatarSelectionScrollList->deleteSingleItem(indexToRemove);
} }
std::string iconCatagoryName = "Inv_BodyShape";
if (hasString("icon_category"))
iconCatagoryName = getString("icon_category");
std::string iconObjectName = "Inv_Object"; std::string iconObjectName = "Inv_Object";
if (hasString("icon_object")) if (hasString("icon_object"))
iconObjectName = getString("icon_object"); iconObjectName = getString("icon_object");
// Add non-Animesh avatars // Add non-Animesh avatars
for (LLCharacter *character : LLCharacter::sInstances) for (LLCharacter* character : LLCharacter::sInstances)
{ {
LLUUID uuid = character->getID(); LLUUID uuid = character->getID();
if (std::find(avatarsToAddToList.begin(), avatarsToAddToList.end(), uuid) == avatarsToAddToList.end()) if (std::find(avatarsToAddToList.begin(), avatarsToAddToList.end(), uuid) == avatarsToAddToList.end())
continue; continue;
LLVOAvatar *avatar = dynamic_cast<LLVOAvatar *>(character); LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(character);
if (!couldAnimateAvatar(avatar)) if (!couldAnimateAvatar(avatar))
continue; continue;
@ -2377,10 +2601,16 @@ void FSFloaterPoser::onAvatarsRefresh()
if (!LLAvatarNameCache::get(uuid, &av_name)) if (!LLAvatarNameCache::get(uuid, &av_name))
continue; continue;
if (LLMuteList::getInstance()->isMuted(uuid))
continue;
if (!avatar->isSelf())
continue;
LLSD row; LLSD row;
row["columns"][COL_ICON]["column"] = "icon"; row["columns"][COL_ICON]["column"] = "icon";
row["columns"][COL_ICON]["type"] = "icon"; row["columns"][COL_ICON]["type"] = "icon";
row["columns"][COL_ICON]["value"] = iconCatagoryName; row["columns"][COL_ICON]["value"] = getIconNameForAvatar(avatar);
row["columns"][COL_NAME]["column"] = "name"; row["columns"][COL_NAME]["column"] = "name";
row["columns"][COL_NAME]["value"] = av_name.getDisplayName(); row["columns"][COL_NAME]["value"] = av_name.getDisplayName();
row["columns"][COL_UUID]["column"] = "uuid"; row["columns"][COL_UUID]["column"] = "uuid";
@ -2397,7 +2627,7 @@ void FSFloaterPoser::onAvatarsRefresh()
if (std::find(avatarsToAddToList.begin(), avatarsToAddToList.end(), uuid) == avatarsToAddToList.end()) if (std::find(avatarsToAddToList.begin(), avatarsToAddToList.end(), uuid) == avatarsToAddToList.end())
continue; continue;
LLControlAvatar *avatar = dynamic_cast<LLControlAvatar *>(character); LLControlAvatar* avatar = dynamic_cast<LLControlAvatar* >(character);
if (!couldAnimateAvatar(avatar)) if (!couldAnimateAvatar(avatar))
continue; continue;
@ -2423,18 +2653,33 @@ void FSFloaterPoser::onAvatarsRefresh()
refreshTextHighlightingOnAvatarScrollList(); refreshTextHighlightingOnAvatarScrollList();
} }
std::string FSFloaterPoser::getIconNameForAvatar(LLVOAvatar* avatar)
{
std::string iconName = "Inv_BodyShape";
if (hasString("icon_category"))
iconName = getString("icon_category");
if (!avatar)
return iconName;
if (avatar->isControlAvatar() && hasString("icon_object"))
return getString("icon_object");
return iconName;
}
std::string FSFloaterPoser::getControlAvatarName(const LLControlAvatar* avatar) std::string FSFloaterPoser::getControlAvatarName(const LLControlAvatar* avatar)
{ {
if (!avatar) if (!avatar)
return ""; return {};
const LLVOVolume* rootVolume = avatar->mRootVolp; const LLVOVolume* rootVolume = avatar->mRootVolp;
const LLViewerObject* rootEditObject = (rootVolume) ? rootVolume->getRootEdit() : NULL; const LLViewerObject* rootEditObject = rootVolume ? rootVolume->getRootEdit() : nullptr;
if (!rootEditObject) if (!rootEditObject)
return ""; return {};
const LLViewerInventoryItem* attachedItem = const LLViewerInventoryItem* attachedItem =
(rootEditObject->isAttachment()) ? gInventory.getItem(rootEditObject->getAttachmentItemID()) : NULL; (rootEditObject->isAttachment()) ? gInventory.getItem(rootEditObject->getAttachmentItemID()) : nullptr;
if (attachedItem) if (attachedItem)
return attachedItem->getName(); return attachedItem->getName();
@ -2442,7 +2687,7 @@ std::string FSFloaterPoser::getControlAvatarName(const LLControlAvatar* avatar)
if (rootEditObject->permYouOwner()) if (rootEditObject->permYouOwner())
return avatar->getFullname(); return avatar->getFullname();
return ""; return {};
} }
void FSFloaterPoser::refreshTextHighlightingOnAvatarScrollList() void FSFloaterPoser::refreshTextHighlightingOnAvatarScrollList()
@ -2456,10 +2701,12 @@ void FSFloaterPoser::refreshTextHighlightingOnAvatarScrollList()
LLUUID selectedAvatarId = cell->getValue().asUUID(); LLUUID selectedAvatarId = cell->getValue().asUUID();
LLVOAvatar* listAvatar = getAvatarByUuid(selectedAvatarId); LLVOAvatar* listAvatar = getAvatarByUuid(selectedAvatarId);
((LLScrollListText*)listItem->getColumn(COL_ICON))->setValue(getIconNameForAvatar(listAvatar));
if (mPoserAnimator.isPosingAvatar(listAvatar)) if (mPoserAnimator.isPosingAvatar(listAvatar))
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD); ((LLScrollListText*)listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD);
else else
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::NORMAL); ((LLScrollListText*)listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::NORMAL);
} }
} }
@ -2492,29 +2739,29 @@ void FSFloaterPoser::addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* ava
for (auto listItem : list->getAllData()) for (auto listItem : list->getAllData())
{ {
FSPoserAnimator::FSPoserJoint *poserJoint = static_cast<FSPoserAnimator::FSPoserJoint *>(listItem->getUserdata()); FSPoserAnimator::FSPoserJoint* poserJoint = static_cast<FSPoserAnimator::FSPoserJoint*>(listItem->getUserdata());
if (!poserJoint) if (!poserJoint)
continue; continue;
((LLScrollListText*)listItem->getColumn(COL_ICON))->setValue(getScrollListIconForJoint(avatar, *poserJoint)); ((LLScrollListText*)listItem->getColumn(COL_ICON))->setValue(getScrollListIconForJoint(avatar, *poserJoint));
if (mPoserAnimator.isPosingAvatarJoint(avatar, *poserJoint)) if (mPoserAnimator.isPosingAvatarJoint(avatar, *poserJoint))
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD); ((LLScrollListText*)listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD);
else else
((LLScrollListText *) listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::NORMAL); ((LLScrollListText*)listItem->getColumn(COL_NAME))->setFontStyle(LLFontGL::NORMAL);
} }
} }
std::string FSFloaterPoser::getScrollListIconForJoint(LLVOAvatar* avatar, FSPoserAnimator::FSPoserJoint joint) std::string FSFloaterPoser::getScrollListIconForJoint(LLVOAvatar* avatar, FSPoserAnimator::FSPoserJoint joint)
{ {
if (!avatar) if (!avatar)
return ""; return {};
if (mPoserAnimator.getRotationIsWorldLocked(avatar, joint)) if (mPoserAnimator.getRotationIsWorldLocked(avatar, joint))
return tryGetString("icon_rotation_is_world_locked"); return tryGetString("icon_rotation_is_world_locked");
if (!getSavingToBvh()) if (!getSavingToBvh())
return ""; return {};
if (joint.boneType() == COL_VOLUMES) if (joint.boneType() == COL_VOLUMES)
return tryGetString("icon_rotation_does_not_export"); return tryGetString("icon_rotation_does_not_export");
@ -2530,10 +2777,10 @@ std::string FSFloaterPoser::getScrollListIconForJoint(LLVOAvatar* avatar, FSPose
return tryGetString("icon_rotation_bvh_unlocked"); return tryGetString("icon_rotation_bvh_unlocked");
} }
std::string FSFloaterPoser::tryGetString(std::string name) std::string FSFloaterPoser::tryGetString(std::string_view name)
{ {
if (name.empty()) if (name.empty())
return ""; return {};
return hasString(name) ? getString(name) : ""; return hasString(name) ? getString(name) : "";
} }
@ -2560,8 +2807,7 @@ bool FSFloaterPoser::savePoseToBvh(LLVOAvatar* avatar, const std::string& poseFi
std::string fullSavePath = std::string fullSavePath =
gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, poseFileName + POSE_EXTERNAL_FORMAT_FILE_EXT); gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, poseFileName + POSE_EXTERNAL_FORMAT_FILE_EXT);
llofstream file; llofstream file(fullSavePath.c_str());
file.open(fullSavePath.c_str());
if (!file.is_open()) if (!file.is_open())
{ {
LL_WARNS("Poser") << "Unable to save pose!" << LL_ENDL; LL_WARNS("Poser") << "Unable to save pose!" << LL_ENDL;
@ -2658,7 +2904,7 @@ void FSFloaterPoser::writeBvhFragment(llofstream* fileStream, LLVOAvatar* avatar
{ {
for (size_t index = 0; index != numberOfBvhChildNodes; ++index) for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{ {
auto nextJoint = mPoserAnimator.getPoserJointByName(joint->bvhChildren()[index]); auto nextJoint = mPoserAnimator.getPoserJointByName(joint->bvhChildren().at(index));
writeBvhFragment(fileStream, avatar, nextJoint, tabStops + 1); writeBvhFragment(fileStream, avatar, nextJoint, tabStops + 1);
} }
} }
@ -2693,7 +2939,7 @@ void FSFloaterPoser::writeFirstFrameOfBvhMotion(llofstream* fileStream, const FS
size_t numberOfBvhChildNodes = joint->bvhChildren().size(); size_t numberOfBvhChildNodes = joint->bvhChildren().size();
for (size_t index = 0; index != numberOfBvhChildNodes; ++index) for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{ {
auto nextJoint = mPoserAnimator.getPoserJointByName(joint->bvhChildren()[index]); auto nextJoint = mPoserAnimator.getPoserJointByName(joint->bvhChildren().at(index));
writeFirstFrameOfBvhMotion(fileStream, nextJoint); writeFirstFrameOfBvhMotion(fileStream, nextJoint);
} }
} }
@ -2722,15 +2968,15 @@ void FSFloaterPoser::writeBvhMotion(llofstream* fileStream, LLVOAvatar* avatar,
size_t numberOfBvhChildNodes = joint->bvhChildren().size(); size_t numberOfBvhChildNodes = joint->bvhChildren().size();
for (size_t index = 0; index != numberOfBvhChildNodes; ++index) for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{ {
auto nextJoint = mPoserAnimator.getPoserJointByName(joint->bvhChildren()[index]); auto nextJoint = mPoserAnimator.getPoserJointByName(joint->bvhChildren().at(index));
writeBvhMotion(fileStream, avatar, nextJoint); writeBvhMotion(fileStream, avatar, nextJoint);
} }
} }
std::string FSFloaterPoser::positionToString(const LLVector3& val) std::string FSFloaterPoser::positionToString(const LLVector3& val)
{ {
const float metresToInches = 39.37008f; constexpr F32 metersToInches = 39.37008f;
return std::to_string(metresToInches * val[VY]) + " " + std::to_string(metresToInches * val[VZ]) + " " + std::to_string(metresToInches * val[VX]); return std::to_string(metersToInches * val[VY]) + " " + std::to_string(metersToInches * val[VZ]) + " " + std::to_string(metersToInches * val[VX]);
} }
std::string FSFloaterPoser::rotationToString(const LLVector3& val) std::string FSFloaterPoser::rotationToString(const LLVector3& val)
@ -2797,7 +3043,7 @@ S32 FSFloaterPoser::getBvhJointNegation(const std::string& jointName) const
return result; return result;
} }
bool FSFloaterPoser::getSavingToBvh() bool FSFloaterPoser::getSavingToBvh() const
{ {
return gSavedSettings.getBOOL(POSER_SAVEEXTERNALFORMAT_SAVE_KEY); return gSavedSettings.getBOOL(POSER_SAVEEXTERNALFORMAT_SAVE_KEY);
} }
@ -2811,7 +3057,7 @@ void FSFloaterPoser::onClickSavingToBvh()
void FSFloaterPoser::onClickLockWorldRotBtn() void FSFloaterPoser::onClickLockWorldRotBtn()
{ {
auto selectedJoints = getUiSelectedPoserJoints(); auto selectedJoints = getUiSelectedPoserJoints();
if (selectedJoints.size() < 1) if (selectedJoints.empty())
return; return;
LLVOAvatar* avatar = getUiSelectedAvatar(); LLVOAvatar* avatar = getUiSelectedAvatar();

View File

@ -31,6 +31,7 @@
#include "llfloater.h" #include "llfloater.h"
#include "lltoolmgr.h" #include "lltoolmgr.h"
#include "fsposeranimator.h" #include "fsposeranimator.h"
#include "fsmaniprotatejoint.h"
class FSVirtualTrackpad; class FSVirtualTrackpad;
class LLButton; class LLButton;
@ -80,7 +81,8 @@ class FSFloaterPoser : public LLFloater, public LLEditMenuHandler
friend class LLFloaterReg; friend class LLFloaterReg;
FSFloaterPoser(const LLSD &key); FSFloaterPoser(const LLSD &key);
public: public:
void updatePosedBones(const std::string& jointName); void updatePosedBones(const std::string& jointName, const LLQuaternion& rotation, const LLVector3& position, const LLVector3& scale);
LLQuaternion getManipGimbalRotation(const std::string& jointName);
void selectJointByName(const std::string& jointName); void selectJointByName(const std::string& jointName);
void undo() override { onUndoLastChange(); }; void undo() override { onUndoLastChange(); };
bool canUndo() const override { return true; } bool canUndo() const override { return true; }
@ -94,6 +96,8 @@ public:
void onClose(bool app_quitting) override; void onClose(bool app_quitting) override;
void onFocusReceived() override; void onFocusReceived() override;
void onFocusLost() override; void onFocusLost() override;
virtual void draw() override;
/// <summary> /// <summary>
/// Refreshes the supplied pose list from the supplued subdirectory. /// Refreshes the supplied pose list from the supplued subdirectory.
/// </summary> /// </summary>
@ -139,7 +143,7 @@ public:
/// Updates the visual with the first selected joint from the supplied collection, if any. /// Updates the visual with the first selected joint from the supplied collection, if any.
/// </summary> /// </summary>
/// <param name="joints">The collection of selected joints.</param> /// <param name="joints">The collection of selected joints.</param>
void updateManipWithFirstSelectedJoint(std::vector<FSPoserAnimator::FSPoserJoint*> joints) const; void updateManipWithFirstSelectedJoint(const std::vector<FSPoserAnimator::FSPoserJoint*>& joints) const;
/// <summary> /// <summary>
/// Gets a detectable avatar by its UUID. /// Gets a detectable avatar by its UUID.
@ -181,6 +185,13 @@ public:
/// <returns>A the collection of UUIDs for nearby avatars.</returns> /// <returns>A the collection of UUIDs for nearby avatars.</returns>
uuid_vec_t getNearbyAvatarsAndAnimeshes() const; uuid_vec_t getNearbyAvatarsAndAnimeshes() const;
/// <summary>
/// Gets whether the supplied character is within chat range of gAgentAvatar.
/// </summary>
/// <param name="character">The character to query whether nearby.</param>
/// <returns>True if the supplied character is within chat range, otherwise false.</returns>
bool avatarIsNearbyMe(LLCharacter* character) const;
/// <summary> /// <summary>
/// Gets a collection of UUIDs for avatars currently being presented on the UI. /// Gets a collection of UUIDs for avatars currently being presented on the UI.
/// </summary> /// </summary>
@ -218,7 +229,7 @@ public:
LLVector3 getPositionOfFirstSelectedJoint() const; LLVector3 getPositionOfFirstSelectedJoint() const;
LLVector3 getScaleOfFirstSelectedJoint() const; LLVector3 getScaleOfFirstSelectedJoint() const;
LLScrollListCtrl* getScrollListForTab(LLPanel * tabPanel) const; LLScrollListCtrl* getScrollListForTab(LLPanel* tabPanel) const;
// Pose load/save // Pose load/save
void createUserPoseDirectoryIfNeeded(); void createUserPoseDirectoryIfNeeded();
void onToggleLoadSavePanel(); void onToggleLoadSavePanel();
@ -242,17 +253,22 @@ public:
void enableVisualManipulators(); void enableVisualManipulators();
void disableVisualManipulators(); void disableVisualManipulators();
// Visual cue for which bone is under the mouse-cursor
void drawOnHoverJointHint();
void markSelectedJointsToHighlight();
// UI Event Handlers // UI Event Handlers
void onAvatarsRefresh(); void onAvatarsRefresh();
void onAvatarSelect(); void onAvatarSelect();
void onJointTabSelect(); void onJointTabSelect();
void onToggleMirrorChange(); void onToggleMirrorChange();
void onToggleSympatheticChange(); void onToggleSympatheticChange();
void onToggleRotationFrameButton(const LLUICtrl* toggleButton);
void onToggleVisualManipulators(); void onToggleVisualManipulators();
void setRotationChangeButtons(bool mirror, bool sympathetic); void setRotationChangeButtons(bool mirror, bool sympathetic);
void onUndoLastChange(); void onUndoLastChange();
void onRedoLastChange(); void onRedoLastChange();
void onResetJoint(const LLSD data); void onResetJoint(const LLSD& data);
void onSetAvatarToTpose(); void onSetAvatarToTpose();
void onPoseStartStop(); void onPoseStartStop();
void onTrackballChanged(); void onTrackballChanged();
@ -280,6 +296,14 @@ public:
void refreshTrackpadCursor(); void refreshTrackpadCursor();
void enableOrDisableRedoAndUndoButton(); void enableOrDisableRedoAndUndoButton();
/// <summary>
/// Determines if we have permission to animate the supplied avatar.
/// </summary>
/// <param name="avatar">The avatar to animate.</param>
/// <returns>True if we have permission to animate, otherwise false.</returns>
bool havePermissionToAnimateOtherAvatar(LLVOAvatar* avatar) const;
/// <summary> /// <summary>
/// Determines if we have permission to animate the supplied avatar. /// Determines if we have permission to animate the supplied avatar.
/// </summary> /// </summary>
@ -306,6 +330,7 @@ public:
/// This facilitates 'conceptual' conversion of Euler frame to up/down, left/right and roll and is rather subjective. /// This facilitates 'conceptual' conversion of Euler frame to up/down, left/right and roll and is rather subjective.
/// Thus, many of these 'conversions' are backed by values in the XML. /// Thus, many of these 'conversions' are backed by values in the XML.
/// </summary> /// </summary>
/// <param name="frame">The reference frame for the change.</param>
/// <param name="jointName">The well-known name of the joint, eg: mChest.</param> /// <param name="jointName">The well-known name of the joint, eg: mChest.</param>
/// <returns>The axial translation so the oily angles make better sense in terms of up/down/left/right/roll.</returns> /// <returns>The axial translation so the oily angles make better sense in terms of up/down/left/right/roll.</returns>
/// <remarks> /// <remarks>
@ -313,14 +338,21 @@ public:
/// No the translation isn't untangling all of that, it's not needed until it is. /// No the translation isn't untangling all of that, it's not needed until it is.
/// We're not landing on Mars with this code, just offering a user reasonable thumb-twiddlings. /// We're not landing on Mars with this code, just offering a user reasonable thumb-twiddlings.
/// </remarks> /// </remarks>
E_BoneAxisTranslation getJointTranslation(const std::string& jointName) const; E_BoneAxisTranslation getJointTranslation(E_PoserReferenceFrame frame, const std::string& jointName) const;
/// <summary> /// <summary>
/// Gets the collection of E_BoneAxisNegation values for the supplied joint. /// Gets the collection of E_BoneAxisNegation values for the supplied joint.
/// </summary> /// </summary>
/// <param name="frame">The reference frame for the change.</param>
/// <param name="jointName">The name of the joind to get the axis transformation for.</param> /// <param name="jointName">The name of the joind to get the axis transformation for.</param>
/// <returns>The kind of axis transformation to perform.</returns> /// <returns>The kind of axis transformation to perform.</returns>
S32 getJointNegation(const std::string& jointName) const; S32 getJointNegation(E_PoserReferenceFrame frame, const std::string& jointName) const;
/// <summary>
/// Gets the reference frame for the rotation/position/scale change.
/// </summary>
/// <returns>The reference frame for the change.</returns>
E_PoserReferenceFrame getReferenceFrame() const;
/// <summary> /// <summary>
/// Gets the axial translation required for joints when saving to BVH. /// Gets the axial translation required for joints when saving to BVH.
@ -336,6 +368,13 @@ public:
/// </summary> /// </summary>
void refreshTextHighlightingOnAvatarScrollList(); void refreshTextHighlightingOnAvatarScrollList();
/// <summary>
/// Gets an appropriate icon for the supplied avatar, based on sharing permission.
/// </summary>
/// <param name="avatar">The avatar to get an icon for.</param>
/// <returns>A string with the name of an icon.</returns>
std::string getIconNameForAvatar(LLVOAvatar* avatar);
/// <summary> /// <summary>
/// Refreshes the text on all joints scroll lists based on their state. /// Refreshes the text on all joints scroll lists based on their state.
/// </summary> /// </summary>
@ -367,7 +406,7 @@ public:
/// </summary> /// </summary>
/// <param name="name">The name of the string.</param> /// <param name="name">The name of the string.</param>
/// <returns>The named string, if it exists, otherwise an empty string.</returns> /// <returns>The named string, if it exists, otherwise an empty string.</returns>
std::string tryGetString(std::string name); std::string tryGetString(std::string_view name);
/// <summary> /// <summary>
/// Gets the name of an item from the supplied object ID. /// Gets the name of an item from the supplied object ID.
@ -383,7 +422,7 @@ public:
/// Gets whether the pose should also write a BVH file when saved. /// Gets whether the pose should also write a BVH file when saved.
/// </summary> /// </summary>
/// <returns>True if the user wants to additionally save a BVH file, otherwise false.</returns> /// <returns>True if the user wants to additionally save a BVH file, otherwise false.</returns>
bool getSavingToBvh(); bool getSavingToBvh() const;
/// <summary> /// <summary>
/// Writes the current pose in BVH-format to the supplied stream. /// Writes the current pose in BVH-format to the supplied stream.
@ -454,6 +493,8 @@ public:
LLTool* mJointRotTool{ nullptr }; LLTool* mJointRotTool{ nullptr };
LLVector3 mLastSliderRotation; LLVector3 mLastSliderRotation;
FSPoserAnimator::FSPoserJoint* mLastSelectedJoint{ nullptr };
U64 timeFadeStartedMicrosec;
FSVirtualTrackpad* mAvatarTrackball{ nullptr }; FSVirtualTrackpad* mAvatarTrackball{ nullptr };
@ -502,6 +543,10 @@ public:
LLButton* mUndoChangeBtn{ nullptr }; LLButton* mUndoChangeBtn{ nullptr };
LLButton* mSetToTposeButton{ nullptr }; LLButton* mSetToTposeButton{ nullptr };
LLButton* mBtnJointRotate{ nullptr }; LLButton* mBtnJointRotate{ nullptr };
LLButton* mBtnJointReset{ nullptr };
LLButton* mBtnWorldFrame{ nullptr };
LLButton* mBtnAvatarFrame{ nullptr };
LLButton* mBtnScreenFrame{ nullptr };
LLLineEditor* mPoseSaveNameEditor{ nullptr }; LLLineEditor* mPoseSaveNameEditor{ nullptr };
@ -516,6 +561,9 @@ public:
LLPanel* mMiscJointsPnl{ nullptr }; LLPanel* mMiscJointsPnl{ nullptr };
LLPanel* mCollisionVolumesPnl{ nullptr }; LLPanel* mCollisionVolumesPnl{ nullptr };
LLPanel* mPosesLoadSavePnl{ nullptr }; LLPanel* mPosesLoadSavePnl{ nullptr };
LLPanel* mPositionPnl{ nullptr };
LLPanel* mMoveTabPnl{ nullptr };
LLPanel* mTrackballButtonPnl{ nullptr };
LLCheckBoxCtrl* mAlsoSaveBvhCbx{ nullptr }; LLCheckBoxCtrl* mAlsoSaveBvhCbx{ nullptr };
LLCheckBoxCtrl* mUnlockPelvisInBvhSaveCbx{ nullptr }; LLCheckBoxCtrl* mUnlockPelvisInBvhSaveCbx{ nullptr };

View File

@ -0,0 +1,109 @@
/**
* @file fsfloatersplashscreensettings.cpp
* @brief Splash screen settings floater
*
* $LicenseInfo:firstyear=2025&license=viewerlgpl$
* Copyright (c) 2025 The Phoenix Firestorm Project, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "fsfloatersplashscreensettings.h"
#include "llcheckboxctrl.h"
#include "llviewercontrol.h"
FSFloaterSplashScreenSettings::FSFloaterSplashScreenSettings(const LLSD& key) :
LLFloater(key),
mHideTopBarCheck(nullptr),
mHideBlogsCheck(nullptr),
mHideDestinationsCheck(nullptr),
mUseGrayModeCheck(nullptr),
mUseHighContrastCheck(nullptr),
mUseAllCapsCheck(nullptr),
mUseLargerFontsCheck(nullptr),
mNoTransparencyCheck(nullptr)
{
}
FSFloaterSplashScreenSettings::~FSFloaterSplashScreenSettings()
{
}
bool FSFloaterSplashScreenSettings::postBuild()
{
mHideTopBarCheck = getChild<LLCheckBoxCtrl>("hide_top_bar");
mHideBlogsCheck = getChild<LLCheckBoxCtrl>("hide_blogs");
mHideDestinationsCheck = getChild<LLCheckBoxCtrl>("hide_destinations");
mUseGrayModeCheck = getChild<LLCheckBoxCtrl>("use_gray_mode");
mUseHighContrastCheck = getChild<LLCheckBoxCtrl>("use_high_contrast");
mUseAllCapsCheck = getChild<LLCheckBoxCtrl>("use_all_caps");
mUseLargerFontsCheck = getChild<LLCheckBoxCtrl>("use_larger_fonts");
mNoTransparencyCheck = getChild<LLCheckBoxCtrl>("no_transparency");
mHideTopBarCheck->setCommitCallback(boost::bind(&FSFloaterSplashScreenSettings::onSettingChanged, this));
mHideBlogsCheck->setCommitCallback(boost::bind(&FSFloaterSplashScreenSettings::onSettingChanged, this));
mHideDestinationsCheck->setCommitCallback(boost::bind(&FSFloaterSplashScreenSettings::onSettingChanged, this));
mUseGrayModeCheck->setCommitCallback(boost::bind(&FSFloaterSplashScreenSettings::onSettingChanged, this));
mUseHighContrastCheck->setCommitCallback(boost::bind(&FSFloaterSplashScreenSettings::onSettingChanged, this));
mUseAllCapsCheck->setCommitCallback(boost::bind(&FSFloaterSplashScreenSettings::onSettingChanged, this));
mUseLargerFontsCheck->setCommitCallback(boost::bind(&FSFloaterSplashScreenSettings::onSettingChanged, this));
mNoTransparencyCheck->setCommitCallback(boost::bind(&FSFloaterSplashScreenSettings::onSettingChanged, this));
loadSettings();
return true;
}
void FSFloaterSplashScreenSettings::onOpen(const LLSD& key)
{
LLFloater::onOpen(key);
loadSettings();
}
void FSFloaterSplashScreenSettings::loadSettings()
{
mHideTopBarCheck->setValue(gSavedSettings.getBOOL("FSSplashScreenHideTopBar"));
mHideBlogsCheck->setValue(gSavedSettings.getBOOL("FSSplashScreenHideBlogs"));
mHideDestinationsCheck->setValue(gSavedSettings.getBOOL("FSSplashScreenHideDestinations"));
mUseGrayModeCheck->setValue(gSavedSettings.getBOOL("FSSplashScreenUseGrayMode"));
mUseHighContrastCheck->setValue(gSavedSettings.getBOOL("FSSplashScreenUseHighContrast"));
mUseAllCapsCheck->setValue(gSavedSettings.getBOOL("FSSplashScreenUseAllCaps"));
mUseLargerFontsCheck->setValue(gSavedSettings.getBOOL("FSSplashScreenUseLargerFonts"));
mNoTransparencyCheck->setValue(gSavedSettings.getBOOL("FSSplashScreenNoTransparency"));
}
void FSFloaterSplashScreenSettings::saveSettings()
{
gSavedSettings.setBOOL("FSSplashScreenHideTopBar", mHideTopBarCheck->getValue().asBoolean());
gSavedSettings.setBOOL("FSSplashScreenHideBlogs", mHideBlogsCheck->getValue().asBoolean());
gSavedSettings.setBOOL("FSSplashScreenHideDestinations", mHideDestinationsCheck->getValue().asBoolean());
gSavedSettings.setBOOL("FSSplashScreenUseGrayMode", mUseGrayModeCheck->getValue().asBoolean());
gSavedSettings.setBOOL("FSSplashScreenUseHighContrast", mUseHighContrastCheck->getValue().asBoolean());
gSavedSettings.setBOOL("FSSplashScreenUseAllCaps", mUseAllCapsCheck->getValue().asBoolean());
gSavedSettings.setBOOL("FSSplashScreenUseLargerFonts", mUseLargerFontsCheck->getValue().asBoolean());
gSavedSettings.setBOOL("FSSplashScreenNoTransparency", mNoTransparencyCheck->getValue().asBoolean());
}
void FSFloaterSplashScreenSettings::onSettingChanged()
{
saveSettings();
}

View File

@ -0,0 +1,59 @@
/**
* @file fsfloatersplashscreensettings.h
* @brief Splash screen settings floater
*
* $LicenseInfo:firstyear=2025&license=viewerlgpl$
* Copyright (c) 2025 The Phoenix Firestorm Project, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#ifndef FS_FLOATERSPLASHSCREENSETTINGS_H
#define FS_FLOATERSPLASHSCREENSETTINGS_H
#include "llfloater.h"
class LLCheckBoxCtrl;
class FSFloaterSplashScreenSettings : public LLFloater
{
public:
FSFloaterSplashScreenSettings(const LLSD& key);
virtual ~FSFloaterSplashScreenSettings();
/*virtual*/ bool postBuild();
/*virtual*/ void onOpen(const LLSD& key);
private:
void onSettingChanged();
void loadSettings();
void saveSettings();
LLCheckBoxCtrl* mHideTopBarCheck;
LLCheckBoxCtrl* mHideBlogsCheck;
LLCheckBoxCtrl* mHideDestinationsCheck;
LLCheckBoxCtrl* mUseGrayModeCheck;
LLCheckBoxCtrl* mUseHighContrastCheck;
LLCheckBoxCtrl* mUseAllCapsCheck;
LLCheckBoxCtrl* mUseLargerFontsCheck;
LLCheckBoxCtrl* mNoTransparencyCheck;
};
#endif // FS_FLOATERSPLASHSCREENSETTINGS_H

View File

@ -25,7 +25,6 @@
*/ */
#include <deque> #include <deque>
#include <boost/algorithm/string.hpp>
#include "fsposingmotion.h" #include "fsposingmotion.h"
#include "llcharacter.h" #include "llcharacter.h"
@ -47,23 +46,26 @@ FSJointPose::FSJointPose(LLJoint* joint, U32 usage, bool isCollisionVolume)
mJointName = joint->getName(); mJointName = joint->getName();
mIsCollisionVolume = isCollisionVolume; mIsCollisionVolume = isCollisionVolume;
mJointNumber = joint->getJointNum();
mCurrentState = FSJointState(joint); mCurrentState = FSJointState(joint);
} }
void FSJointPose::setPublicPosition(const LLVector3& pos) void FSJointPose::setPublicPosition(const LLVector3& pos)
{ {
addStateToUndo(FSJointState(mCurrentState)); addStateToUndo(mCurrentState);
mCurrentState.mPosition.set(pos); mCurrentState.mPosition.set(pos);
mCurrentState.mLastChangeWasRotational = false; mCurrentState.mLastChangeWasRotational = false;
} }
void FSJointPose::setPublicRotation(bool zeroBase, const LLQuaternion& rot) void FSJointPose::setPublicRotation(bool zeroBase, const LLQuaternion& rot)
{ {
addStateToUndo(FSJointState(mCurrentState)); addStateToUndo(mCurrentState);
if (zeroBase) if (zeroBase)
zeroBaseRotation(true); zeroBaseRotation(true);
else
mCurrentState.mUserSpecifiedBaseZero = false;
mCurrentState.mRotation.set(rot); mCurrentState.mRotation.set(rot);
mCurrentState.mLastChangeWasRotational = true; mCurrentState.mLastChangeWasRotational = true;
@ -71,7 +73,7 @@ void FSJointPose::setPublicRotation(bool zeroBase, const LLQuaternion& rot)
void FSJointPose::setPublicScale(const LLVector3& scale) void FSJointPose::setPublicScale(const LLVector3& scale)
{ {
addStateToUndo(FSJointState(mCurrentState)); addStateToUndo(mCurrentState);
mCurrentState.mScale.set(scale); mCurrentState.mScale.set(scale);
mCurrentState.mLastChangeWasRotational = false; mCurrentState.mLastChangeWasRotational = false;
} }
@ -79,27 +81,30 @@ void FSJointPose::setPublicScale(const LLVector3& scale)
bool FSJointPose::undoLastChange() bool FSJointPose::undoLastChange()
{ {
bool changeType = mCurrentState.mLastChangeWasRotational; bool changeType = mCurrentState.mLastChangeWasRotational;
mCurrentState = undoLastStateChange(FSJointState(mCurrentState)); mCurrentState = undoLastStateChange(mCurrentState);
return changeType; return changeType;
} }
void FSJointPose::redoLastChange() void FSJointPose::redoLastChange()
{ {
mCurrentState = redoLastStateChange(FSJointState(mCurrentState)); mCurrentState = redoLastStateChange(mCurrentState);
} }
void FSJointPose::resetJoint() void FSJointPose::resetJoint()
{ {
addStateToUndo(FSJointState(mCurrentState)); addStateToUndo(mCurrentState);
mCurrentState.resetJoint(); mCurrentState.resetJoint();
mCurrentState.mLastChangeWasRotational = true; mCurrentState.mLastChangeWasRotational = true;
} }
void FSJointPose::addStateToUndo(FSJointState stateToAddToUndo) void FSJointPose::addStateToUndo(const FSJointState& stateToAddToUndo)
{ {
auto timeIntervalSinceLastChange = std::chrono::system_clock::now() - mTimeLastUpdatedCurrentState; mModifiedThisSession = true;
mTimeLastUpdatedCurrentState = std::chrono::system_clock::now();
auto now = std::chrono::system_clock::now();
auto timeIntervalSinceLastChange = now - mTimeLastUpdatedCurrentState;
mTimeLastUpdatedCurrentState = now;
if (timeIntervalSinceLastChange < UndoUpdateInterval) if (timeIntervalSinceLastChange < UndoUpdateInterval)
return; return;
@ -119,7 +124,7 @@ void FSJointPose::addStateToUndo(FSJointState stateToAddToUndo)
mLastSetJointStates.pop_back(); mLastSetJointStates.pop_back();
} }
FSJointPose::FSJointState FSJointPose::undoLastStateChange(FSJointState thingToSet) FSJointPose::FSJointState FSJointPose::undoLastStateChange(const FSJointState& thingToSet)
{ {
if (mLastSetJointStates.empty()) if (mLastSetJointStates.empty())
return thingToSet; return thingToSet;
@ -133,7 +138,7 @@ FSJointPose::FSJointState FSJointPose::undoLastStateChange(FSJointState thingToS
return mLastSetJointStates.at(mUndoneJointStatesIndex); return mLastSetJointStates.at(mUndoneJointStatesIndex);
} }
FSJointPose::FSJointState FSJointPose::redoLastStateChange(FSJointState thingToSet) FSJointPose::FSJointState FSJointPose::redoLastStateChange(const FSJointState& thingToSet)
{ {
if (mLastSetJointStates.empty()) if (mLastSetJointStates.empty())
return thingToSet; return thingToSet;
@ -151,40 +156,41 @@ FSJointPose::FSJointState FSJointPose::redoLastStateChange(FSJointState thingToS
void FSJointPose::recaptureJoint() void FSJointPose::recaptureJoint()
{ {
if (mIsCollisionVolume)
return;
LLJoint* joint = mJointState->getJoint(); LLJoint* joint = mJointState->getJoint();
if (!joint) if (!joint)
return; return;
addStateToUndo(FSJointState(mCurrentState)); addStateToUndo(mCurrentState);
if (mIsCollisionVolume)
{
mCurrentState.mPosition.clear();
mCurrentState.mScale.clear();
}
mCurrentState = FSJointState(joint); mCurrentState = FSJointState(joint);
mCurrentState.mLastChangeWasRotational = true; mCurrentState.mLastChangeWasRotational = true;
} }
LLQuaternion FSJointPose::recaptureJointAsDelta(bool zeroBase) LLQuaternion FSJointPose::updateJointAsDelta(bool zeroBase, const LLQuaternion& rotation, const LLVector3& position, const LLVector3& scale)
{ {
LLJoint* joint = mJointState->getJoint(); addStateToUndo(mCurrentState);
if (!joint)
return LLQuaternion::DEFAULT;
addStateToUndo(FSJointState(mCurrentState));
mCurrentState.mLastChangeWasRotational = true; mCurrentState.mLastChangeWasRotational = true;
return mCurrentState.updateFromJoint(joint, zeroBase);
return mCurrentState.updateFromJointProperties(zeroBase, rotation, position, scale);
} }
void FSJointPose::setBaseRotation(LLQuaternion rotation, LLJoint::JointPriority priority) void FSJointPose::setBaseRotation(const LLQuaternion& rotation, LLJoint::JointPriority priority)
{ {
mCurrentState.resetBaseRotation(rotation, priority); mCurrentState.resetBaseRotation(rotation, priority);
} }
void FSJointPose::setBasePosition(LLVector3 position, LLJoint::JointPriority priority) void FSJointPose::setBasePosition(const LLVector3& position, LLJoint::JointPriority priority)
{ {
mCurrentState.resetBasePosition(position, priority); mCurrentState.resetBasePosition(position, priority);
} }
void FSJointPose::setBaseScale(LLVector3 scale, LLJoint::JointPriority priority) void FSJointPose::setBaseScale(const LLVector3& scale, LLJoint::JointPriority priority)
{ {
mCurrentState.resetBaseScale(scale, priority); mCurrentState.resetBaseScale(scale, priority);
} }
@ -247,6 +253,7 @@ void FSJointPose::reflectRotation()
if (mIsCollisionVolume) if (mIsCollisionVolume)
return; return;
mModifiedThisSession = true;
mCurrentState.reflectRotation(); mCurrentState.reflectRotation();
} }

View File

@ -97,6 +97,8 @@ class FSJointPose
/// 'Public rotation' is the amount of rotation the user has added to the initial state. /// '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). /// 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. /// This distinguishes 'private' rotation, which is the state inherited from something like a pose in-world.
/// If zeroBase is true, we treat rotations as if in BVH mode: user work.
/// If zeroBase is false, we treat as NOT BVH: some existing pose and user work.
/// </remarks> /// </remarks>
void setPublicRotation(bool zeroBase, const LLQuaternion& rot); void setPublicRotation(bool zeroBase, const LLQuaternion& rot);
@ -173,29 +175,32 @@ class FSJointPose
/// Recalculates the delta reltive to the base for a new rotation. /// Recalculates the delta reltive to the base for a new rotation.
/// </summary> /// </summary>
/// <param name="zeroBase">Whether to zero the base rotation on setting the supplied rotation.</param> /// <param name="zeroBase">Whether to zero the base rotation on setting the supplied rotation.</param>
/// <param name="rotation">The rotation of the supplied joint.</param>
/// <param name="position">The position of the supplied joint.</param>
/// <param name="scale">The scale of the supplied joint.</param>
/// <returns>The rotation of the public difference between before and after recapture.</returns> /// <returns>The rotation of the public difference between before and after recapture.</returns>
LLQuaternion recaptureJointAsDelta(bool zeroBase); LLQuaternion updateJointAsDelta(bool zeroBase, const LLQuaternion& rotation, const LLVector3& position, const LLVector3& scale);
/// <summary> /// <summary>
/// Sets the base rotation to the supplied rotation if the supplied priority is appropriate. /// Sets the base rotation to the supplied rotation if the supplied priority is appropriate.
/// </summary> /// </summary>
/// <param name="rotation">The base rotation to set; zero is ignored.</param> /// <param name="rotation">The base rotation to set; zero is ignored.</param>
/// <param name="priority">The priority of the base rotation; only priority equal or higher than any prior sets have any effect.</param> /// <param name="priority">The priority of the base rotation; only priority equal or higher than any prior sets have any effect.</param>
void setBaseRotation(LLQuaternion rotation, LLJoint::JointPriority priority); void setBaseRotation(const LLQuaternion& rotation, LLJoint::JointPriority priority);
/// <summary> /// <summary>
/// Sets the base position to the supplied position if the supplied priority is appropriate. /// Sets the base position to the supplied position if the supplied priority is appropriate.
/// </summary> /// </summary>
/// <param name="position">The base position to set; zero is ignored.</param> /// <param name="position">The base position to set; zero is ignored.</param>
/// <param name="priority">The priority of the base rotation; only priority equal or higher than any prior sets have any effect.</param> /// <param name="priority">The priority of the base rotation; only priority equal or higher than any prior sets have any effect.</param>
void setBasePosition(LLVector3 position, LLJoint::JointPriority priority); void setBasePosition(const LLVector3& position, LLJoint::JointPriority priority);
/// <summary> /// <summary>
/// Sets the base scale to the supplied scale if the supplied priority is appropriate. /// Sets the base scale to the supplied scale if the supplied priority is appropriate.
/// </summary> /// </summary>
/// <param name="scale">The base scale to set; zero is ignored.</param> /// <param name="scale">The base scale to set; zero is ignored.</param>
/// <param name="priority">The priority of the base rotation; only priority equal or higher than any prior sets have any effect.</param> /// <param name="priority">The priority of the base rotation; only priority equal or higher than any prior sets have any effect.</param>
void setBaseScale(LLVector3 scale, LLJoint::JointPriority priority); void setBaseScale(const LLVector3& scale, LLJoint::JointPriority priority);
/// <summary> /// <summary>
/// Sets the priority of the bone to the supplied value. /// Sets the priority of the bone to the supplied value.
@ -253,6 +258,18 @@ class FSJointPose
/// </summary> /// </summary>
LLPointer<LLJointState> getJointState() const { return mJointState; } LLPointer<LLJointState> getJointState() const { return mJointState; }
/// <summary>
/// Gets whether this joint has been modified this session.
/// </summary>
/// <returns>True if the joint has been changed at all, otherwise false.</returns>
bool getJointModified() const { return mModifiedThisSession; }
/// <summary>
/// Gets the number of the joint represented by this.
/// </summary>
/// <returns>The joint number, derived from LLjoint.</returns>
S32 getJointNumber() const { return mJointNumber; }
class FSJointState class FSJointState
{ {
public: public:
@ -326,15 +343,12 @@ class FSJointPose
joint->setScale(mBaseScale); joint->setScale(mBaseScale);
} }
LLQuaternion updateFromJoint(LLJoint* joint, bool zeroBase) LLQuaternion updateFromJointProperties(bool zeroBase, const LLQuaternion rotation, const LLVector3 position, const LLVector3 scale)
{ {
if (!joint)
return LLQuaternion::DEFAULT;
LLQuaternion initalPublicRot = mRotation; LLQuaternion initalPublicRot = mRotation;
LLQuaternion invRot = mBaseRotation; LLQuaternion invRot = mBaseRotation;
invRot.conjugate(); invRot.conjugate();
LLQuaternion newPublicRot = joint->getRotation() * invRot; LLQuaternion newPublicRot = rotation * invRot;
if (zeroBase) if (zeroBase)
{ {
@ -343,8 +357,8 @@ class FSJointPose
} }
mRotation.set(newPublicRot); mRotation.set(newPublicRot);
mPosition.set(joint->getPosition() - mBasePosition); mPosition.set(position - mBasePosition);
mScale.set(joint->getScale() - mBaseScale); mScale.set(scale - mBaseScale);
return newPublicRot *= ~initalPublicRot; return newPublicRot *= ~initalPublicRot;
} }
@ -450,15 +464,22 @@ class FSJointPose
/// </summary> /// </summary>
bool mIsCollisionVolume{ false }; bool mIsCollisionVolume{ false };
S32 mJointNumber = -1;
/// <summary>
/// Whether this joint has ever been changed by poser.
/// </summary>
bool mModifiedThisSession{ false };
std::deque<FSJointState> mLastSetJointStates; std::deque<FSJointState> mLastSetJointStates;
size_t mUndoneJointStatesIndex = 0; size_t mUndoneJointStatesIndex = 0;
std::chrono::system_clock::time_point mTimeLastUpdatedCurrentState = std::chrono::system_clock::now(); std::chrono::system_clock::time_point mTimeLastUpdatedCurrentState = std::chrono::system_clock::now();
FSJointState mCurrentState; FSJointState mCurrentState;
void addStateToUndo(FSJointState stateToAddToUndo); void addStateToUndo(const FSJointState& stateToAddToUndo);
FSJointState undoLastStateChange(FSJointState currentState); FSJointState undoLastStateChange(const FSJointState& currentState);
FSJointState redoLastStateChange(FSJointState currentState); FSJointState redoLastStateChange(const FSJointState& currentState);
}; };
#endif // FS_JOINTPPOSE_H #endif // FS_JOINTPPOSE_H

View File

@ -25,11 +25,8 @@
* $/LicenseInfo$ * $/LicenseInfo$
*/ */
#include "llviewerprecompiledheaders.h" #include "llviewerprecompiledheaders.h"
#include "fsmaniprotatejoint.h"
#include "fsmaniprotatejoint.h"
// library includes
#include "llmath.h" #include "llmath.h"
#include "llgl.h" #include "llgl.h"
#include "llrender.h" #include "llrender.h"
@ -37,7 +34,6 @@
#include "llprimitive.h" #include "llprimitive.h"
#include "llview.h" #include "llview.h"
#include "llfontgl.h" #include "llfontgl.h"
#include "llrendersphere.h" #include "llrendersphere.h"
#include "llvoavatar.h" #include "llvoavatar.h"
#include "lljoint.h" #include "lljoint.h"
@ -173,15 +169,18 @@ static void renderStaticSphere(const LLVector3& joint_world_position, const LLCo
} }
} }
bool FSManipRotateJoint::isMouseOverJoint(S32 mouseX, S32 mouseY, const LLVector3& jointWorldPos, F32 jointRadius, F32& outDistanceFromCamera, F32& outRayDistanceFromCenter) const bool FSManipRotateJoint::isMouseOverJoint(S32 mouseX, S32 mouseY, const LLVector3& jointWorldPos, F32 jointRadius, F32& outDistanceFromCamera, F32& outRayDistanceFromCenter) const
{ {
// LL_INFOS("FSManipRotateJoint") << "Checking mouse("<< mouseX << "," << mouseY << ") over joint at: " << jointWorldPos << LL_ENDL; if (!mJoint || !mAvatar)
return false;
auto joint_center = gAgent.getPosGlobalFromAgent( jointWorldPos ); if (mAvatar->isDead() || !mAvatar->isFullyLoaded())
return false;
auto joint_center = mAvatar->getPosGlobalFromAgent(jointWorldPos);
// centre in *agent* space // centre in *agent* space
LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(joint_center); LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(joint_center);
LLVector3 ray_pt, ray_dir; LLVector3 ray_pt, ray_dir;
LLManipRotate::mouseToRay(mouseX, mouseY, &ray_pt, &ray_dir); LLManipRotate::mouseToRay(mouseX, mouseY, &ray_pt, &ray_dir);
@ -203,7 +202,7 @@ bool FSManipRotateJoint::isMouseOverJoint(S32 mouseX, S32 mouseY, const LLVector
return true; return true;
} }
} }
return (false); return false;
} }
//static //static
@ -232,7 +231,6 @@ const std::vector<std::string_view> FSManipRotateJoint::sSelectableJoints =
{ "mHipRight" }, { "mHipRight" },
{ "mKneeRight" }, { "mKneeRight" },
{ "mAnkleRight" }, { "mAnkleRight" },
}; };
const std::unordered_map<FSManipRotateJoint::e_manip_part, FSManipRotateJoint::RingRenderParams> FSManipRotateJoint::sRingParams = const std::unordered_map<FSManipRotateJoint::e_manip_part, FSManipRotateJoint::RingRenderParams> FSManipRotateJoint::sRingParams =
@ -241,6 +239,7 @@ const std::unordered_map<FSManipRotateJoint::e_manip_part, FSManipRotateJoint::R
{ LL_ROT_Y, { LL_ROT_Y, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), 90.f, LLVector3(1.f,0.f,0.f), LLColor4(0.f,1.f,0.f,1.f), LLColor4(0.f,1.f,0.f,0.3f), 1 } }, { LL_ROT_Y, { LL_ROT_Y, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), 90.f, LLVector3(1.f,0.f,0.f), LLColor4(0.f,1.f,0.f,1.f), LLColor4(0.f,1.f,0.f,0.3f), 1 } },
{ LL_ROT_X, { LL_ROT_X, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), 90.f, LLVector3(0.f,1.f,0.f), LLColor4(1.f,0.f,0.f,1.f), LLColor4(1.f,0.f,0.f,0.3f), 0 } } { LL_ROT_X, { LL_ROT_X, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), 90.f, LLVector3(0.f,1.f,0.f), LLColor4(1.f,0.f,0.f,1.f), LLColor4(1.f,0.f,0.f,0.3f), 0 } }
}; };
// Helper function: Builds an alignment quaternion from the computed bone axes. // Helper function: Builds an alignment quaternion from the computed bone axes.
// This quaternion rotates from the default coordinate system (assumed to be // This quaternion rotates from the default coordinate system (assumed to be
// X = (1,0,0), Y = (0,1,0), Z = (0,0,1)) into the bones natural coordinate system. // X = (1,0,0), Y = (0,1,0), Z = (0,0,1)) into the bones natural coordinate system.
@ -268,7 +267,7 @@ FSManipRotateJoint::BoneAxes FSManipRotateJoint::computeBoneAxes() const
BoneAxes axes; BoneAxes axes;
// Use 0,0,0 as local start for joint. // Use 0,0,0 as local start for joint.
LLVector3 joint_local_pos (0.f,0.f,0.f); LLVector3 joint_local_pos(0.f,0.f,0.f);
// Transform the local endpoint (mEnd) into world space. // Transform the local endpoint (mEnd) into world space.
LLVector3 localEnd = mJoint->getEnd(); LLVector3 localEnd = mJoint->getEnd();
@ -333,10 +332,11 @@ void FSManipRotateJoint::highlightHoverSpheres(S32 mouseX, S32 mouseY)
// Iterate through the avatar's joint map. // Iterate through the avatar's joint map.
F32 nearest_hit_distance = 0.f; F32 nearest_hit_distance = 0.f;
F32 nearest_ray_distance = 0.f; F32 nearest_ray_distance = 0.f;
LLJoint * nearest_joint = nullptr; LLJoint* nearest_joint = nullptr;
for ( const auto& entry : getSelectableJoints()) LLCachedControl<F32> target_radius(gSavedSettings, "FSManipRotateJointTargetSize", 0.03f);
{
for (const auto& entry : getSelectableJoints())
{
LLJoint* joint = mAvatar->getJoint(std::string(entry)); LLJoint* joint = mAvatar->getJoint(std::string(entry));
if (!joint) if (!joint)
continue; continue;
@ -347,14 +347,13 @@ void FSManipRotateJoint::highlightHoverSpheres(S32 mouseX, S32 mouseY)
// Retrieve the joint's world position (in agent space). // Retrieve the joint's world position (in agent space).
LLVector3 jointWorldPos = joint->getWorldPosition(); LLVector3 jointWorldPos = joint->getWorldPosition();
LLCachedControl<F32> target_radius(gSavedSettings, "FSManipRotateJointTargetSize", 0.03f);
F32 distance_from_camera; F32 distance_from_camera;
F32 distance_from_joint; F32 distance_from_joint;
if (isMouseOverJoint(mouseX, mouseY, jointWorldPos, target_radius, distance_from_camera, distance_from_joint) == true) if (!isMouseOverJoint(mouseX, mouseY, jointWorldPos, target_radius, distance_from_camera, distance_from_joint))
{ continue;
// we want to highlight the closest // we want to highlight the closest
// If there is no joint or // If there is no joint or this joint is a closer hit than the previous one
// this joint is a closer hit than the previous one
if (!nearest_joint || nearest_ray_distance > distance_from_camera || if (!nearest_joint || nearest_ray_distance > distance_from_camera ||
(nearest_ray_distance == distance_from_camera && nearest_hit_distance > distance_from_joint)) (nearest_ray_distance == distance_from_camera && nearest_hit_distance > distance_from_joint))
{ {
@ -363,7 +362,7 @@ void FSManipRotateJoint::highlightHoverSpheres(S32 mouseX, S32 mouseY)
nearest_ray_distance = distance_from_camera; nearest_ray_distance = distance_from_camera;
} }
} }
}
mHighlightedJoint = nearest_joint; mHighlightedJoint = nearest_joint;
} }
@ -376,19 +375,23 @@ FSManipRotateJoint::FSManipRotateJoint(LLToolComposite* composite)
void FSManipRotateJoint::setJoint(LLJoint* joint) void FSManipRotateJoint::setJoint(LLJoint* joint)
{ {
mJoint = joint; mJoint = joint;
if (!mJoint)
return;
// Save initial rotation as baseline for delta rotation // Save initial rotation as baseline for delta rotation
if (mJoint) mSavedJointRot = getSelectedJointWorldRotation();
{
mSavedJointRot = mJoint->getWorldRotation();
mBoneAxes = computeBoneAxes(); mBoneAxes = computeBoneAxes();
mNaturalAlignmentQuat = computeAlignmentQuat(mBoneAxes); mNaturalAlignmentQuat = computeAlignmentQuat(mBoneAxes);
}
} }
void FSManipRotateJoint::setAvatar(LLVOAvatar* avatar) void FSManipRotateJoint::setAvatar(LLVOAvatar* avatar)
{ {
mAvatar = avatar; mAvatar = avatar;
if (!avatar)
mJoint = nullptr;
if (mAvatar && mJoint)
setJoint(avatar->getJoint(mJoint->getJointNum()));
} }
/** /**
@ -403,7 +406,7 @@ void FSManipRotateJoint::handleSelect()
// Not entirely sure this is needed in the current implementation. // Not entirely sure this is needed in the current implementation.
if (mJoint) if (mJoint)
{ {
mSavedJointRot = mJoint->getWorldRotation(); mSavedJointRot = getSelectedJointWorldRotation();
} }
} }
@ -422,15 +425,12 @@ void FSManipRotateJoint::handleSelect()
*/ */
bool FSManipRotateJoint::updateVisiblity() bool FSManipRotateJoint::updateVisiblity()
{ {
if (!mJoint) if (!isAvatarJointSafeToUse())
{
// No joint to manipulate, not visible
return false; return false;
}
if (!hasMouseCapture()) if (!hasMouseCapture())
{ {
mRotationCenter = gAgent.getPosGlobalFromAgent( mJoint->getWorldPosition() ); mRotationCenter = mAvatar->getPosGlobalFromAgent(mJoint->getWorldPosition());
mCamEdgeOn = false; mCamEdgeOn = false;
} }
@ -439,7 +439,7 @@ bool FSManipRotateJoint::updateVisiblity()
//Assume that UI scale factor is equivalent for X and Y axis //Assume that UI scale factor is equivalent for X and Y axis
F32 ui_scale_factor = LLUI::getScaleFactor().mV[VX]; F32 ui_scale_factor = LLUI::getScaleFactor().mV[VX];
const LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal( mRotationCenter ); // Convert from world/agent to global const LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(mRotationCenter); // Convert from world/agent to global
const auto * viewer_camera = LLViewerCamera::getInstance(); const auto * viewer_camera = LLViewerCamera::getInstance();
visible = viewer_camera->projectPosAgentToScreen(agent_space_center, mCenterScreen ); visible = viewer_camera->projectPosAgentToScreen(agent_space_center, mCenterScreen );
@ -473,7 +473,6 @@ bool FSManipRotateJoint::updateVisiblity()
return visible; return visible;
} }
/** /**
* @brief Updates the scale of a specific manipulator part. * @brief Updates the scale of a specific manipulator part.
* *
@ -503,18 +502,26 @@ void FSManipRotateJoint::renderRingPass(const RingRenderParams& params, float ra
// If an extra rotation is specified, apply it. // If an extra rotation is specified, apply it.
if (params.extraRotateAngle != 0.f) if (params.extraRotateAngle != 0.f)
{ {
gGL.rotatef(params.extraRotateAngle, params.extraRotateAxis.mV[0], gGL.rotatef(params.extraRotateAngle, params.extraRotateAxis.mV[VX], params.extraRotateAxis.mV[VY], params.extraRotateAxis.mV[VZ]);
params.extraRotateAxis.mV[1], params.extraRotateAxis.mV[2]);
} }
// Get the appropriate scale value from mManipulatorScales. // Get the appropriate scale value from mManipulatorScales.
float scaleVal = 1.f; float scaleVal = 1.f;
switch (params.scaleIndex) switch (params.scaleIndex)
{ {
case 0: scaleVal = mManipulatorScales.mV[VX]; break; case 0:
case 1: scaleVal = mManipulatorScales.mV[VY]; break; scaleVal = mManipulatorScales.mV[VX];
case 2: scaleVal = mManipulatorScales.mV[VZ]; break; break;
case 3: scaleVal = mManipulatorScales.mV[VW]; break; case 1:
default: break; scaleVal = mManipulatorScales.mV[VY];
break;
case 2:
scaleVal = mManipulatorScales.mV[VZ];
break;
case 3:
scaleVal = mManipulatorScales.mV[VW];
break;
default:
break;
} }
gGL.scalef(scaleVal, scaleVal, scaleVal); gGL.scalef(scaleVal, scaleVal, scaleVal);
gl_ring(radius, width, params.primaryColor, params.secondaryColor, CIRCLE_STEPS, pass); gl_ring(radius, width, params.primaryColor, params.secondaryColor, CIRCLE_STEPS, pass);
@ -552,7 +559,7 @@ void FSManipRotateJoint::renderManipulatorRings(const LLVector3& agent_space_cen
for (int pass = 0; pass < 2; ++pass) for (int pass = 0; pass < 2; ++pass)
{ {
if( mManipPart == LL_NO_PART || mManipPart == LL_ROT_ROLL || mHighlightedPart == LL_ROT_ROLL) if (mManipPart == LL_NO_PART || mManipPart == LL_ROT_ROLL || mHighlightedPart == LL_ROT_ROLL)
{ {
renderCenterSphere( mRadiusMeters); renderCenterSphere( mRadiusMeters);
for (auto& ring_params : sRingParams) for (auto& ring_params : sRingParams)
@ -565,9 +572,10 @@ void FSManipRotateJoint::renderManipulatorRings(const LLVector3& agent_space_cen
} }
renderRingPass(params, mRadiusMeters, width_meters, pass); renderRingPass(params, mRadiusMeters, width_meters, pass);
} }
if( mManipPart == LL_ROT_ROLL || mHighlightedPart == LL_ROT_ROLL) if( mManipPart == LL_ROT_ROLL || mHighlightedPart == LL_ROT_ROLL)
{ {
static const auto roll_color = LLColor4(1.f,0.65f,0.0f,0.4f); static const auto roll_color = LLColor4(1.f, 0.65f, 0.0f, 0.4f);
updateManipulatorScale(mManipPart, mManipulatorScales); updateManipulatorScale(mManipPart, mManipulatorScales);
gGL.pushMatrix(); gGL.pushMatrix();
{ {
@ -673,7 +681,6 @@ void FSManipRotateJoint::renderCenterSphere(const F32 radius, const LLColor4& no
gGL.popMatrix(); gGL.popMatrix();
} }
/** /**
* @brief Renders the joint rotation manipulator and associated visual elements. * @brief Renders the joint rotation manipulator and associated visual elements.
* *
@ -697,15 +704,12 @@ void FSManipRotateJoint::renderCenterSphere(const F32 radius, const LLColor4& no
*/ */
void FSManipRotateJoint::render() void FSManipRotateJoint::render()
{ {
// Early-out if no joint or avatar. if (!isAvatarJointSafeToUse())
// Needs more something: if they log out while dots on them, asplode
if (!mJoint || !mAvatar || mAvatar->isDead())
{
return; return;
}
// update visibility and rotation center. // update visibility and rotation center.
bool activeJointVisible = updateVisiblity(); bool activeJointVisible = updateVisiblity();
// Setup GL state. // Setup GL state.
LLGLSUIDefault gls_ui; LLGLSUIDefault gls_ui;
gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep); gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep);
@ -715,54 +719,50 @@ void FSManipRotateJoint::render()
// Iterate through the avatar's joint map. // Iterate through the avatar's joint map.
// If a joint other than the currently selected is highlighted, render a pulsing sphere. // If a joint other than the currently selected is highlighted, render a pulsing sphere.
// otherwise a small static sphere // otherwise a small static sphere
LLCachedControl<bool> show_joint_markers(gSavedSettings, "FSManipShowJointMarkers", true);
LLVector3 jointLocation;
for (const auto& entry : getSelectableJoints()) for (const auto& entry : getSelectableJoints())
{ {
LLJoint* joint = mAvatar->getJoint(std::string(entry)); LLJoint* joint = mAvatar->getJoint(std::string(entry));
if (!joint) if (!joint)
continue; continue;
// Update the joint's world matrix to ensure its position is current. // Update the joint's world matrix to ensure its position is current.
joint->updateWorldMatrixParent(); joint->updateWorldMatrixParent();
joint->updateWorldMatrix(); joint->updateWorldMatrix();
if( joint == mHighlightedJoint && joint != mJoint ) if (joint == mJoint)
continue;
jointLocation = joint->getWorldPosition();
if (joint == mHighlightedJoint)
{ {
renderPulsingSphere(joint->getWorldPosition()); renderPulsingSphere(jointLocation);
} continue;
else if( joint != mJoint )
{
// Render a static sphere for the joint being manipulated.
LLCachedControl<bool> show_joint_markers(gSavedSettings, "FSManipShowJointMarkers", true);
if(show_joint_markers)
{
renderStaticSphere(joint->getWorldPosition(), LLColor4(1.f, 0.5f, 0.f, 0.5f), 0.01f);
}
} }
if (show_joint_markers)
renderStaticSphere(jointLocation, LLColor4(1.f, 0.5f, 0.f, 0.5f), 0.01f);
} }
if (!activeJointVisible) if (!activeJointVisible)
{
return; return;
}
// Update joint world matrices.
mJoint->updateWorldMatrixParent();
mJoint->updateWorldMatrix();
const LLQuaternion joint_world_rotation = mJoint->getWorldRotation();
const LLQuaternion joint_world_rotation = getSelectedJointWorldRotation();
const LLQuaternion parentWorldRot = (mJoint->getParent()) ? mJoint->getParent()->getWorldRotation() : LLQuaternion::DEFAULT; const LLQuaternion parentWorldRot = (mJoint->getParent()) ? mJoint->getParent()->getWorldRotation() : LLQuaternion::DEFAULT;
LLQuaternion currentLocalRot = mJoint->getRotation(); LLQuaternion currentLocalRot = mJoint->getRotation();
LLQuaternion rotatedNaturalAlignment = mNaturalAlignmentQuat * currentLocalRot; LLQuaternion rotatedNaturalAlignment = mNaturalAlignmentQuat * currentLocalRot;
// Compute the final world alignment: // Compute the final world alignment:
LLQuaternion final_world_alignment = rotatedNaturalAlignment * parentWorldRot; LLQuaternion final_world_alignment = rotatedNaturalAlignment * parentWorldRot;
const LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(mRotationCenter); const LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(mRotationCenter);
LLCachedControl<bool> use_natural_direction(gSavedSettings, "FSManipRotateJointUseNaturalDirection", true); LLCachedControl<bool> use_natural_direction(gSavedSettings, "FSManipRotateJointUseNaturalDirection", true);
LLQuaternion active_rotation = use_natural_direction? final_world_alignment : joint_world_rotation; LLQuaternion active_rotation = use_natural_direction ? final_world_alignment : joint_world_rotation;
active_rotation.normalize(); active_rotation.normalize();
// Render the manipulator rings in a separate function. // Render the manipulator rings in a separate function.
gGL.matrixMode(LLRender::MM_MODELVIEW); gGL.matrixMode(LLRender::MM_MODELVIEW);
renderAxes(agent_space_center, mRadiusMeters * 1.5f, active_rotation); renderAxes(agent_space_center, mRadiusMeters * 1.5f, active_rotation);
@ -875,9 +875,7 @@ void FSManipRotateJoint::renderNameXYZ(const LLQuaternion& rot)
S32 vertical_offset = window_center_y - VERTICAL_OFFSET; S32 vertical_offset = window_center_y - VERTICAL_OFFSET;
LLVector3 euler_angles; LLVector3 euler_angles;
rot.getEulerAngles(&euler_angles.mV[0], rot.getEulerAngles(&euler_angles.mV[VX], &euler_angles.mV[VY], &euler_angles.mV[VZ]);
&euler_angles.mV[1],
&euler_angles.mV[2]);
euler_angles *= RAD_TO_DEG; euler_angles *= RAD_TO_DEG;
for (S32 i = 0; i < 3; ++i) for (S32 i = 0; i < 3; ++i)
{ {
@ -925,15 +923,6 @@ void FSManipRotateJoint::renderNameXYZ(const LLQuaternion& rot)
base_y += 20.f; base_y += 20.f;
renderTextWithShadow(llformat("Joint: %s", mJoint->getName().c_str()), window_center_x - 130.f, base_y, LLColor4(1.f, 0.1f, 1.f, 1.f)); renderTextWithShadow(llformat("Joint: %s", mJoint->getName().c_str()), window_center_x - 130.f, base_y, LLColor4(1.f, 0.1f, 1.f, 1.f));
renderTextWithShadow(llformat("Manip: %s%c", getManipPartString(mManipPart).c_str(), mCamEdgeOn?'*':' '), window_center_x + 30.f, base_y, LLColor4(1.f, 1.f, .1f, 1.f)); renderTextWithShadow(llformat("Manip: %s%c", getManipPartString(mManipPart).c_str(), mCamEdgeOn?'*':' '), window_center_x + 30.f, base_y, LLColor4(1.f, 1.f, .1f, 1.f));
if (mManipPart != LL_NO_PART)
{
LL_INFOS("FSManipRotateJoint") << "Joint: " << mJoint->getName()
<< ", Manip: " << getManipPartString(mManipPart)
<< ", Quaternion: " << rot
<< ", Euler Angles: " << mLastEuler
<< ", Delta Angle: " << mLastAngle * RAD_TO_DEG
<< LL_ENDL;
}
} }
gGL.popMatrix(); gGL.popMatrix();
@ -954,26 +943,24 @@ void FSManipRotateJoint::renderActiveRing( F32 radius, F32 width, const LLColor4
} }
} }
// ------------------------------------- // -------------------------------------
// Overriding because the base uses mObjectSelection->getFirstMoveableObject(true) // Overriding because the base uses mObjectSelection->getFirstMoveableObject(true)
// Not sure we use it though...TBC (see mouse down on part instead) // Not sure we use it though...TBC (see mouse down on part instead)
bool FSManipRotateJoint::handleMouseDown(S32 x, S32 y, MASK mask) bool FSManipRotateJoint::handleMouseDown(S32 x, S32 y, MASK mask)
{ {
if (!mJoint) if (!isAvatarJointSafeToUse())
{
return false; return false;
}
// Highlight the manipulator as before. // Highlight the manipulator as before.
highlightManipulators(x, y); highlightManipulators(x, y);
if (mHighlightedPart != LL_NO_PART) if (mHighlightedPart == LL_NO_PART)
{ return false;
mManipPart = (EManipPart)mHighlightedPart; mManipPart = (EManipPart)mHighlightedPart;
// Get the joint's center in agent space. // Get the joint's center in agent space.
LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(mRotationCenter); LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(mRotationCenter);
// Use the existing function to get the intersection point. // Use the existing function to get the intersection point.
LLVector3 intersection = intersectMouseWithSphere(x, y, agent_space_center, mRadiusMeters); LLVector3 intersection = intersectMouseWithSphere(x, y, agent_space_center, mRadiusMeters);
@ -989,13 +976,13 @@ bool FSManipRotateJoint::handleMouseDown(S32 x, S32 y, MASK mask)
// Save the valid intersection point. // Save the valid intersection point.
mInitialIntersection = intersection; mInitialIntersection = intersection;
// Also store the joint's current rotation. // Also store the joint's current rotation.
mSavedJointRot = mJoint->getWorldRotation(); mSavedJointRot = getSelectedJointWorldRotation();
// Capture the mouse for dragging. // Capture the mouse for dragging.
setMouseCapture(true); setMouseCapture(true);
return true; return true;
} }
}
return false; return false;
} }
@ -1017,24 +1004,27 @@ bool FSManipRotateJoint::handleMouseDown(S32 x, S32 y, MASK mask)
*/ */
bool FSManipRotateJoint::handleMouseDownOnPart(S32 x, S32 y, MASK mask) bool FSManipRotateJoint::handleMouseDownOnPart(S32 x, S32 y, MASK mask)
{ {
auto * poser = dynamic_cast<FSFloaterPoser*>(LLFloaterReg::findInstance("fs_poser")); // For joint manipulation, require both a valid joint and avatar.
if (!isAvatarJointSafeToUse())
return false;
auto* poser = LLFloaterReg::findTypedInstance<FSFloaterPoser>("fs_poser");
if (!poser)
return false;
// Determine which ring (axis) is under the mouse, also highlights selectable joints. // Determine which ring (axis) is under the mouse, also highlights selectable joints.
highlightManipulators(x, y); highlightManipulators(x, y);
// For joint manipulation, require both a valid joint and avatar.
if (!mJoint || !mAvatar || mAvatar->isDead() || !poser)
{
return false;
}
poser->setFocus(true); poser->setFocus(true);
S32 hit_part = mHighlightedPart; S32 hit_part = mHighlightedPart;
// Save the joints current world rotation as the basis for the drag. // Save the joints current world rotation as the basis for the drag.
mSavedJointRot = mJoint->getWorldRotation(); mSavedJointRot = getSelectedJointWorldRotation();
mLastSetRotation.set(LLQuaternion());
mManipPart = (EManipPart)hit_part; mManipPart = (EManipPart)hit_part;
// Convert rotation center from global to agent space. // Convert rotation center from global to agent space.
LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(mRotationCenter); LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(mRotationCenter);
// based on mManipPArt (set in highlightmanipulators). decide whether we are constrained or not in the rotation // based on mManipPArt (set in highlightmanipulators). decide whether we are constrained or not in the rotation
if (mManipPart == LL_ROT_GENERAL) if (mManipPart == LL_ROT_GENERAL)
@ -1098,35 +1088,27 @@ bool FSManipRotateJoint::handleMouseDownOnPart(S32 x, S32 y, MASK mask)
// We use mouseUp to update the UI, updating it during the drag is too slow. // We use mouseUp to update the UI, updating it during the drag is too slow.
bool FSManipRotateJoint::handleMouseUp(S32 x, S32 y, MASK mask) bool FSManipRotateJoint::handleMouseUp(S32 x, S32 y, MASK mask)
{ {
auto * poser = dynamic_cast<FSFloaterPoser*>(LLFloaterReg::findInstance("fs_poser"));
if (hasMouseCapture()) if (hasMouseCapture())
{ {
// Update the UI, by causing it to read back the position of the selected joints and aply those relative to the base rot
if (poser && mJoint)
{
poser->updatePosedBones(mJoint->getName());
}
// Release mouse
setMouseCapture(false); setMouseCapture(false);
mManipPart = LL_NO_PART; mManipPart = LL_NO_PART;
mLastAngle = 0.0f; mLastAngle = 0.0f;
mCamEdgeOn = false; mCamEdgeOn = false;
return true; return true;
} }
else if(mHighlightedJoint) else if (mHighlightedJoint)
{ {
auto* poser = dynamic_cast<FSFloaterPoser*>(LLFloaterReg::findInstance("fs_poser"));
if (poser) if (poser)
{
poser->selectJointByName(mHighlightedJoint->getName()); poser->selectJointByName(mHighlightedJoint->getName());
}
return true; return true;
} }
return false; return false;
} }
/** /**
* @brief Does all the hard work of working out what inworld control we are interacting with * @brief Does all the hard work of working out what inworld control we are interacting with
* *
@ -1143,7 +1125,7 @@ void FSManipRotateJoint::highlightManipulators(S32 x, S32 y)
mHighlightedPart = LL_NO_PART; mHighlightedPart = LL_NO_PART;
// Instead of using mObjectSelection->getFirstMoveableObject(), // Instead of using mObjectSelection->getFirstMoveableObject(),
// simply require that the joint (and the avatar) is valid. // simply require that the joint (and the avatar) is valid.
if (!mJoint || !mAvatar || mAvatar->isDead()) if (!isAvatarJointSafeToUse())
{ {
highlightHoverSpheres(x, y); highlightHoverSpheres(x, y);
gViewerWindow->setCursor(UI_CURSOR_ARROW); gViewerWindow->setCursor(UI_CURSOR_ARROW);
@ -1153,13 +1135,13 @@ void FSManipRotateJoint::highlightManipulators(S32 x, S32 y)
// Decide which rotation to use based on a user toggle. // Decide which rotation to use based on a user toggle.
LLCachedControl<bool> use_natural_direction(gSavedSettings, "FSManipRotateJointUseNaturalDirection", true); LLCachedControl<bool> use_natural_direction(gSavedSettings, "FSManipRotateJointUseNaturalDirection", true);
// Compute the rotation center in agent space. // Compute the rotation center in agent space.
LLVector3 agent_space_rotation_center = gAgent.getPosAgentFromGlobal(mRotationCenter); LLVector3 agent_space_rotation_center = mAvatar->getPosAgentFromGlobal(mRotationCenter);
// Update joint world matrices. // Update joint world matrices.
mJoint->updateWorldMatrixParent(); mJoint->updateWorldMatrixParent();
mJoint->updateWorldMatrix(); mJoint->updateWorldMatrix();
const LLQuaternion joint_world_rotation = mJoint->getWorldRotation(); const LLQuaternion joint_world_rotation = getSelectedJointWorldRotation();
const LLQuaternion parentWorldRot = (mJoint->getParent()) ? mJoint->getParent()->getWorldRotation() : LLQuaternion::DEFAULT; const LLQuaternion parentWorldRot = (mJoint->getParent()) ? mJoint->getParent()->getWorldRotation() : LLQuaternion::DEFAULT;
@ -1171,7 +1153,6 @@ void FSManipRotateJoint::highlightManipulators(S32 x, S32 y)
LLQuaternion final_world_alignment = rotatedNaturalAlignment * parentWorldRot; LLQuaternion final_world_alignment = rotatedNaturalAlignment * parentWorldRot;
final_world_alignment.normalize(); final_world_alignment.normalize();
LLQuaternion joint_rot = use_natural_direction ? final_world_alignment : joint_world_rotation; LLQuaternion joint_rot = use_natural_direction ? final_world_alignment : joint_world_rotation;
// Compute the three local axes in world space. // Compute the three local axes in world space.
@ -1308,14 +1289,18 @@ bool FSManipRotateJoint::handleHover(S32 x, S32 y, MASK mask)
{ {
highlightManipulators(x, y); highlightManipulators(x, y);
} }
return true; return true;
} }
LLQuaternion FSManipRotateJoint::dragUnconstrained(S32 x, S32 y) LLQuaternion FSManipRotateJoint::dragUnconstrained(S32 x, S32 y)
{ {
if (!isAvatarJointSafeToUse())
return LLQuaternion();
// Get the camera position and the joints pivot (in agent space) // Get the camera position and the joints pivot (in agent space)
LLVector3 cam = gAgentCamera.getCameraPositionAgent(); LLVector3 cam = gAgentCamera.getCameraPositionAgent();
LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(mRotationCenter); LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(mRotationCenter);
// Compute the current intersection on the sphere. // Compute the current intersection on the sphere.
mMouseCur = intersectMouseWithSphere(x, y, agent_space_center, mRadiusMeters); mMouseCur = intersectMouseWithSphere(x, y, agent_space_center, mRadiusMeters);
@ -1388,10 +1373,7 @@ static LLQuaternion extractTwist(const LLQuaternion& rot, const LLVector3& axis)
LLVector3 proj = axis * dot; // proj is now purely along 'axis' LLVector3 proj = axis * dot; // proj is now purely along 'axis'
// Build the “twist” quaternion from (proj, w), then renormalize // Build the “twist” quaternion from (proj, w), then renormalize
LLQuaternion twist(proj.mV[VX], LLQuaternion twist(proj.mV[VX], proj.mV[VY], proj.mV[VZ], w);
proj.mV[VY],
proj.mV[VZ],
w);
if (w < 0.f) if (w < 0.f)
{ {
twist = -twist; twist = -twist;
@ -1399,16 +1381,21 @@ static LLQuaternion extractTwist(const LLQuaternion& rot, const LLVector3& axis)
twist.normalize(); twist.normalize();
return twist; return twist;
} }
LLQuaternion FSManipRotateJoint::dragConstrained(S32 x, S32 y) LLQuaternion FSManipRotateJoint::dragConstrained(S32 x, S32 y)
{ {
if (!isAvatarJointSafeToUse())
return LLQuaternion();
// Get the constraint axis from our joint manipulator. // Get the constraint axis from our joint manipulator.
LLVector3 constraint_axis = getConstraintAxis(); LLVector3 constraint_axis = getConstraintAxis();
LLVector3 agent_space_center = gAgent.getPosAgentFromGlobal(mRotationCenter); LLVector3 agent_space_center = mAvatar->getPosAgentFromGlobal(mRotationCenter);
if (mCamEdgeOn) if (mCamEdgeOn)
{ {
LLQuaternion freeRot = dragUnconstrained(x, y); LLQuaternion freeRot = dragUnconstrained(x, y);
return extractTwist(freeRot, constraint_axis); return extractTwist(freeRot, constraint_axis);
} }
// Project the current mouse position onto the plane defined by the constraint axis. // Project the current mouse position onto the plane defined by the constraint axis.
LLVector3 projected_mouse; LLVector3 projected_mouse;
bool hit = getMousePointOnPlaneAgent(projected_mouse, x, y, agent_space_center, constraint_axis); bool hit = getMousePointOnPlaneAgent(projected_mouse, x, y, agent_space_center, constraint_axis);
@ -1416,6 +1403,7 @@ LLQuaternion FSManipRotateJoint::dragConstrained(S32 x, S32 y)
{ {
return LLQuaternion::DEFAULT; return LLQuaternion::DEFAULT;
} }
projected_mouse -= agent_space_center; projected_mouse -= agent_space_center;
projected_mouse.normalize(); projected_mouse.normalize();
@ -1424,6 +1412,7 @@ LLQuaternion FSManipRotateJoint::dragConstrained(S32 x, S32 y)
initial_proj -= (initial_proj * constraint_axis) * constraint_axis; initial_proj -= (initial_proj * constraint_axis) * constraint_axis;
initial_proj.normalize(); initial_proj.normalize();
//float angle = acos(initial_proj * projected_mouse); // angle in (-pi, pi)
// Compute the signed angle using atan2. // Compute the signed angle using atan2.
// The numerator is the magnitude of the cross product projected along the constraint axis. // The numerator is the magnitude of the cross product projected along the constraint axis.
float numerator = (initial_proj % projected_mouse) * constraint_axis; float numerator = (initial_proj % projected_mouse) * constraint_axis;
@ -1431,14 +1420,16 @@ LLQuaternion FSManipRotateJoint::dragConstrained(S32 x, S32 y)
float denominator = initial_proj * projected_mouse; float denominator = initial_proj * projected_mouse;
float angle = atan2(numerator, denominator); // angle in (-pi, pi) float angle = atan2(numerator, denominator); // angle in (-pi, pi)
mLastAngle = angle; mLastAngle = angle;
return LLQuaternion(angle, constraint_axis); return LLQuaternion(angle, constraint_axis);
} }
void FSManipRotateJoint::drag(S32 x, S32 y) void FSManipRotateJoint::drag(S32 x, S32 y)
{ {
if (!updateVisiblity() || !mJoint) return; if (!updateVisiblity())
return;
LLQuaternion delta_rot; LLQuaternion delta_send, delta_rot;
if (mManipPart == LL_ROT_GENERAL) if (mManipPart == LL_ROT_GENERAL)
{ {
delta_rot = dragUnconstrained(x, y); delta_rot = dragUnconstrained(x, y);
@ -1448,9 +1439,30 @@ void FSManipRotateJoint::drag(S32 x, S32 y)
delta_rot = dragConstrained(x, y); delta_rot = dragConstrained(x, y);
} }
// Compose the saved joint rotation with the delta to compute the new world rotation. delta_send.set(delta_rot);
LLQuaternion new_world_rot = mSavedJointRot * delta_rot; delta_send *= ~mLastSetRotation;
mJoint->setWorldRotation(new_world_rot); mLastSetRotation.set(delta_rot);
delta_send = mSavedJointRot * delta_send * ~mSavedJointRot;
switch (mReferenceFrame)
{
case POSER_FRAME_CAMERA:
case POSER_FRAME_AVATAR:
delta_send.conjugate();
break;
case POSER_FRAME_WORLD:
delta_send.mQ[VX] *= -1;
break;
case POSER_FRAME_BONE:
default:
break;
}
auto* poser = LLFloaterReg::findTypedInstance<FSFloaterPoser>("fs_poser");
if (poser && mJoint)
poser->updatePosedBones(mJoint->getName(), delta_send, LLVector3::zero, LLVector3::zero);
} }
// set mConstrainedAxis based on mManipParat and returns it too. // set mConstrainedAxis based on mManipParat and returns it too.
@ -1493,12 +1505,41 @@ LLVector3 FSManipRotateJoint::setConstraintAxis()
} }
else else
{ {
active_rotation = mJoint->getWorldRotation(); active_rotation = getSelectedJointWorldRotation();
} }
axis = axis * active_rotation; axis = axis * active_rotation;
axis.normalize(); axis.normalize();
} }
} }
mConstraintAxis = axis; mConstraintAxis = axis;
return axis; return axis;
} }
LLQuaternion FSManipRotateJoint::getSelectedJointWorldRotation()
{
LLQuaternion joinRot;
if (!mJoint || !mAvatar)
return joinRot;
auto* poser = dynamic_cast<FSFloaterPoser*>(LLFloaterReg::findInstance("fs_poser"));
if (!poser)
return joinRot;
return poser->getManipGimbalRotation(mJoint->getName());
}
bool FSManipRotateJoint::isAvatarJointSafeToUse()
{
if (!mJoint || !mAvatar)
return false;
if (mAvatar->isDead() || !mAvatar->isFullyLoaded())
{
setAvatar(nullptr);
return false;
}
return true;
}

View File

@ -1,5 +1,5 @@
/** /**
* @file fsmaniproatejoint.h * @file fsmaniprotatejoint.h
* @brief custom manipulator for rotating joints * @brief custom manipulator for rotating joints
* *
* $LicenseInfo:firstyear=2024&license=viewerlgpl$ * $LicenseInfo:firstyear=2024&license=viewerlgpl$
@ -32,7 +32,18 @@
#include "llmaniprotate.h" #include "llmaniprotate.h"
class LLJoint; class LLJoint;
class LLVOAvatar; // or LLVOAvatarSelf, etc. class LLVOAvatar; // for LLVOAvatarSelf, etc.
/// <summary>
/// A set of reference frames for presenting the gimbal within.
/// </summary>
typedef enum E_PoserReferenceFrame
{
POSER_FRAME_BONE = 0, // frame is the bone the gimbal is centered on
POSER_FRAME_WORLD = 1, // frame is world North-South-East-West-Up-Down
POSER_FRAME_AVATAR = 2, // frame is mPelvis rotation
POSER_FRAME_CAMERA = 3, // frame is defined by vector of camera position to bone position
} E_PoserReferenceFrame;
namespace { namespace {
const F32 AXIS_ONTO_CAM_TOLERANCE = cos( 85.f * DEG_TO_RAD ); // cos() is not constexpr til c++26 const F32 AXIS_ONTO_CAM_TOLERANCE = cos( 85.f * DEG_TO_RAD ); // cos() is not constexpr til c++26
@ -74,11 +85,25 @@ public:
FSManipRotateJoint(LLToolComposite* composite); FSManipRotateJoint(LLToolComposite* composite);
virtual ~FSManipRotateJoint() {} virtual ~FSManipRotateJoint() {}
static std::string getManipPartString(EManipPart part); static std::string getManipPartString(EManipPart part);
// Called to designate which joint we are going to manipulate.
/// <summary>
/// Sets the joint we are going to manipulate.
/// </summary>
/// <param name="joint">The joint to interact with.</param>
void setJoint(LLJoint* joint); void setJoint(LLJoint* joint);
/// <summary>
/// Sets the avatar the manip should interact with.
/// </summary>
/// <param name="avatar">The avatar to interact with.</param>
void setAvatar(LLVOAvatar* avatar); void setAvatar(LLVOAvatar* avatar);
/// <summary>
/// Sets the reference frame for the manipulator.
/// </summary>
/// <param name="avatar">The E_PoserReferenceFrame to use.</param>
void setReferenceFrame(const E_PoserReferenceFrame frame) { mReferenceFrame = frame; };
// Overrides // Overrides
void handleSelect() override; void handleSelect() override;
bool updateVisiblity() override; bool updateVisiblity() override;
@ -138,8 +163,13 @@ private:
void renderRingPass(const RingRenderParams& params, float radius, float width, int pass); void renderRingPass(const RingRenderParams& params, float radius, float width, int pass);
void renderAxes(const LLVector3& center, F32 size, const LLQuaternion& rotation); void renderAxes(const LLVector3& center, F32 size, const LLQuaternion& rotation);
bool isAvatarJointSafeToUse();
LLQuaternion getSelectedJointWorldRotation();
float mLastAngle = 0.f; float mLastAngle = 0.f;
LLVector3 mConstraintAxis; LLVector3 mConstraintAxis;
E_PoserReferenceFrame mReferenceFrame = POSER_FRAME_BONE;
LLQuaternion mLastSetRotation;
}; };
#endif // FS_MANIP_ROTATE_JOINT_H #endif // FS_MANIP_ROTATE_JOINT_H

View File

@ -32,6 +32,8 @@
#include "fsparticipantlist.h" #include "fsparticipantlist.h"
#include "llagent.h" #include "llagent.h"
#include "llchatentry.h"
#include "fsfloaterim.h"
#include "llimview.h" #include "llimview.h"
#include "llspeakers.h" #include "llspeakers.h"
@ -88,7 +90,16 @@ void FSPanelGroupControlPanel::setSessionId(const LLUUID& session_id)
return; return;
mParticipantList = new FSParticipantList(speaker_manager, getChild<LLAvatarList>("grp_speakers_list"), true,false); mParticipantList = new FSParticipantList(speaker_manager, getChild<LLAvatarList>("grp_speakers_list"), true,false);
if (mParticipantList)
{
mParticipantList->setInsertMentionCallback(boost::bind(&FSPanelGroupControlPanel::insertMentionAtCursor, this, _1));
} }
}
}
void FSPanelGroupControlPanel::insertMentionAtCursor(const LLUUID& avatar_id)
{
FSFloaterIM::getInstance(getSessionId())->findChild<LLChatEntry>("chat_editor")->insertMentionAtCursor("secondlife:///app/agent/" + avatar_id.asString() + "/mention");
} }
uuid_vec_t FSPanelGroupControlPanel::getParticipants() const uuid_vec_t FSPanelGroupControlPanel::getParticipants() const

View File

@ -69,6 +69,7 @@ public:
void draw() override; void draw() override;
uuid_vec_t getParticipants() const override; uuid_vec_t getParticipants() const override;
void insertMentionAtCursor(const LLUUID& url);
protected: protected:
LLUUID mGroupID; LLUUID mGroupID;

View File

@ -929,6 +929,23 @@ void FSPanelLogin::loadLoginPage()
params["noversionpopup"] = "true"; params["noversionpopup"] = "true";
} }
// Splash screen settings
static const std::pair<std::string, std::string> mappings[] = {
{"FSSplashScreenHideTopBar", "hidetopbar"},
{"FSSplashScreenHideBlogs", "hideblogs"},
{"FSSplashScreenHideDestinations", "hidedestinations"},
{"FSSplashScreenUseGrayMode", "usegraymode"},
{"FSSplashScreenUseHighContrast", "usehighcontrast"},
{"FSSplashScreenUseAllCaps", "useallcaps"},
{"FSSplashScreenUseLargerFonts", "uselargerfonts"},
{"FSSplashScreenNoTransparency", "notransparency"},
};
for (const auto &m : mappings)
{
params[m.second] = gSavedSettings.getBOOL(m.first) ? "1" : "0";
}
// Make an LLURI with this augmented info // Make an LLURI with this augmented info
std::string url = login_page.scheme().empty()? login_page.authority() : login_page.scheme() + "://" + login_page.authority(); std::string url = login_page.scheme().empty()? login_page.authority() : login_page.scheme() + "://" + login_page.authority();
LLURI login_uri(LLURI::buildHTTP(url, LLURI login_uri(LLURI::buildHTTP(url,

View File

@ -39,6 +39,7 @@
#include "llnotificationsutil.h" #include "llnotificationsutil.h"
#include "lloutputmonitorctrl.h" #include "lloutputmonitorctrl.h"
#include "llspeakers.h" #include "llspeakers.h"
#include "llurlaction.h"
#include "llviewercontrol.h" #include "llviewercontrol.h"
#include "llviewermenu.h" #include "llviewermenu.h"
#include "llvoiceclient.h" #include "llvoiceclient.h"
@ -69,6 +70,7 @@ FSParticipantList::FSParticipantList(LLSpeakerMgr* data_source,
mParticipantListMenu(NULL), mParticipantListMenu(NULL),
mExcludeAgent(exclude_agent), mExcludeAgent(exclude_agent),
mValidateSpeakerCallback(NULL), mValidateSpeakerCallback(NULL),
mInsertMentionCallback(NULL),
mConvType(CONV_UNKNOWN) mConvType(CONV_UNKNOWN)
{ {
mSpeakerAddListener = new SpeakerAddListener(*this); mSpeakerAddListener = new SpeakerAddListener(*this);
@ -301,6 +303,11 @@ void FSParticipantList::setValidateSpeakerCallback(validate_speaker_callback_t c
mValidateSpeakerCallback = cb; mValidateSpeakerCallback = cb;
} }
void FSParticipantList::setInsertMentionCallback(insert_mention_callback_t cb)
{
mInsertMentionCallback = cb;
}
void FSParticipantList::update() void FSParticipantList::update()
{ {
mSpeakerMgr->update(true); mSpeakerMgr->update(true);
@ -530,6 +537,9 @@ LLContextMenu* FSParticipantList::FSParticipantListMenu::createMenu()
registrar.add("ParticipantList.ModerateVoice", boost::bind(&FSParticipantList::FSParticipantListMenu::moderateVoice, this, _2)); registrar.add("ParticipantList.ModerateVoice", boost::bind(&FSParticipantList::FSParticipantListMenu::moderateVoice, this, _2));
registrar.add("Mention.CopyURI", boost::bind(&FSParticipantList::FSParticipantListMenu::copyURLToClipboard, this, mUUIDs.front()));
registrar.add("Mention.Chat", boost::bind(&FSParticipantList::FSParticipantListMenu::insertMentionAtCursor, this, mUUIDs.front()));
enable_registrar.add("ParticipantList.EnableItem", boost::bind(&FSParticipantList::FSParticipantListMenu::enableContextMenuItem, this, _2)); enable_registrar.add("ParticipantList.EnableItem", boost::bind(&FSParticipantList::FSParticipantListMenu::enableContextMenuItem, this, _2));
enable_registrar.add("ParticipantList.EnableItem.Moderate", boost::bind(&FSParticipantList::FSParticipantListMenu::enableModerateContextMenuItem, this, _2)); enable_registrar.add("ParticipantList.EnableItem.Moderate", boost::bind(&FSParticipantList::FSParticipantListMenu::enableModerateContextMenuItem, this, _2));
enable_registrar.add("ParticipantList.CheckItem", boost::bind(&FSParticipantList::FSParticipantListMenu::checkContextMenuItem, this, _2)); enable_registrar.add("ParticipantList.CheckItem", boost::bind(&FSParticipantList::FSParticipantListMenu::checkContextMenuItem, this, _2));
@ -556,6 +566,19 @@ LLContextMenu* FSParticipantList::FSParticipantListMenu::createMenu()
return main_menu; return main_menu;
} }
void FSParticipantList::FSParticipantListMenu::copyURLToClipboard(const LLUUID& avatar_id)
{
LLUrlAction::copyURLToClipboard("secondlife:///app/agent/" + mUUIDs.front().asString() + "/mention");
}
void FSParticipantList::FSParticipantListMenu::insertMentionAtCursor(const LLUUID& avatar_id)
{
if (mParent.mInsertMentionCallback)
{
mParent.mInsertMentionCallback(avatar_id);
}
}
void FSParticipantList::FSParticipantListMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y) void FSParticipantList::FSParticipantListMenu::show(LLView* spawning_view, const uuid_vec_t& uuids, S32 x, S32 y)
{ {
if (uuids.size() == 0) return; if (uuids.size() == 0) return;

View File

@ -53,6 +53,7 @@ public:
}; };
typedef boost::function<bool (const LLUUID& speaker_id)> validate_speaker_callback_t; typedef boost::function<bool (const LLUUID& speaker_id)> validate_speaker_callback_t;
typedef boost::function<void (const LLUUID& speaker_id)> insert_mention_callback_t;
FSParticipantList(LLSpeakerMgr* data_source, FSParticipantList(LLSpeakerMgr* data_source,
LLAvatarList* avatar_list, LLAvatarList* avatar_list,
@ -94,6 +95,7 @@ public:
* @see onAddItemEvent() * @see onAddItemEvent()
*/ */
void setValidateSpeakerCallback(validate_speaker_callback_t cb); void setValidateSpeakerCallback(validate_speaker_callback_t cb);
void setInsertMentionCallback(insert_mention_callback_t cb);
EConversationType const getType() const { return mConvType; } EConversationType const getType() const { return mConvType; }
@ -240,6 +242,10 @@ protected:
static void confirmMuteAllCallback(const LLSD& notification, const LLSD& response); static void confirmMuteAllCallback(const LLSD& notification, const LLSD& response);
void handleAddToContactSet(); void handleAddToContactSet();
// mentions support
void copyURLToClipboard(const LLUUID& avatar_id);
void insertMentionAtCursor(const LLUUID& avatar_id);
}; };
/** /**
@ -300,6 +306,8 @@ private:
LLPointer<LLAvatarItemRecentSpeakerComparator> mSortByRecentSpeakers; LLPointer<LLAvatarItemRecentSpeakerComparator> mSortByRecentSpeakers;
validate_speaker_callback_t mValidateSpeakerCallback; validate_speaker_callback_t mValidateSpeakerCallback;
insert_mention_callback_t mInsertMentionCallback;
EConversationType mConvType; EConversationType mConvType;
}; };

View File

@ -29,6 +29,7 @@
#include "fsposeranimator.h" #include "fsposeranimator.h"
#include "llcharacter.h" #include "llcharacter.h"
#include "llagent.h" #include "llagent.h"
#include "llagentcamera.h"
#include "fsposingmotion.h" #include "fsposingmotion.h"
std::map<LLUUID, LLAssetID> FSPoserAnimator::sAvatarIdToRegisteredAnimationId; std::map<LLUUID, LLAssetID> FSPoserAnimator::sAvatarIdToRegisteredAnimationId;
@ -52,6 +53,25 @@ bool FSPoserAnimator::isPosingAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint
return posingMotion->currentlyPosingJoint(jointPose); return posingMotion->currentlyPosingJoint(jointPose);
} }
bool FSPoserAnimator::hasJointBeenChanged(LLVOAvatar* avatar, const FSPoserJoint& joint)
{
if (!isAvatarSafeToUse(avatar))
return false;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return false;
if (posingMotion->isStopped())
return false;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose)
return false;
return jointPose->getJointModified();
}
void FSPoserAnimator::setPosingAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, bool shouldPose) void FSPoserAnimator::setPosingAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, bool shouldPose)
{ {
if (!isAvatarSafeToUse(avatar)) if (!isAvatarSafeToUse(avatar))
@ -212,7 +232,8 @@ LLVector3 FSPoserAnimator::getJointPosition(LLVOAvatar* avatar, const FSPoserJoi
return jointPose->getPublicPosition(); return jointPose->getPublicPosition();
} }
void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& position, E_BoneDeflectionStyles style) void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& position, E_PoserReferenceFrame frame,
E_BoneDeflectionStyles style)
{ {
if (!isAvatarSafeToUse(avatar)) if (!isAvatarSafeToUse(avatar))
return; return;
@ -231,7 +252,8 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j
if (!jointPose) if (!jointPose)
return; return;
LLVector3 positionDelta = jointPose->getPublicPosition() - position; LLVector3 jointPosition = jointPose->getPublicPosition();
LLVector3 positionDelta = jointPosition - position;
switch (style) switch (style)
{ {
@ -239,13 +261,13 @@ void FSPoserAnimator::setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* j
case MIRROR_DELTA: case MIRROR_DELTA:
case SYMPATHETIC_DELTA: case SYMPATHETIC_DELTA:
case SYMPATHETIC: case SYMPATHETIC:
jointPose->setPublicPosition(position); jointPose->setPublicPosition(jointPosition - positionDelta);
break; break;
case DELTAMODE: case DELTAMODE:
case NONE: case NONE:
default: default:
jointPose->setPublicPosition(position); jointPose->setPublicPosition(jointPosition - positionDelta);
return; return;
} }
@ -406,6 +428,7 @@ void FSPoserAnimator::setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar)
return; return;
posingMotion->setAllRotationsToZeroAndClearUndo(); posingMotion->setAllRotationsToZeroAndClearUndo();
mPosingState.purgeMotionStates(avatar);
for (size_t index = 0; index != PoserJoints.size(); ++index) for (size_t index = 0; index != PoserJoints.size(); ++index)
{ {
@ -420,10 +443,9 @@ void FSPoserAnimator::setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar)
posingMotion->setJointBvhLock(jointPose, false); posingMotion->setJointBvhLock(jointPose, false);
} }
} }
void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation) void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint)
{ {
if (!isAvatarSafeToUse(avatar)) if (!isAvatarSafeToUse(avatar))
return; return;
@ -440,8 +462,9 @@ void FSPoserAnimator::recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joi
setPosingAvatarJoint(avatar, joint, true); setPosingAvatarJoint(avatar, joint, true);
} }
void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero, void FSPoserAnimator::updateJointFromManip(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero,
E_BoneDeflectionStyles style) E_BoneDeflectionStyles style, E_PoserReferenceFrame frame, const LLQuaternion& rotation,
const LLVector3& position, const LLVector3& scale)
{ {
if (!isAvatarSafeToUse(avatar)) if (!isAvatarSafeToUse(avatar))
return; return;
@ -454,27 +477,34 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi
if (!jointPose) if (!jointPose)
return; return;
LLQuaternion deltaRot = jointPose->recaptureJointAsDelta(resetBaseRotationToZero); LLQuaternion framedRotation = changeToRotationFrame(avatar, rotation, frame, jointPose);
jointPose->setPublicRotation(resetBaseRotationToZero, framedRotation * jointPose->getPublicRotation());
deRotateWorldLockedDescendants(joint, posingMotion, deltaRot); deRotateWorldLockedDescendants(joint, posingMotion, framedRotation);
if (style == NONE || style == DELTAMODE) if (style == NONE || style == DELTAMODE)
return; return;
auto oppositePoserJoint = getPoserJointByName(joint->mirrorJointName());
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName()); FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
if (!oppositeJointPose) if (!oppositeJointPose)
return; return;
LLQuaternion mirroredRotation = LLQuaternion(-framedRotation.mQ[VX], framedRotation.mQ[VY], -framedRotation.mQ[VZ], framedRotation.mQ[VW]);
switch (style) switch (style)
{ {
case SYMPATHETIC: case SYMPATHETIC:
case SYMPATHETIC_DELTA: case SYMPATHETIC_DELTA:
oppositeJointPose->cloneRotationFrom(jointPose); oppositeJointPose->setPublicRotation(resetBaseRotationToZero, framedRotation * oppositeJointPose->getPublicRotation());
if (oppositePoserJoint)
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, framedRotation);
break; break;
case MIRROR: case MIRROR:
case MIRROR_DELTA: case MIRROR_DELTA:
oppositeJointPose->mirrorRotationFrom(jointPose); oppositeJointPose->setPublicRotation(resetBaseRotationToZero, mirroredRotation * oppositeJointPose->getPublicRotation());
if (oppositePoserJoint)
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, mirroredRotation);
break; break;
default: default:
@ -482,6 +512,54 @@ void FSPoserAnimator::recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoi
} }
} }
LLQuaternion FSPoserAnimator::getManipGimbalRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, E_PoserReferenceFrame frame)
{
LLQuaternion globalRot(-1.f, 0.f, 0.f, 0.f);
if (frame == POSER_FRAME_WORLD)
return globalRot;
if (!joint)
return globalRot;
if (!isAvatarSafeToUse(avatar))
return globalRot;
if (frame == POSER_FRAME_AVATAR)
{
LLJoint* pelvis = avatar->getJoint("mPelvis");
if (pelvis)
return pelvis->getWorldRotation();
return globalRot;
}
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return globalRot;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint->jointName());
if (!jointPose)
return globalRot;
LLJoint* llJoint = jointPose->getJointState()->getJoint();
if (!llJoint)
return globalRot;
if (frame == POSER_FRAME_BONE)
return llJoint->getWorldRotation();
LLVector3 skyward(0.f, 0.f, 1.f);
LLVector3 left(1.f, 0.f, 0.f);
LLVector3 up, jointToCameraPosition, jointPosition;
jointPosition = llJoint->getWorldPosition();
jointToCameraPosition = jointPosition - gAgentCamera.getCameraPositionAgent();
jointToCameraPosition.normalize();
left.setVec(skyward % jointToCameraPosition);
up.setVec(jointToCameraPosition % left);
return LLQuaternion(jointToCameraPosition, left, up);
}
LLVector3 FSPoserAnimator::getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, bool lockWholeAvatar) const LLVector3 FSPoserAnimator::getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, bool lockWholeAvatar) const
{ {
auto rotation = getJointRotation(avatar, joint, SWAP_NOTHING, NEGATE_NOTHING); auto rotation = getJointRotation(avatar, joint, SWAP_NOTHING, NEGATE_NOTHING);
@ -525,12 +603,13 @@ LLVector3 FSPoserAnimator::getJointRotation(LLVOAvatar* avatar, const FSPoserJoi
if (!jointPose) if (!jointPose)
return vec3; return vec3;
return translateRotationFromQuaternion(translation, negation, jointPose->getPublicRotation()); return translateRotationFromQuaternion(jointPose, translation, negation, jointPose->getPublicRotation());
} }
void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& absRotation, void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& absRotation,
const LLVector3& deltaRotation, E_BoneDeflectionStyles deflectionStyle, const LLVector3& deltaRotation, E_BoneDeflectionStyles style, E_PoserReferenceFrame frame,
E_BoneAxisTranslation translation, S32 negation, bool resetBaseRotationToZero, E_RotationStyle rotationStyle) E_BoneAxisTranslation translation, S32 negation, bool resetBaseRotationToZero,
E_RotationStyle rotationStyle)
{ {
if (!isAvatarSafeToUse(avatar)) if (!isAvatarSafeToUse(avatar))
return; return;
@ -545,13 +624,15 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
if (!jointPose) if (!jointPose)
return; return;
LLQuaternion absRot = translateRotationToQuaternion(translation, negation, absRotation); bool translationRequiresDelta = frame != POSER_FRAME_BONE;
LLQuaternion deltaRot = translateRotationToQuaternion(translation, negation, deltaRotation);
switch (deflectionStyle) LLQuaternion absRot = translateRotationToQuaternion(avatar, jointPose, frame, translation, negation, absRotation);
LLQuaternion deltaRot = translateRotationToQuaternion(avatar, jointPose, frame, translation, negation, deltaRotation);
switch (style)
{ {
case SYMPATHETIC: case SYMPATHETIC:
case MIRROR: case MIRROR:
if (rotationStyle == DELTAIC_ROT) if (rotationStyle == DELTAIC_ROT || translationRequiresDelta)
jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation()); jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
else else
jointPose->setPublicRotation(resetBaseRotationToZero, absRot); jointPose->setPublicRotation(resetBaseRotationToZero, absRot);
@ -570,7 +651,7 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
case NONE: case NONE:
default: default:
if (rotationStyle == DELTAIC_ROT) if (rotationStyle == DELTAIC_ROT || translationRequiresDelta)
jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation()); jointPose->setPublicRotation(resetBaseRotationToZero, deltaRot * jointPose->getPublicRotation());
else else
jointPose->setPublicRotation(resetBaseRotationToZero, absRot); jointPose->setPublicRotation(resetBaseRotationToZero, absRot);
@ -586,8 +667,8 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
if (!oppositeJointPose) if (!oppositeJointPose)
return; return;
LLQuaternion inv_quat = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]); LLQuaternion mirroredRotation = LLQuaternion(-deltaRot.mQ[VX], deltaRot.mQ[VY], -deltaRot.mQ[VZ], deltaRot.mQ[VW]);
switch (deflectionStyle) switch (style)
{ {
case SYMPATHETIC: case SYMPATHETIC:
oppositeJointPose->cloneRotationFrom(jointPose); oppositeJointPose->cloneRotationFrom(jointPose);
@ -604,13 +685,13 @@ void FSPoserAnimator::setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* j
case MIRROR: case MIRROR:
oppositeJointPose->mirrorRotationFrom(jointPose); oppositeJointPose->mirrorRotationFrom(jointPose);
if (oppositePoserJoint) if (oppositePoserJoint)
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, inv_quat); deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, mirroredRotation);
break; break;
case MIRROR_DELTA: case MIRROR_DELTA:
oppositeJointPose->setPublicRotation(resetBaseRotationToZero, inv_quat * oppositeJointPose->getPublicRotation()); oppositeJointPose->setPublicRotation(resetBaseRotationToZero, mirroredRotation * oppositeJointPose->getPublicRotation());
if (oppositePoserJoint) if (oppositePoserJoint)
deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, inv_quat); deRotateWorldLockedDescendants(oppositePoserJoint, posingMotion, mirroredRotation);
break; break;
default: default:
@ -709,8 +790,9 @@ void FSPoserAnimator::flipEntirePose(LLVOAvatar* avatar)
} }
} }
// from the UI to the bone, the inverse translation, the un-swap, the backwards // from the UI to the bone. Bone rotations we store are relative to the skeleton (or to T-Pose, if you want to visualize).
LLQuaternion FSPoserAnimator::translateRotationToQuaternion(E_BoneAxisTranslation translation, S32 negation, LLVector3 rotation) LLQuaternion FSPoserAnimator::translateRotationToQuaternion(LLVOAvatar* avatar, FSJointPose* joint, E_PoserReferenceFrame frame,
E_BoneAxisTranslation translation, S32 negation, LLVector3 rotation)
{ {
if (negation & NEGATE_ALL) if (negation & NEGATE_ALL)
{ {
@ -761,11 +843,63 @@ LLQuaternion FSPoserAnimator::translateRotationToQuaternion(E_BoneAxisTranslatio
rot_quat = LLQuaternion(rot_mat) * rot_quat; rot_quat = LLQuaternion(rot_mat) * rot_quat;
rot_quat.normalize(); rot_quat.normalize();
rot_quat = changeToRotationFrame(avatar, rot_quat, frame, joint);
return rot_quat; return rot_quat;
} }
LLQuaternion FSPoserAnimator::changeToRotationFrame(LLVOAvatar* avatar, const LLQuaternion& rotation, E_PoserReferenceFrame frame, FSJointPose* joint)
{
if (!joint || !avatar)
return rotation;
LLJoint* pelvis = avatar->getJoint("mPelvis");
if (!pelvis)
return rotation;
LLVector3 skyward(0.f, 0.f, 1.f);
LLVector3 left(1.f, 0.f, 0.f);
LLVector3 forwards(0.f, 1.f, 0.f);
LLVector3 up, jointToCameraPosition, jointPosition;
LLQuaternion worldRotOfWorld(forwards, left, skyward);
LLQuaternion differenceInWorldRot, rotDiffInChildFrame, worldRotOfPelvis, worldRotOfCamera;
LLQuaternion worldRotOfThisJoint = joint->getJointState()->getJoint()->getWorldRotation();
switch (frame)
{
case POSER_FRAME_WORLD:
differenceInWorldRot = worldRotOfThisJoint * ~worldRotOfWorld;
break;
case POSER_FRAME_AVATAR:
worldRotOfPelvis = pelvis->getWorldRotation();
differenceInWorldRot = worldRotOfThisJoint * ~worldRotOfPelvis;
break;
case POSER_FRAME_CAMERA:
jointPosition = joint->getJointState()->getJoint()->getWorldPosition();
jointToCameraPosition = jointPosition - gAgentCamera.getCameraPositionAgent();
jointToCameraPosition.normalize();
left.setVec(skyward % jointToCameraPosition);
up.setVec(jointToCameraPosition % left);
worldRotOfCamera = LLQuaternion(jointToCameraPosition, left, up);
differenceInWorldRot = worldRotOfThisJoint * ~worldRotOfCamera;
break;
case POSER_FRAME_BONE:
default:
return rotation;
}
rotDiffInChildFrame = differenceInWorldRot * rotation * ~differenceInWorldRot;
rotDiffInChildFrame.conjugate();
return rotDiffInChildFrame;
}
// from the bone to the UI; this is the 'forwards' use of the enum // from the bone to the UI; this is the 'forwards' use of the enum
LLVector3 FSPoserAnimator::translateRotationFromQuaternion(E_BoneAxisTranslation translation, S32 negation, const LLQuaternion& rotation) const LLVector3 FSPoserAnimator::translateRotationFromQuaternion(FSJointPose* joint, E_BoneAxisTranslation translation, S32 negation, const LLQuaternion& rotation) const
{ {
LLVector3 vec3; LLVector3 vec3;
@ -833,7 +967,8 @@ LLVector3 FSPoserAnimator::getJointScale(LLVOAvatar* avatar, const FSPoserJoint&
return jointPose->getPublicScale(); return jointPose->getPublicScale();
} }
void FSPoserAnimator::setJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& scale, E_BoneDeflectionStyles style) void FSPoserAnimator::setJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& scale, E_PoserReferenceFrame frame,
E_BoneDeflectionStyles style)
{ {
if (!isAvatarSafeToUse(avatar)) if (!isAvatarSafeToUse(avatar))
return; return;
@ -852,25 +987,46 @@ void FSPoserAnimator::setJointScale(LLVOAvatar* avatar, const FSPoserJoint* join
if (!jointPose) if (!jointPose)
return; return;
jointPose->setPublicScale(scale); LLVector3 jointScale = jointPose->getPublicScale();
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName()); LLVector3 scaleDelta = jointScale - scale;
if (!oppositeJointPose)
return;
switch (style) switch (style)
{ {
case SYMPATHETIC:
case MIRROR: case MIRROR:
case SYMPATHETIC_DELTA:
case MIRROR_DELTA: case MIRROR_DELTA:
oppositeJointPose->setPublicScale(scale); case SYMPATHETIC_DELTA:
case SYMPATHETIC:
jointPose->setPublicScale(jointScale - scaleDelta);
break; break;
case DELTAMODE: case DELTAMODE:
case NONE: case NONE:
default: default:
jointPose->setPublicScale(jointScale - scaleDelta);
return; return;
} }
FSJointPose* oppositeJointPose = posingMotion->getJointPoseByJointName(joint->mirrorJointName());
if (!oppositeJointPose)
return;
LLVector3 oppositeJointScale = oppositeJointPose->getPublicScale();
switch (style)
{
case MIRROR:
case MIRROR_DELTA:
oppositeJointPose->setPublicScale(oppositeJointScale + scaleDelta);
break;
case SYMPATHETIC_DELTA:
case SYMPATHETIC:
oppositeJointPose->setPublicScale(oppositeJointScale - scaleDelta);
break;
default:
break;
}
} }
bool FSPoserAnimator::tryGetJointSaveVectors(LLVOAvatar* avatar, const FSPoserJoint& joint, LLVector3* rot, LLVector3* pos, bool FSPoserAnimator::tryGetJointSaveVectors(LLVOAvatar* avatar, const FSPoserJoint& joint, LLVector3* rot, LLVector3* pos,
@ -914,7 +1070,7 @@ void FSPoserAnimator::loadJointRotation(LLVOAvatar* avatar, const FSPoserJoint*
jointPose->purgeUndoQueue(); jointPose->purgeUndoQueue();
LLQuaternion rot = translateRotationToQuaternion(SWAP_NOTHING, NEGATE_NOTHING, rotation); LLQuaternion rot = translateRotationToQuaternion(avatar, jointPose, POSER_FRAME_BONE, SWAP_NOTHING, NEGATE_NOTHING, rotation);
jointPose->setPublicRotation(setBaseToZero, rot); jointPose->setPublicRotation(setBaseToZero, rot);
} }
@ -963,13 +1119,21 @@ void FSPoserAnimator::loadJointScale(LLVOAvatar* avatar, const FSPoserJoint* joi
} }
} }
bool FSPoserAnimator::loadPosingState(LLVOAvatar* avatar, LLSD pose) bool FSPoserAnimator::loadPosingState(LLVOAvatar* avatar, bool ignoreOwnership, LLSD pose)
{ {
if (!isAvatarSafeToUse(avatar)) if (!isAvatarSafeToUse(avatar))
return false; return false;
mPosingState.purgeMotionStates(avatar); mPosingState.purgeMotionStates(avatar);
mPosingState.restoreMotionStates(avatar, pose); mPosingState.restoreMotionStates(avatar, ignoreOwnership, pose);
return applyStatesToPosingMotion(avatar);
}
bool FSPoserAnimator::applyStatesToPosingMotion(LLVOAvatar* avatar)
{
if (!isAvatarSafeToUse(avatar))
return false;
FSPosingMotion* posingMotion = getPosingMotion(avatar); FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion) if (!posingMotion)
@ -1007,9 +1171,9 @@ void FSPoserAnimator::applyJointMirrorToBaseRotations(FSPosingMotion* posingMoti
} }
} }
void FSPoserAnimator::savePosingState(LLVOAvatar* avatar, LLSD* saveRecord) void FSPoserAnimator::savePosingState(LLVOAvatar* avatar, bool ignoreOwnership, LLSD* saveRecord)
{ {
mPosingState.writeMotionStates(avatar, saveRecord); mPosingState.writeMotionStates(avatar, ignoreOwnership, saveRecord);
} }
const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName) const const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const std::string& jointName) const
@ -1023,6 +1187,39 @@ const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByName(const
return nullptr; return nullptr;
} }
const FSPoserAnimator::FSPoserJoint* FSPoserAnimator::getPoserJointByNumber(LLVOAvatar* avatar, const S32 jointNumber) const
{
if (!avatar)
return nullptr;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return nullptr;
FSJointPose* parentJoint = posingMotion->getJointPoseByJointNumber(jointNumber);
if (!parentJoint)
return nullptr;
return getPoserJointByName(parentJoint->jointName());
}
bool FSPoserAnimator::tryGetJointNumber(LLVOAvatar* avatar, const FSPoserJoint &poserJoint, S32& jointNumber)
{
if (!avatar)
return false;
FSPosingMotion* posingMotion = getPosingMotion(avatar);
if (!posingMotion)
return false;
FSJointPose* parentJoint = posingMotion->getJointPoseByJointName(poserJoint.jointName());
if (!parentJoint)
return false;
jointNumber = parentJoint->getJointNumber();
return jointNumber >= 0;
}
bool FSPoserAnimator::tryPosingAvatar(LLVOAvatar* avatar) bool FSPoserAnimator::tryPosingAvatar(LLVOAvatar* avatar)
{ {
if (!isAvatarSafeToUse(avatar)) if (!isAvatarSafeToUse(avatar))
@ -1038,7 +1235,6 @@ bool FSPoserAnimator::tryPosingAvatar(LLVOAvatar* avatar)
gAgent.stopFidget(); gAgent.stopFidget();
mPosingState.captureMotionStates(avatar); mPosingState.captureMotionStates(avatar);
avatar->startDefaultMotions(); avatar->startDefaultMotions();
avatar->startMotion(posingMotion->motionId()); avatar->startMotion(posingMotion->motionId());
@ -1048,7 +1244,7 @@ bool FSPoserAnimator::tryPosingAvatar(LLVOAvatar* avatar)
return false; return false;
} }
void FSPoserAnimator::updatePosingState(LLVOAvatar* avatar, std::vector<FSPoserAnimator::FSPoserJoint*> jointsRecaptured) void FSPoserAnimator::updatePosingState(LLVOAvatar* avatar, const std::vector<FSPoserAnimator::FSPoserJoint*>& jointsRecaptured)
{ {
if (!avatar) if (!avatar)
return; return;
@ -1057,14 +1253,20 @@ void FSPoserAnimator::updatePosingState(LLVOAvatar* avatar, std::vector<FSPoserA
if (!posingMotion) if (!posingMotion)
return; return;
std::string jointNamesRecaptured; std::vector<S32> jointNumbersRecaptured;
for (auto item : jointsRecaptured) for (auto item : jointsRecaptured)
jointNamesRecaptured += item->jointName(); {
auto poserJoint = posingMotion->getJointPoseByJointName(item->jointName());
if (!poserJoint)
continue;
mPosingState.updateMotionStates(avatar, posingMotion, jointNamesRecaptured); jointNumbersRecaptured.push_back(poserJoint->getJointNumber());
}
mPosingState.updateMotionStates(avatar, posingMotion, jointNumbersRecaptured);
} }
void FSPoserAnimator::stopPosingAvatar(LLVOAvatar *avatar) void FSPoserAnimator::stopPosingAvatar(LLVOAvatar* avatar)
{ {
if (!avatar || avatar->isDead()) if (!avatar || avatar->isDead())
return; return;
@ -1128,7 +1330,7 @@ bool FSPoserAnimator::isAvatarSafeToUse(LLVOAvatar* avatar) const
return true; return true;
} }
int FSPoserAnimator::getChildJointDepth(const FSPoserJoint* joint, int depth) const int FSPoserAnimator::getChildJointDepth(const FSPoserJoint* joint, S32 depth) const
{ {
size_t numberOfBvhChildNodes = joint->bvhChildren().size(); size_t numberOfBvhChildNodes = joint->bvhChildren().size();
if (numberOfBvhChildNodes < 1) if (numberOfBvhChildNodes < 1)
@ -1138,7 +1340,7 @@ int FSPoserAnimator::getChildJointDepth(const FSPoserJoint* joint, int depth) co
for (size_t index = 0; index != numberOfBvhChildNodes; ++index) for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{ {
auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]); auto nextJoint = getPoserJointByName(joint->bvhChildren().at(index));
if (!nextJoint) if (!nextJoint)
continue; continue;
@ -1162,7 +1364,7 @@ void FSPoserAnimator::deRotateWorldLockedDescendants(const FSPoserJoint* joint,
for (size_t index = 0; index != numberOfBvhChildNodes; ++index) for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{ {
auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]); auto nextJoint = getPoserJointByName(joint->bvhChildren().at(index));
if (!nextJoint) if (!nextJoint)
continue; continue;
@ -1193,7 +1395,7 @@ void FSPoserAnimator::deRotateJointOrFirstLockedChild(const FSPoserJoint* joint,
for (size_t index = 0; index != numberOfBvhChildNodes; ++index) for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{ {
auto nextJoint = getPoserJointByName(joint->bvhChildren()[index]); auto nextJoint = getPoserJointByName(joint->bvhChildren().at(index));
if (!nextJoint) if (!nextJoint)
continue; continue;
@ -1209,7 +1411,7 @@ void FSPoserAnimator::undoOrRedoWorldLockedDescendants(const FSPoserJoint& joint
for (size_t index = 0; index != numberOfBvhChildNodes; ++index) for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{ {
auto nextJoint = getPoserJointByName(joint.bvhChildren()[index]); auto nextJoint = getPoserJointByName(joint.bvhChildren().at(index));
if (!nextJoint) if (!nextJoint)
continue; continue;
@ -1219,6 +1421,9 @@ void FSPoserAnimator::undoOrRedoWorldLockedDescendants(const FSPoserJoint& joint
void FSPoserAnimator::undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo) void FSPoserAnimator::undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo)
{ {
if (!posingMotion)
return;
FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName()); FSJointPose* jointPose = posingMotion->getJointPoseByJointName(joint.jointName());
if (!jointPose) if (!jointPose)
return; return;
@ -1239,7 +1444,7 @@ void FSPoserAnimator::undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& join
for (size_t index = 0; index != numberOfBvhChildNodes; ++index) for (size_t index = 0; index != numberOfBvhChildNodes; ++index)
{ {
auto nextJoint = getPoserJointByName(joint.bvhChildren()[index]); auto nextJoint = getPoserJointByName(joint.bvhChildren().at(index));
if (!nextJoint) if (!nextJoint)
continue; continue;

View File

@ -30,6 +30,7 @@
#include "fsposingmotion.h" #include "fsposingmotion.h"
#include "fsposestate.h" #include "fsposestate.h"
#include "llvoavatar.h" #include "llvoavatar.h"
#include "fsmaniprotatejoint.h"
/// <summary> /// <summary>
/// Describes how we will cluster the joints/bones/thingos. /// Describes how we will cluster the joints/bones/thingos.
@ -360,31 +361,31 @@ public:
{ "mSpine4", "", MISC, { "mChest", }, "-0.205 0.015 0.000" }, { "mSpine4", "", MISC, { "mChest", }, "-0.205 0.015 0.000" },
// Collision Volumes // Collision Volumes
{ "HEAD", "", COL_VOLUMES }, { "HEAD", "", COL_VOLUMES, {}, "0 0.07 0.02", "0.000 0.100 0.000" },
{ "NECK", "", COL_VOLUMES }, { "NECK", "", COL_VOLUMES, {}, "0 0.02 0.0", "0.000 0.080 0.000" },
{ "L_CLAVICLE", "R_CLAVICLE", COL_VOLUMES }, { "L_CLAVICLE", "R_CLAVICLE", COL_VOLUMES, {}, "0 0.02 0.02", "0.1 0.0 0.0" },
{ "R_CLAVICLE", "L_CLAVICLE", COL_VOLUMES, {}, "", "", true }, { "R_CLAVICLE", "L_CLAVICLE", COL_VOLUMES, {}, "0 0.02 0.02", "-0.1 0.0 0.0", true },
{ "CHEST", "", COL_VOLUMES }, { "CHEST", "", COL_VOLUMES, {}, "0 0.07 0.028", "0.000 0.152 -0.096" },
{ "LEFT_PEC", "RIGHT_PEC", COL_VOLUMES }, { "LEFT_PEC", "RIGHT_PEC", COL_VOLUMES, {}, "0.082 0.042 0.119", "0.000 -0.006 0.080" },
{ "RIGHT_PEC", "LEFT_PEC", COL_VOLUMES, {}, "", "", true }, { "RIGHT_PEC", "LEFT_PEC", COL_VOLUMES, {}, "-0.082 0.042 0.119", "0.000 -0.006 0.080", true },
{ "UPPER_BACK", "", COL_VOLUMES }, { "UPPER_BACK", "", COL_VOLUMES, {}, "0.0 0.017 0.0", "0.0 0.0 -0.100" },
{ "LEFT_HANDLE", "RIGHT_HANDLE", COL_VOLUMES }, { "LEFT_HANDLE", "RIGHT_HANDLE", COL_VOLUMES, {}, "0.10 0.058 0.0", "0.100 0.000 0.000" },
{ "RIGHT_HANDLE", "LEFT_HANDLE", COL_VOLUMES, {}, "", "", true }, { "RIGHT_HANDLE", "LEFT_HANDLE", COL_VOLUMES, {}, "-0.10 0.058 0.0", "-0.100 0.000 0.000", true },
{ "BELLY", "", COL_VOLUMES }, { "BELLY", "", COL_VOLUMES, {}, "0 0.04 0.028", "0.000 0.094 0.028" },
{ "PELVIS", "", COL_VOLUMES }, { "PELVIS", "", COL_VOLUMES, {}, "0 -0.02 -0.01", "0.000 0.095 0.030" },
{ "BUTT", "", COL_VOLUMES }, { "BUTT", "", COL_VOLUMES, {}, "0 -0.1 -0.06", "0.000 0.000 -0.100" },
{ "L_UPPER_ARM", "R_UPPER_ARM", COL_VOLUMES }, { "L_UPPER_ARM", "R_UPPER_ARM", COL_VOLUMES, {}, "0.12 0.01 0.0", "0.130 -0.003 0.000" },
{ "R_UPPER_ARM", "L_UPPER_ARM", COL_VOLUMES, {}, "", "", true }, { "R_UPPER_ARM", "L_UPPER_ARM", COL_VOLUMES, {}, "-0.12 0.01 0.0", "-0.130 -0.003 0.000", true },
{ "L_LOWER_ARM", "R_LOWER_ARM", COL_VOLUMES }, { "L_LOWER_ARM", "R_LOWER_ARM", COL_VOLUMES, {}, "0.1 0.0 0.0", "0.100 -0.001 0.000" },
{ "R_LOWER_ARM", "L_LOWER_ARM", COL_VOLUMES, {}, "", "", true }, { "R_LOWER_ARM", "L_LOWER_ARM", COL_VOLUMES, {}, "-0.1 0.0 0.0", "-0.100 -0.001 0.000", true },
{ "L_HAND", "R_HAND", COL_VOLUMES }, { "L_HAND", "R_HAND", COL_VOLUMES, {}, "0.05 0.0 0.01", "0.049 -0.001 0.005" },
{ "R_HAND", "L_HAND", COL_VOLUMES, {}, "", "", true }, { "R_HAND", "L_HAND", COL_VOLUMES, {}, "-0.05 0.0 0.01", "-0.049 -0.001 0.005", true },
{ "L_UPPER_LEG", "R_UPPER_LEG", COL_VOLUMES }, { "L_UPPER_LEG", "R_UPPER_LEG", COL_VOLUMES, {}, "-0.05 -0.22 -0.02", "0.000 -0.200 0.000" },
{ "R_UPPER_LEG", "L_UPPER_LEG", COL_VOLUMES, {}, "", "", true }, { "R_UPPER_LEG", "L_UPPER_LEG", COL_VOLUMES, {}, "0.05 -0.22 -0.02", "0.000 -0.200 0.000", true },
{ "L_LOWER_LEG", "R_LOWER_LEG", COL_VOLUMES }, { "L_LOWER_LEG", "R_LOWER_LEG", COL_VOLUMES, {}, "0.0 -0.2 -0.02", "0.000 -0.150 -0.010" },
{ "R_LOWER_LEG", "L_LOWER_LEG", COL_VOLUMES, {}, "", "", true }, { "R_LOWER_LEG", "L_LOWER_LEG", COL_VOLUMES, {}, "0.0 -0.2 -0.02", "0.000 -0.150 -0.010", true },
{ "L_FOOT", "R_FOOT", COL_VOLUMES }, { "L_FOOT", "R_FOOT", COL_VOLUMES, {}, "0.0 -0.041 0.077", "0.000 -0.026 0.089" },
{ "R_FOOT", "L_FOOT", COL_VOLUMES, {}, "", "", true }, { "R_FOOT", "L_FOOT", COL_VOLUMES, {}, "0.0 -0.041 0.077", "0.000 -0.026 0.089", true },
}; };
public: public:
@ -395,6 +396,20 @@ public:
/// <returns>The matching joint if found, otherwise nullptr</returns> /// <returns>The matching joint if found, otherwise nullptr</returns>
const FSPoserJoint* getPoserJointByName(const std::string& jointName) const; const FSPoserJoint* getPoserJointByName(const std::string& jointName) const;
/// <summary>
/// Get a PoserJoint case-insensitive-matching the supplied name.
/// </summary>
/// <param name="jointNumber">The name of the joint to match.</param>
/// <returns>The matching joint if found, otherwise nullptr</returns>
const FSPoserJoint* getPoserJointByNumber(LLVOAvatar* avatar, const S32 jointNumber) const;
/// <summary>
/// Get a PoserJoint by its LLJoint number.
/// </summary>
/// <param name="jointNumber">The name of the joint to match.</param>
/// <returns>The matching joint if found, otherwise nullptr</returns>
bool tryGetJointNumber(LLVOAvatar* avatar, const FSPoserJoint &poserJoint, S32& jointNumber);
/// <summary> /// <summary>
/// Tries to start posing the supplied avatar. /// Tries to start posing the supplied avatar.
/// </summary> /// </summary>
@ -423,6 +438,14 @@ public:
/// <returns>True if this is joint is being posed for the supplied avatar, otherwise false.</returns> /// <returns>True if this is joint is being posed for the supplied avatar, otherwise false.</returns>
bool isPosingAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint& joint); bool isPosingAvatarJoint(LLVOAvatar* avatar, const FSPoserJoint& joint);
/// <summary>
/// Determines whether the supplied PoserJoint for the supplied avatar has been modified this session, even if all change has been reverted.
/// </summary>
/// <param name="avatar">The avatar having the joint to which we refer.</param>
/// <param name="joint">The joint being queried for.</param>
/// <returns>True if this is joint has been changed while posing even if the change has been reverted or undone, otherwise false.</returns>
bool hasJointBeenChanged(LLVOAvatar* avatar, const FSPoserJoint& joint);
/// <summary> /// <summary>
/// Sets whether the supplied PoserJoint for the supplied avatar should be posed. /// Sets whether the supplied PoserJoint for the supplied avatar should be posed.
/// </summary> /// </summary>
@ -479,8 +502,10 @@ public:
/// <param name="avatar">The avatar whose joint is to be set.</param> /// <param name="avatar">The avatar whose joint is to be set.</param>
/// <param name="joint">The joint to set.</param> /// <param name="joint">The joint to set.</param>
/// <param name="position">The position to set the joint to.</param> /// <param name="position">The position to set the joint to.</param>
/// <param name="frame">The frame to translate the position to.</param>
/// <param name="style">Any ancilliary action to be taken with the change to be made.</param> /// <param name="style">Any ancilliary action to be taken with the change to be made.</param>
void setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& position, E_BoneDeflectionStyles style); void setJointPosition(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& position, E_PoserReferenceFrame frame,
E_BoneDeflectionStyles style);
/// <summary> /// <summary>
/// Gets the rotation of a joint for the supplied avatar. /// Gets the rotation of a joint for the supplied avatar.
@ -506,6 +531,15 @@ public:
/// </remarks> /// </remarks>
LLVector3 getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, bool lockWholeAvatar) const; LLVector3 getJointExportRotation(LLVOAvatar* avatar, const FSPoserJoint& joint, bool lockWholeAvatar) const;
/// <summary>
/// Gets the rotation suitable for the Manip gimbal for the supplied avatar and joint.
/// </summary>
/// <param name="avatar">The avatar having the Manip gimbal placed upon it.</param>
/// <param name="joint">The joint on the avatar where the manip should be placed.</param>
/// <param name="frame">The frame of reference for the gimbal.</param>
/// <returns>The rotation to set the gimbal to.</returns>
LLQuaternion getManipGimbalRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, E_PoserReferenceFrame frame);
/// <summary> /// <summary>
/// Sets the rotation of a joint for the supplied avatar. /// Sets the rotation of a joint for the supplied avatar.
/// </summary> /// </summary>
@ -518,8 +552,9 @@ public:
/// <param name="negation">The style of negation to apply to the set.</param> /// <param name="negation">The style of negation to apply to the set.</param>
/// <param name="resetBaseRotationToZero">Whether to set the base rotation to zero on setting the rotation.</param> /// <param name="resetBaseRotationToZero">Whether to set the base rotation to zero on setting the rotation.</param>
/// <param name="rotationStyle">Whether to apply the supplied rotation as a delta to the supplied joint.</param> /// <param name="rotationStyle">Whether to apply the supplied rotation as a delta to the supplied joint.</param>
void setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& absRotation, const LLVector3& deltaRotation, E_BoneDeflectionStyles style, void setJointRotation(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& absRotation, const LLVector3& deltaRotation,
E_BoneAxisTranslation translation, S32 negation, bool resetBaseRotationToZero, E_RotationStyle rotationStyle); E_BoneDeflectionStyles style, E_PoserReferenceFrame frame, E_BoneAxisTranslation translation, S32 negation,
bool resetBaseRotationToZero, E_RotationStyle rotationStyle);
/// <summary> /// <summary>
/// Gets the scale of a joint for the supplied avatar. /// Gets the scale of a joint for the supplied avatar.
@ -535,8 +570,10 @@ public:
/// <param name="avatar">The avatar whose joint is to be set.</param> /// <param name="avatar">The avatar whose joint is to be set.</param>
/// <param name="joint">The joint to set.</param> /// <param name="joint">The joint to set.</param>
/// <param name="scale">The scale to set the joint to.</param> /// <param name="scale">The scale to set the joint to.</param>
/// <param name="frame">The frame to translate the position to.</param>
/// <param name="style">Any ancilliary action to be taken with the change to be made.</param> /// <param name="style">Any ancilliary action to be taken with the change to be made.</param>
void setJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& scale, E_BoneDeflectionStyles style); void setJointScale(LLVOAvatar* avatar, const FSPoserJoint* joint, const LLVector3& scale, E_PoserReferenceFrame frame,
E_BoneDeflectionStyles style);
/// <summary> /// <summary>
/// Reflects the joint with its opposite if it has one, or just mirror the rotation of itself. /// Reflects the joint with its opposite if it has one, or just mirror the rotation of itself.
@ -564,9 +601,7 @@ public:
/// </summary> /// </summary>
/// <param name="avatar">The avatar whose joint is to be recaptured.</param> /// <param name="avatar">The avatar whose joint is to be recaptured.</param>
/// <param name="joint">The joint to recapture.</param> /// <param name="joint">The joint to recapture.</param>
/// <param name="translation">The axial translation form the supplied joint.</param> void recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint);
/// <param name="negation">The style of negation to apply to the recapture.</param>
void recaptureJoint(LLVOAvatar* avatar, const FSPoserJoint& joint, E_BoneAxisTranslation translation, S32 negation);
/// <summary> /// <summary>
/// Recaptures any change in joint state. /// Recaptures any change in joint state.
@ -575,7 +610,11 @@ public:
/// <param name="joint">The joint to recapture.</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="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> /// <param name="style">Any ancilliary action to be taken with the change to be made.</param>
void recaptureJointAsDelta(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero, E_BoneDeflectionStyles style); /// <param name="rotation">The rotation of the supplied joint.</param>
/// <param name="position">The position of the supplied joint.</param>
/// <param name="scale">The scale of the supplied joint.</param>
void updateJointFromManip(LLVOAvatar* avatar, const FSPoserJoint* joint, bool resetBaseRotationToZero, E_BoneDeflectionStyles style,
E_PoserReferenceFrame frame, const LLQuaternion& rotation, const LLVector3& position, const LLVector3& scale);
/// <summary> /// <summary>
/// Sets all of the joint rotations of the supplied avatar to zero. /// Sets all of the joint rotations of the supplied avatar to zero.
@ -711,6 +750,7 @@ public:
/// Loads the posing state (base rotations) to the supplied avatars posing-motion, from the supplied record. /// Loads the posing state (base rotations) to the supplied avatars posing-motion, from the supplied record.
/// </summary> /// </summary>
/// <param name="avatar">That avatar whose posing state should be loaded.</param> /// <param name="avatar">That avatar whose posing state should be loaded.</param>
/// <param name="ignoreOwnership">Whether to ignore ownership. For use when reading a local file.</param>
/// <param name="pose">The record to read the posing state from.</param> /// <param name="pose">The record to read the posing state from.</param>
/// <returns>True if the pose loaded successfully, otherwise false.</returns> /// <returns>True if the pose loaded successfully, otherwise false.</returns>
/// <remarks> /// <remarks>
@ -718,32 +758,29 @@ public:
/// it can take several frames for the animation to be loaded and ready. /// it can take several frames for the animation to be loaded and ready.
/// It may therefore be necessary to attempt this several times. /// It may therefore be necessary to attempt this several times.
/// </remarks> /// </remarks>
bool loadPosingState(LLVOAvatar* avatar, LLSD pose); bool loadPosingState(LLVOAvatar* avatar, bool ignoreOwnership, LLSD pose);
/// <summary>
/// Applies the posing states to the posing motion for the supplied avatar.
/// </summary>
/// <param name="avatar">That avatar whose posing state should be loaded.</param>
/// <returns>True if the state applied successfully, otherwise false.</returns>
bool applyStatesToPosingMotion(LLVOAvatar* avatar);
/// <summary> /// <summary>
/// Adds the posing state for the supplied avatar to the supplied record. /// Adds the posing state for the supplied avatar to the supplied record.
/// </summary> /// </summary>
/// <param name="avatar">That avatar whose posing state should be written.</param> /// <param name="avatar">That avatar whose posing state should be written.</param>
/// <param name="ignoreOwnership">Whether to ignore ownership while saving.</param>
/// <param name="saveRecord">The record to write the posing state to.</param> /// <param name="saveRecord">The record to write the posing state to.</param>
void savePosingState(LLVOAvatar* avatar, LLSD* saveRecord); void savePosingState(LLVOAvatar* avatar, bool ignoreOwnership, LLSD* saveRecord);
/// <summary> /// <summary>
/// Purges and recaptures the pose state for the supplied avatar. /// Purges and recaptures the pose state for the supplied avatar.
/// </summary> /// </summary>
/// <param name="avatar">The avatar whose pose state is to be recapture.</param> /// <param name="avatar">The avatar whose pose state is to be recapture.</param>
/// <param name="jointsRecaptured">The joints which were recaptured.</param> /// <param name="jointsRecaptured">The joints which were recaptured.</param>
void updatePosingState(LLVOAvatar* avatar, std::vector<FSPoserAnimator::FSPoserJoint*> jointsRecaptured); void updatePosingState(LLVOAvatar* avatar, const std::vector<FSPoserAnimator::FSPoserJoint*>& jointsRecaptured);
/// <summary>
/// Add a new posing state, or updates the matching posing state with the supplied data.
/// </summary>
/// <param name="avatar">The avatar the posing state is intended for.</param>
/// <param name="animId">The ID of the animation.</param>
/// <param name="updateTime">The frame-time of the animation.</param>
/// <param name="jointNames">The names of the joints, if any, the animation should specifically be applied to.</param>
/// <param name="captureOrder">The capture order.</param>
/// <returns>True if the posing state was added or changed by the update data, otherwise false.</returns>
bool addOrUpdatePosingState(LLVOAvatar* avatar, LLUUID animId, F32 updateTime, std::string jointNames, int captureOrder);
/// <summary> /// <summary>
/// Traverses the joints and applies reversals to the base rotations if needed. /// Traverses the joints and applies reversals to the base rotations if needed.
@ -756,13 +793,18 @@ public:
private: private:
/// <summary> /// <summary>
/// Translates a rotation vector from the UI to a Quaternion for the bone. /// Translates the supplied rotation vector from UI to a Quaternion for the bone.
/// This also performs the axis-swapping the UI needs for up/down/left/right to make sense. /// Also performs the axis-swapping and other transformations for up/down/left/right to make sense.
/// </summary> /// </summary>
/// <param name="avatar">The avatar whose joint is being manipulated.</param>
/// <param name="joint">The joint which is being altered.</param>
/// <param name="frame">The frame of reference the translation should be performed in.</param>
/// <param name="translation">The axis translation to perform.</param> /// <param name="translation">The axis translation to perform.</param>
/// <param name="rotation">The rotation to transform to quaternion.</param> /// <param name="negation">The style of axis-negation.</param>
/// <returns>The rotation quaternion.</returns> /// <param name="rotation">The rotation to translate and transform to quaternion.</param>
LLQuaternion translateRotationToQuaternion(E_BoneAxisTranslation translation, S32 negation, LLVector3 rotation); /// <returns>The translated rotation quaternion.</returns>
LLQuaternion translateRotationToQuaternion(LLVOAvatar* avatar, FSJointPose* joint, E_PoserReferenceFrame frame,
E_BoneAxisTranslation translation, S32 negation, LLVector3 rotation);
/// <summary> /// <summary>
/// Translates a bone-rotation quaternion to a vector usable easily on the UI. /// Translates a bone-rotation quaternion to a vector usable easily on the UI.
@ -770,7 +812,7 @@ public:
/// <param name="translation">The axis translation to perform.</param> /// <param name="translation">The axis translation to perform.</param>
/// <param name="rotation">The rotation to transform to matrix.</param> /// <param name="rotation">The rotation to transform to matrix.</param>
/// <returns>The rotation vector.</returns> /// <returns>The rotation vector.</returns>
LLVector3 translateRotationFromQuaternion(E_BoneAxisTranslation translation, S32 negation, const LLQuaternion& rotation) const; LLVector3 translateRotationFromQuaternion(FSJointPose* joint, E_BoneAxisTranslation translation, S32 negation, const LLQuaternion& rotation) const;
/// <summary> /// <summary>
/// Creates a posing motion for the supplied avatar. /// Creates a posing motion for the supplied avatar.
@ -803,7 +845,7 @@ public:
/// <param name="joint">The joint to determine the depth for.</param> /// <param name="joint">The joint to determine the depth for.</param>
/// <param name="depth">The depth of the supplied joint.</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> /// <returns>The number of generations of descendents the joint has, if none, then zero.</returns>
int getChildJointDepth(const FSPoserJoint* joint, int depth) const; int getChildJointDepth(const FSPoserJoint* joint, S32 depth) const;
/// <summary> /// <summary>
/// Derotates the first world-locked child joint to the supplied joint. /// Derotates the first world-locked child joint to the supplied joint.
@ -844,6 +886,20 @@ public:
/// <param name="redo">Whether to redo the edit, otherwise the edit is undone.</param> /// <param name="redo">Whether to redo the edit, otherwise the edit is undone.</param>
void undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo); void undoOrRedoJointOrFirstLockedChild(const FSPoserJoint& joint, FSPosingMotion* posingMotion, bool redo);
/// <summary>
/// Converts the supplied rotation into the desired frame.
/// </summary>
/// <param name="avatar">The avatar owning the supplied joint.</param>
/// <param name="rotation">The rotation to convert.</param>
/// <param name="frame">The frame to translate the rotation to.</param>
/// <param name="joint">The joint whose rotation is being changed.</param>
/// <remarks>
/// Input rotations have no implicit frame: it's just a rotation and ordinarily applied, inherits the joint's rotational framing.
/// This method imposes a framing upon the supplied rotation, meaning user input is considered as relative to something like
/// 'the world', 'avatar pelvis' or the position of the camera relative to the joint.
/// </remarks>
LLQuaternion changeToRotationFrame(LLVOAvatar* avatar, const LLQuaternion& rotation, E_PoserReferenceFrame frame, FSJointPose* joint);
/// <summary> /// <summary>
/// Maps the avatar's ID to the animation registered to them. /// 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. /// Thus we start/stop the same animation, and get/set the same rotations etc.

View File

@ -3,6 +3,7 @@
std::map<LLUUID, std::vector<FSPoseState::fsMotionState>> FSPoseState::sMotionStates; std::map<LLUUID, std::vector<FSPoseState::fsMotionState>> FSPoseState::sMotionStates;
std::map<LLUUID, int> FSPoseState::sCaptureOrder; std::map<LLUUID, int> FSPoseState::sCaptureOrder;
std::map<LLUUID, bool> FSPoseState::sMotionStatesOwnedByMe;
void FSPoseState::captureMotionStates(LLVOAvatar* avatar) void FSPoseState::captureMotionStates(LLVOAvatar* avatar)
{ {
@ -10,6 +11,7 @@ void FSPoseState::captureMotionStates(LLVOAvatar* avatar)
return; return;
sCaptureOrder[avatar->getID()] = 0; sCaptureOrder[avatar->getID()] = 0;
int animNumber = 0;
for (auto anim_it = avatar->mPlayingAnimations.begin(); anim_it != avatar->mPlayingAnimations.end(); ++anim_it) for (auto anim_it = avatar->mPlayingAnimations.begin(); anim_it != avatar->mPlayingAnimations.end(); ++anim_it)
{ {
@ -21,27 +23,26 @@ void FSPoseState::captureMotionStates(LLVOAvatar* avatar)
newState.motionId = anim_it->first; newState.motionId = anim_it->first;
newState.lastUpdateTime = motion->getLastUpdateTime(); newState.lastUpdateTime = motion->getLastUpdateTime();
newState.captureOrder = 0; newState.captureOrder = 0;
newState.avatarOwnsPose = canSaveMotionId(avatar, anim_it->first); newState.inLayerOrder = animNumber++;
newState.gAgentOwnsPose = canSaveMotionId(avatar, anim_it->first);
sMotionStates[avatar->getID()].push_back(newState); sMotionStates[avatar->getID()].push_back(newState);
} }
} }
void FSPoseState::updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingMotion, std::string jointNamesRecaptured) void FSPoseState::updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingMotion, const std::vector<S32>& jointNumbersRecaptured)
{ {
if (!avatar || !posingMotion) if (!avatar || !posingMotion)
return; return;
sCaptureOrder[avatar->getID()]++; sCaptureOrder[avatar->getID()]++;
S32 animNumber = 0;
// if an animation for avatar is a subset of jointNamesRecaptured, delete it // if an animation for avatar is a subset of jointNumbersRecaptured, delete it
// this happens on second/subsequent recaptures; the first recapture is no longer needed // this happens on second/subsequent recaptures; the first recapture is no longer needed
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end();) for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end();)
{ {
std::string joints = (*it).jointNamesAnimated; if (vector2IsSubsetOfVector1(jointNumbersRecaptured, (*it).jointNumbersAnimated))
bool recaptureMatches = !joints.empty() && !jointNamesRecaptured.empty() && jointNamesRecaptured.find(joints) != std::string::npos;
if (recaptureMatches)
it = sMotionStates[avatar->getID()].erase(it); it = sMotionStates[avatar->getID()].erase(it);
else else
it++; it++;
@ -53,7 +54,7 @@ void FSPoseState::updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingM
if (!motion) if (!motion)
continue; continue;
if (!posingMotion->otherMotionAnimatesJoints(motion, jointNamesRecaptured)) if (!posingMotion->otherMotionAnimatesJoints(motion, jointNumbersRecaptured))
continue; continue;
bool foundMatch = false; bool foundMatch = false;
@ -73,43 +74,15 @@ void FSPoseState::updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingM
fsMotionState newState; fsMotionState newState;
newState.motionId = anim_it->first; newState.motionId = anim_it->first;
newState.lastUpdateTime = motion->getLastUpdateTime(); newState.lastUpdateTime = motion->getLastUpdateTime();
newState.jointNamesAnimated = jointNamesRecaptured; newState.jointNumbersAnimated = jointNumbersRecaptured;
newState.captureOrder = sCaptureOrder[avatar->getID()]; newState.captureOrder = sCaptureOrder[avatar->getID()];
newState.avatarOwnsPose = canSaveMotionId(avatar, anim_it->first); newState.inLayerOrder = animNumber++;
newState.gAgentOwnsPose = canSaveMotionId(avatar, anim_it->first);
sMotionStates[avatar->getID()].push_back(newState); sMotionStates[avatar->getID()].push_back(newState);
} }
} }
bool FSPoseState::addOrUpdatePosingMotionState(LLVOAvatar* avatar, LLUUID animId, F32 updateTime, std::string jointNames, int captureOrder)
{
if (!avatar)
return false;
bool foundMatch = false;
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); it++)
{
bool motionIdMatches = (*it).motionId == animId;
bool updateTimesMatch = (*it).lastUpdateTime == updateTime;
bool jointNamesMatch = (*it).jointNamesAnimated == jointNames;
bool captureOrdersMatch = (*it).captureOrder == captureOrder;
foundMatch = motionIdMatches && updateTimesMatch && jointNamesMatch && captureOrdersMatch;
if (foundMatch)
return false;
}
fsMotionState newState;
newState.motionId = animId;
newState.lastUpdateTime = updateTime;
newState.jointNamesAnimated = jointNames;
newState.captureOrder = captureOrder;
newState.avatarOwnsPose = false;
sMotionStates[avatar->getID()].push_back(newState);
return true;
}
void FSPoseState::purgeMotionStates(LLVOAvatar* avatar) void FSPoseState::purgeMotionStates(LLVOAvatar* avatar)
{ {
if (!avatar) if (!avatar)
@ -118,26 +91,35 @@ void FSPoseState::purgeMotionStates(LLVOAvatar* avatar)
sMotionStates[avatar->getID()].clear(); sMotionStates[avatar->getID()].clear();
} }
void FSPoseState::writeMotionStates(LLVOAvatar* avatar, LLSD* saveRecord) void FSPoseState::writeMotionStates(LLVOAvatar* avatar, bool ignoreOwnership, LLSD* saveRecord)
{ {
if (!avatar) if (!avatar)
return; return;
int animNumber = 0; S32 animNumber = 0;
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); ++it) for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); ++it)
{ {
if (!it->avatarOwnsPose) if (!ignoreOwnership && !it->gAgentOwnsPose)
{
if (it->requeriedAssetInventory)
continue; continue;
it->gAgentOwnsPose = canSaveMotionId(avatar, it->motionId);
it->requeriedAssetInventory = true;
if (!it->gAgentOwnsPose)
continue;
}
std::string uniqueAnimId = "poseState" + std::to_string(animNumber++); std::string uniqueAnimId = "poseState" + std::to_string(animNumber++);
(*saveRecord)[uniqueAnimId]["animationId"] = it->motionId.asString(); (*saveRecord)[uniqueAnimId]["animationId"] = it->motionId.asString();
(*saveRecord)[uniqueAnimId]["lastUpdateTime"] = it->lastUpdateTime; (*saveRecord)[uniqueAnimId]["lastUpdateTime"] = it->lastUpdateTime;
(*saveRecord)[uniqueAnimId]["jointNamesAnimated"] = it->jointNamesAnimated; (*saveRecord)[uniqueAnimId]["jointNumbersAnimated"] = encodeVectorToString(it->jointNumbersAnimated);
(*saveRecord)[uniqueAnimId]["captureOrder"] = it->captureOrder; (*saveRecord)[uniqueAnimId]["captureOrder"] = it->captureOrder;
(*saveRecord)[uniqueAnimId]["inLayerOrder"] = it->inLayerOrder;
} }
} }
void FSPoseState::restoreMotionStates(LLVOAvatar* avatar, LLSD pose) void FSPoseState::restoreMotionStates(LLVOAvatar* avatar, bool ignoreOwnership, LLSD pose)
{ {
if (!avatar) if (!avatar)
return; return;
@ -153,25 +135,33 @@ void FSPoseState::restoreMotionStates(LLVOAvatar* avatar, LLSD pose)
continue; continue;
fsMotionState newState; fsMotionState newState;
newState.avatarOwnsPose = true;
if (control_map.has("animationId")) if (control_map.has("animationId"))
{ {
std::string const name = control_map["animationId"].asString(); std::string const name = control_map["animationId"].asString();
LLUUID animId; LLUUID animId;
if (LLUUID::parseUUID(name, &animId)) if (LLUUID::parseUUID(name, &animId))
{
newState.motionId = animId; newState.motionId = animId;
newState.gAgentOwnsPose = ignoreOwnership || canSaveMotionId(avatar, animId);
if (ignoreOwnership)
sMotionStatesOwnedByMe[animId] = true;
}
} }
if (control_map.has("lastUpdateTime")) if (control_map.has("lastUpdateTime"))
newState.lastUpdateTime = (F32)control_map["lastUpdateTime"].asReal(); newState.lastUpdateTime = (F32)control_map["lastUpdateTime"].asReal();
if (control_map.has("jointNamesAnimated")) if (control_map.has("jointNumbersAnimated"))
newState.jointNamesAnimated = control_map["jointNamesAnimated"].asString(); newState.jointNumbersAnimated = decodeStringToVector(control_map["jointNumbersAnimated"].asString());
if (control_map.has("captureOrder")) if (control_map.has("captureOrder"))
newState.captureOrder = control_map["captureOrder"].asInteger(); newState.captureOrder = control_map["captureOrder"].asInteger();
if (control_map.has("inLayerOrder"))
newState.inLayerOrder = control_map["inLayerOrder"].asInteger();
if (newState.captureOrder > sCaptureOrder[avatar->getID()]) if (newState.captureOrder > sCaptureOrder[avatar->getID()])
sCaptureOrder[avatar->getID()] = newState.captureOrder; sCaptureOrder[avatar->getID()] = newState.captureOrder;
@ -188,7 +178,7 @@ bool FSPoseState::applyMotionStatesToPosingMotion(LLVOAvatar* avatar, FSPosingMo
std::sort(sMotionStates[avatar->getID()].begin(), sMotionStates[avatar->getID()].end(), compareByCaptureOrder()); std::sort(sMotionStates[avatar->getID()].begin(), sMotionStates[avatar->getID()].end(), compareByCaptureOrder());
int lastCaptureOrder = 0; S32 lastCaptureOrder = 0;
bool needPriorityReset = false; bool needPriorityReset = false;
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); it++) for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); it++)
{ {
@ -207,7 +197,7 @@ bool FSPoseState::applyMotionStatesToPosingMotion(LLVOAvatar* avatar, FSPosingMo
resetPriorityForCaptureOrder(avatar, posingMotion, lastCaptureOrder); resetPriorityForCaptureOrder(avatar, posingMotion, lastCaptureOrder);
} }
it->motionApplied = posingMotion->loadOtherMotionToBaseOfThisMotion(kfm, it->lastUpdateTime, it->jointNamesAnimated); it->motionApplied = posingMotion->loadOtherMotionToBaseOfThisMotion(kfm, it->lastUpdateTime, it->jointNumbersAnimated);
} }
else else
{ {
@ -221,30 +211,50 @@ bool FSPoseState::applyMotionStatesToPosingMotion(LLVOAvatar* avatar, FSPosingMo
return allMotionsApplied; return allMotionsApplied;
} }
void FSPoseState::resetPriorityForCaptureOrder(LLVOAvatar* avatar, FSPosingMotion* posingMotion, int captureOrder) void FSPoseState::resetPriorityForCaptureOrder(LLVOAvatar* avatar, FSPosingMotion* posingMotion, S32 captureOrder)
{ {
for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); it++) for (auto it = sMotionStates[avatar->getID()].begin(); it != sMotionStates[avatar->getID()].end(); it++)
{ {
if (it->jointNamesAnimated.empty()) if (it->jointNumbersAnimated.empty())
continue; continue;
if (it->motionApplied) if (it->motionApplied)
continue; continue;
if (it->captureOrder != captureOrder) if (it->captureOrder != captureOrder)
continue; continue;
posingMotion->resetBonePriority(it->jointNamesAnimated); posingMotion->resetBonePriority(it->jointNumbersAnimated);
} }
} }
bool FSPoseState::canSaveMotionId(LLVOAvatar* avatar, LLAssetID motionId) bool FSPoseState::canSaveMotionId(LLVOAvatar* avatarPlayingMotionId, LLAssetID motionId)
{ {
if (!gAgentAvatarp || gAgentAvatarp.isNull()) if (!gAgentAvatarp || gAgentAvatarp.isNull())
return false; return false;
if (sMotionStatesOwnedByMe[motionId])
return true;
// does the animation exist in inventory // does the animation exist in inventory
LLInventoryItem* item = gInventory.getItem(motionId); LLInventoryItem* item = gInventory.getItem(motionId);
if (item && item->getPermissions().getOwner() == avatar->getID()) if (item && item->getPermissions().getOwner() == gAgentAvatarp->getID())
return true; {
sMotionStatesOwnedByMe[motionId] = true;
return sMotionStatesOwnedByMe[motionId];
}
if (!avatarPlayingMotionId)
return false;
if (avatarPlayingMotionId->getID() == gAgentAvatarp->getID())
return motionIdIsAgentAnimationSource(motionId);
return motionIdIsFromPrimAgentOwnsAgentIsSittingOn(avatarPlayingMotionId, motionId);
}
bool FSPoseState::motionIdIsAgentAnimationSource(LLAssetID motionId)
{
if (!gAgentAvatarp || gAgentAvatarp.isNull())
return false;
for (const auto& [anim_object_id, anim_anim_id] : gAgentAvatarp->mAnimationSources) for (const auto& [anim_object_id, anim_anim_id] : gAgentAvatarp->mAnimationSources)
{ {
@ -252,17 +262,150 @@ bool FSPoseState::canSaveMotionId(LLVOAvatar* avatar, LLAssetID motionId)
continue; continue;
// is the item that started the anim in inventory // is the item that started the anim in inventory
item = gInventory.getItem(anim_object_id); LLInventoryItem* item = gInventory.getItem(anim_object_id);
if (item && item->getPermissions().getOwner() == avatar->getID()) if (item && item->getPermissions().getOwner() == gAgentAvatarp->getID())
return true; {
sMotionStatesOwnedByMe[motionId] = true;
return sMotionStatesOwnedByMe[motionId];
}
// is the item that start the animation in-world // is the item that start the animation in-world
LLViewerObject* object = gObjectList.findObject(anim_object_id); LLViewerObject* object = gObjectList.findObject(anim_object_id);
if (object && object->permYouOwner()) if (object && object->permYouOwner())
return true; {
sMotionStatesOwnedByMe[motionId] = true;
return false; return sMotionStatesOwnedByMe[motionId];
}
} }
return false; return false;
} }
bool FSPoseState::motionIdIsFromPrimAgentOwnsAgentIsSittingOn(LLVOAvatar* avatarPlayingMotionId, LLAssetID motionId)
{
if (!avatarPlayingMotionId)
return false;
const LLViewerObject* agentRoot = dynamic_cast<LLViewerObject*>(avatarPlayingMotionId->getRoot());
if (!agentRoot)
return false;
const LLUUID& assetIdTheyAreSittingOn = agentRoot->getID();
if (assetIdTheyAreSittingOn == avatarPlayingMotionId->getID())
return false; // they are not sitting on a thing
LLViewerObject* object = gObjectList.findObject(assetIdTheyAreSittingOn);
if (!object || !object->permYouOwner())
return false; // gAgent does not own what they are sitting on
if (object->isInventoryPending())
return false;
if (object->isInventoryDirty() || !object->getInventoryRoot())
{
object->requestInventory();
return false; // whatever they are sitting on, we don't have the inventory list for yet
}
LLInventoryItem* item = object->getInventoryItemByAsset(motionId, LLAssetType::AT_ANIMATION);
if (item && item->getPermissions().getOwner() == gAgentAvatarp->getID())
{
sMotionStatesOwnedByMe[motionId] = true;
return sMotionStatesOwnedByMe[motionId];
}
return false;
}
bool FSPoseState::vector2IsSubsetOfVector1(std::vector<S32> newRecapture, std::vector<S32> oldRecapture)
{
if (newRecapture.empty())
return false;
if (oldRecapture.empty())
return false;
if (newRecapture.size() < oldRecapture.size())
return false;
for (S32 number : oldRecapture)
if (std::find(newRecapture.begin(), newRecapture.end(), number) == newRecapture.end())
return false;
return true;
}
std::string FSPoseState::encodeVectorToString(const std::vector<S32>& vector)
{
std::string encoded = "";
if (vector.empty())
return encoded;
for (S32 numberToEncode : vector)
{
if (numberToEncode > 251) // max 216 at time of writing
continue;
S32 number = numberToEncode;
if (number >= 189)
{
encoded += "~";
number -= 189;
}
if (number >= 126)
{
encoded += "}";
number -= 126;
}
if (number >= 63)
{
encoded += "|";
number -= 63;
}
encoded += char(number + int('?'));
}
return encoded;
}
std::vector<S32> FSPoseState::decodeStringToVector(std::string_view vector)
{
std::vector<S32> decoded;
if (vector.empty())
return decoded;
S32 number = 0;
for (char ch : vector)
{
if (ch > '~' || ch < '?')
continue;
if (ch == '~')
{
number += 189;
continue;
}
if (ch == '}')
{
number += 126;
continue;
}
if (ch == '|')
{
number += 63;
continue;
}
number -= int('?');
number += S32(ch);
decoded.push_back(number);
number = 0;
}
return decoded;
}

View File

@ -49,18 +49,7 @@ public:
/// <param name="avatar">The avatar whose animations are to be captured.</param> /// <param name="avatar">The avatar whose animations are to be captured.</param>
/// <param name="posingMotion">The posing motion.</param> /// <param name="posingMotion">The posing motion.</param>
/// <param name="jointNamesRecaptured">The names of the joints being recaptured.</param> /// <param name="jointNamesRecaptured">The names of the joints being recaptured.</param>
void updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingMotion, std::string jointNamesRecaptured); void updateMotionStates(LLVOAvatar* avatar, FSPosingMotion* posingMotion, const std::vector<S32>& jointNamesRecaptured);
/// <summary>
/// Add a new posing state, or updates the matching posing state with the supplied data.
/// </summary>
/// <param name="avatar">The avatar the posing state is intended for.</param>
/// <param name="animId">The ID of the animation.</param>
/// <param name="updateTime">The frame-time of the animation.</param>
/// <param name="jointNames">The names of the joints, if any, the animation should specifically be applied to.</param>
/// <param name="captureOrder">The capture order.</param>
/// <returns>True if the posing state was added or changed by the update data, otherwise false.</returns>
bool addOrUpdatePosingMotionState(LLVOAvatar* avatar, LLUUID animId, F32 updateTime, std::string jointNames, int captureOrder);
/// <summary> /// <summary>
/// Removes all current animation states for the supplied avatar. /// Removes all current animation states for the supplied avatar.
@ -72,16 +61,17 @@ public:
/// Writes any documented poses for the supplied avatar to the supplied stream. /// Writes any documented poses for the supplied avatar to the supplied stream.
/// </summary> /// </summary>
/// <param name="avatar">The avatar whose animations may have been captured.</param> /// <param name="avatar">The avatar whose animations may have been captured.</param>
/// <param name="ignoreOwnership">Whether to ignore ownership. For use when preparing saveRecord to send to another by collab.</param>
/// <param name="saveRecord">The record to add to.</param> /// <param name="saveRecord">The record to add to.</param>
void writeMotionStates(LLVOAvatar* avatar, LLSD* saveRecord); void writeMotionStates(LLVOAvatar* avatar, bool ignoreOwnership, LLSD* saveRecord);
/// <summary> /// <summary>
/// Restores pose state(s) from the supplied record. /// Restores pose state(s) from the supplied record.
/// </summary> /// </summary>
/// <param name="avatar">The avatar whose animations may have been captured.</param> /// <param name="avatar">The avatar whose animations may have been captured.</param>
/// <param name="posingMotion">The posing motion.</param> /// <param name="ignoreOwnership">Whether to ignore ownership. For use when reading a local file.</param>
/// <param name="pose">The record to read from.</param> /// <param name="pose">The record to read from.</param>
void restoreMotionStates(LLVOAvatar* avatar, LLSD pose); void restoreMotionStates(LLVOAvatar* avatar, bool ignoreOwnership, LLSD pose);
/// <summary> /// <summary>
/// Applies the motion states for the supplied avatar to the supplied motion. /// Applies the motion states for the supplied avatar to the supplied motion.
@ -122,20 +112,30 @@ private:
bool motionApplied = false; bool motionApplied = false;
/// <summary> /// <summary>
/// Whether the avatar owns the pose, or the pose was loaded. /// For non-gAgent, we permit a query of the inventory of a prim they are sitting on.
/// Because this involves latency, we may retry ownership checking at save-time.
/// </summary> /// </summary>
bool avatarOwnsPose = false; bool requeriedAssetInventory = false;
/// <summary> /// <summary>
/// When reloading, larger numbers are loaded last, nesting order and priority. /// Whether gAgent owns the pose, or the pose was loaded from XML.
/// This is used to represent recaptures, where joints could be animated with different poses.
/// </summary> /// </summary>
int captureOrder = 0; bool gAgentOwnsPose = false;
/// <summary> /// <summary>
/// When reloading, and if not-empty, the names of the bones this motionId should affect. /// Represents 'capture layers: how the user layers animations 'on top of' others.
/// </summary> /// </summary>
std ::string jointNamesAnimated; S32 captureOrder = 0;
/// <summary>
/// Represents in-layer order of capture.
/// </summary>
S32 inLayerOrder = 0;
/// <summary>
/// When reloading, and if not-empty, the bone-numbers this motionId should affect.
/// </summary>
std ::vector<S32> jointNumbersAnimated;
}; };
/// <summary> /// <summary>
@ -144,15 +144,61 @@ private:
/// <param name="avatar">The avatar being posed by the motion.</param> /// <param name="avatar">The avatar being posed by the motion.</param>
/// <param name="posingMotion">The posing motion.</param> /// <param name="posingMotion">The posing motion.</param>
/// <param name="captureOrder">The order of the capture.</param> /// <param name="captureOrder">The order of the capture.</param>
void resetPriorityForCaptureOrder(LLVOAvatar* avatar, FSPosingMotion* posingMotion, int captureOrder); void resetPriorityForCaptureOrder(LLVOAvatar* avatar, FSPosingMotion* posingMotion, S32 captureOrder);
/// <summary> /// <summary>
/// Gets whether the supplied avatar owns, and thus can save information about the supplied asset ID. /// Gets whether gAgentID owns, and thus can save information about the supplied motionId.
/// </summary> /// </summary>
/// <param name="avatar">The avatar to query ownership for.</param> /// <param name="avatarPlayingMotionId">The avatar playing the supplied motionId.</param>
/// <param name="motionId">The asset ID of the object.</param> /// <param name="motionId">The motionId of the animation.</param>
/// <returns>True if the avatar owns the asset, otherwise false.</returns> /// <returns>True if the gAgent owns the motionId, otherwise false.</returns>
bool canSaveMotionId(LLVOAvatar* avatar, LLAssetID motionId); /// <remarks>
/// This only works reliably for self.
/// For motions playing on others, the motion needs to be an asset in gAgent's inventory.
/// </remarks>
bool canSaveMotionId(LLVOAvatar* avatarPlayingMotionId, LLAssetID motionId);
/// <summary>
/// Examines gAgent's animation source list for the supplied animation Id.
/// </summary>
/// <param name="motionId">The ID of the motion to query.</param>
/// <returns>True if gAgent is playing the animation, otherwise false.</returns>
bool motionIdIsAgentAnimationSource(LLAssetID motionId);
/// <summary>
/// Queries a specific condition of the supplied animation ID.
/// </summary>
/// <param name="avatarPlayingMotionId">The avatar to query for.</param>
/// <param name="motionId">The motion ID to query for.</param>
/// <returns>
/// True if the supplied avatar is sitting on an object owned by gAgent, and that object
/// contains an animation asset with the same assetId.
/// </returns>
/// <remarks>
/// This is intended to test for a situation a photographer might arrange.
/// If you are sitting on photographer's prim, playing photographer's pose, and photographer wants to save their work,
/// this allows them to save the Animation ID and state to XML.
/// It is intended this be called twice at least, as it does not implement a callback onInventoryLoaded.
/// Presently this works fine: first time being when posing starts, second when pose is saved.
/// </remarks>
bool motionIdIsFromPrimAgentOwnsAgentIsSittingOn(LLVOAvatar* avatarPlayingMotionId, LLAssetID motionId);
/// <summary>
/// Tests if all the members of supplied vector2 are members of supplied vector1.
/// </summary>
/// <param name="vector1">The super-set.</param>
/// <param name="vector2">The possible sub-set.</param>
/// <returns>True if all members of vector2 are members of vector1, otherwise false.</returns>
bool vector2IsSubsetOfVector1(std::vector<S32> vector1, std::vector<S32> vector2);
/// <summary>
/// Two symmetric methods for (de)serializing vectors to both XML and collab-safe short-as-possible strings and back again.
/// </summary>
/// <remarks>
/// Collab-safe means ASCII-printable chars, and delimiter usage does not conflict with Collab's delimiter.
/// </remarks>
std::string encodeVectorToString(const std::vector<S32>& vector);
std::vector<S32> decodeStringToVector(std::string_view vector);
struct compareByCaptureOrder struct compareByCaptureOrder
{ {
@ -160,13 +206,16 @@ private:
{ {
if (a.captureOrder < b.captureOrder) if (a.captureOrder < b.captureOrder)
return true; // Ascending order return true; // Ascending order
if (a.captureOrder == b.captureOrder && a.inLayerOrder < b.inLayerOrder)
return true; // Ascending order in layer
return false; return false;
} }
}; };
static std::map <LLUUID, std::vector<fsMotionState>> sMotionStates; static std::map<LLUUID, std::vector<fsMotionState>> sMotionStates;
static std::map<LLUUID, int> sCaptureOrder; static std::map<LLUUID, S32> sCaptureOrder;
static std::map<LLUUID, bool> sMotionStatesOwnedByMe;
}; };
#endif // LL_FSPoseState_H #endif // LL_FSPoseState_H

View File

@ -36,7 +36,7 @@ FSPosingMotion::FSPosingMotion(const LLUUID& id) : LLKeyframeMotion(id)
mJointMotionList = &dummyMotionList; mJointMotionList = &dummyMotionList;
} }
LLMotion::LLMotionInitStatus FSPosingMotion::onInitialize(LLCharacter *character) LLMotion::LLMotionInitStatus FSPosingMotion::onInitialize(LLCharacter* character)
{ {
if (!character) if (!character)
return STATUS_FAILURE; return STATUS_FAILURE;
@ -184,7 +184,7 @@ void FSPosingMotion::removeJointFromState(LLJoint* joint)
void FSPosingMotion::setJointState(LLJoint* joint, U32 state) void FSPosingMotion::setJointState(LLJoint* joint, U32 state)
{ {
if (mJointPoses.size() < 1) if (mJointPoses.empty())
return; return;
if (!joint) if (!joint)
return; return;
@ -208,7 +208,7 @@ void FSPosingMotion::setJointState(LLJoint* joint, U32 state)
FSJointPose* FSPosingMotion::getJointPoseByJointName(const std::string& name) FSJointPose* FSPosingMotion::getJointPoseByJointName(const std::string& name)
{ {
if (mJointPoses.size() < 1) if (name.empty() || mJointPoses.empty())
return nullptr; return nullptr;
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter) for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
@ -222,15 +222,33 @@ FSJointPose* FSPosingMotion::getJointPoseByJointName(const std::string& name)
return nullptr; return nullptr;
} }
FSJointPose* FSPosingMotion::getJointPoseByJointNumber(const S32 number)
{
if (mJointPoses.empty())
return nullptr;
if (number < 0)
return nullptr;
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
{
if (poserJoint_iter->getJointNumber() != number)
continue;
return &*poserJoint_iter;
}
return nullptr;
}
bool FSPosingMotion::currentlyPosingJoint(LLJoint* joint) bool FSPosingMotion::currentlyPosingJoint(LLJoint* joint)
{ {
if (mJointPoses.size() < 1) if (mJointPoses.empty())
return false; return false;
if (!joint) if (!joint)
return false; return false;
LLPose* pose = this->getPose(); LLPose* pose = getPose();
if (!pose) if (!pose)
return false; return false;
@ -270,14 +288,14 @@ void FSPosingMotion::setJointBvhLock(FSJointPose* joint, bool lockInBvh)
joint->zeroBaseRotation(lockInBvh); joint->zeroBaseRotation(lockInBvh);
} }
bool FSPosingMotion::loadOtherMotionToBaseOfThisMotion(LLKeyframeMotion* motionToLoad, F32 timeToLoadAt, std::string selectedJointNames) bool FSPosingMotion::loadOtherMotionToBaseOfThisMotion(LLKeyframeMotion* motionToLoad, F32 timeToLoadAt, const std::vector<S32>& selectedJointNumbers)
{ {
FSPosingMotion* motionToLoadAsFsPosingMotion = static_cast<FSPosingMotion*>(motionToLoad); FSPosingMotion* motionToLoadAsFsPosingMotion = static_cast<FSPosingMotion*>(motionToLoad);
if (!motionToLoadAsFsPosingMotion) if (!motionToLoadAsFsPosingMotion)
return false; return false;
LLJoint::JointPriority priority = motionToLoad->getPriority(); LLJoint::JointPriority priority = motionToLoad->getPriority();
bool motionIsForAllJoints = selectedJointNames.empty(); bool motionIsForAllJoints = selectedJointNumbers.empty();
LLQuaternion rot; LLQuaternion rot;
LLVector3 position, scale; LLVector3 position, scale;
@ -285,16 +303,18 @@ bool FSPosingMotion::loadOtherMotionToBaseOfThisMotion(LLKeyframeMotion* motionT
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter) for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
{ {
S32 jointNumber = poserJoint_iter->getJointNumber();
std::string jointName = poserJoint_iter->jointName(); std::string jointName = poserJoint_iter->jointName();
bool motionIsForThisJoint = selectedJointNames.find(jointName) != std::string::npos; bool motionIsForThisJoint =
std::find(selectedJointNumbers.begin(), selectedJointNumbers.end(), jointNumber) != selectedJointNumbers.end();
if (!motionIsForAllJoints && !motionIsForThisJoint) if (!motionIsForAllJoints && !motionIsForThisJoint)
continue; continue;
hasRotation = hasPosition = hasScale = false; hasRotation = hasPosition = hasScale = false;
motionToLoadAsFsPosingMotion->getJointStateAtTime(jointName, timeToLoadAt, &hasRotation, &rot, &hasPosition, &position, &hasScale, &scale); motionToLoadAsFsPosingMotion->getJointStateAtTime(jointName, timeToLoadAt, &hasRotation, &rot, &hasPosition, &position, &hasScale, &scale);
if (hasRotation) if (hasRotation && !poserJoint_iter->userHasSetBaseRotationToZero())
poserJoint_iter->setBaseRotation(rot, priority); poserJoint_iter->setBaseRotation(rot, priority);
if (hasPosition) if (hasPosition)
@ -337,16 +357,17 @@ void FSPosingMotion::getJointStateAtTime(std::string jointPoseName, F32 timeToLo
} }
} }
bool FSPosingMotion::otherMotionAnimatesJoints(LLKeyframeMotion* motionToQuery, std::string recapturedJointNames) bool FSPosingMotion::otherMotionAnimatesJoints(LLKeyframeMotion* motionToQuery, const std::vector<S32>& recapturedJointNumbers)
{ {
FSPosingMotion* motionToLoadAsFsPosingMotion = static_cast<FSPosingMotion*>(motionToQuery); FSPosingMotion* motionToLoadAsFsPosingMotion = static_cast<FSPosingMotion*>(motionToQuery);
if (!motionToLoadAsFsPosingMotion) if (!motionToLoadAsFsPosingMotion)
return false; return false;
return motionToLoadAsFsPosingMotion->motionAnimatesJoints(recapturedJointNames); return motionToLoadAsFsPosingMotion->motionAnimatesJoints(recapturedJointNumbers);
} }
bool FSPosingMotion::motionAnimatesJoints(std::string recapturedJointNames) // Do not try to access FSPosingMotion state; you are a LLKeyframeMotion cast as a FSPosingMotion, NOT an FSPosingMotion.
bool FSPosingMotion::motionAnimatesJoints(const std::vector<S32>& recapturedJointNumbers)
{ {
if (mJointMotionList == nullptr) if (mJointMotionList == nullptr)
return false; return false;
@ -354,7 +375,9 @@ bool FSPosingMotion::motionAnimatesJoints(std::string recapturedJointNames)
for (U32 i = 0; i < mJointMotionList->getNumJointMotions(); i++) for (U32 i = 0; i < mJointMotionList->getNumJointMotions(); i++)
{ {
JointMotion* jm = mJointMotionList->getJointMotion(i); JointMotion* jm = mJointMotionList->getJointMotion(i);
if (recapturedJointNames.find(jm->mJointName) == std::string::npos) LLJoint* joint = mCharacter->getJoint(jm->mJointName);
if (std::find(recapturedJointNumbers.begin(), recapturedJointNumbers.end(), joint->getJointNum()) == recapturedJointNumbers.end())
continue; continue;
if (jm->mRotationCurve.mNumKeys > 0) if (jm->mRotationCurve.mNumKeys > 0)
@ -364,20 +387,19 @@ bool FSPosingMotion::motionAnimatesJoints(std::string recapturedJointNames)
return false; return false;
} }
void FSPosingMotion::resetBonePriority(std::string boneNamesToReset) void FSPosingMotion::resetBonePriority(const std::vector<S32>& boneNumbersToReset)
{ {
if (boneNamesToReset.empty()) for (S32 boneNumber : boneNumbersToReset)
return; {
for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter) for (auto poserJoint_iter = mJointPoses.begin(); poserJoint_iter != mJointPoses.end(); ++poserJoint_iter)
{ {
std::string jointName = poserJoint_iter->jointName(); if (poserJoint_iter->getJointNumber() == boneNumber)
if (boneNamesToReset.find(jointName) != std::string::npos)
poserJoint_iter->setJointPriority(LLJoint::LOW_PRIORITY); poserJoint_iter->setJointPriority(LLJoint::LOW_PRIORITY);
} }
}
} }
bool FSPosingMotion::vectorsNotQuiteEqual(LLVector3 v1, LLVector3 v2) const bool FSPosingMotion::vectorsNotQuiteEqual(const LLVector3& v1, const LLVector3& v2) const
{ {
if (vectorAxesAlmostEqual(v1.mV[VX], v2.mV[VX]) && if (vectorAxesAlmostEqual(v1.mV[VX], v2.mV[VX]) &&
vectorAxesAlmostEqual(v1.mV[VY], v2.mV[VY]) && vectorAxesAlmostEqual(v1.mV[VY], v2.mV[VY]) &&

View File

@ -68,7 +68,7 @@ public:
// run-time (post constructor) initialization, // run-time (post constructor) initialization,
// called after parameters have been set // called after parameters have been set
// must return true to indicate success and be available for activation // must return true to indicate success and be available for activation
virtual LLMotionInitStatus onInitialize(LLCharacter *character); virtual LLMotionInitStatus onInitialize(LLCharacter* character);
// called when a motion is activated // called when a motion is activated
// must return TRUE to indicate success, or else // must return TRUE to indicate success, or else
@ -108,6 +108,13 @@ public:
/// <returns>The matching joint pose, if found, otherwise null.</returns> /// <returns>The matching joint pose, if found, otherwise null.</returns>
FSJointPose* getJointPoseByJointName(const std::string& name); FSJointPose* getJointPoseByJointName(const std::string& name);
/// <summary>
/// Gets the joint pose by its LLJoint number.
/// </summary>
/// <param name="number">The number of the joint to get the pose for.</param>
/// <returns>The matching joint pose, if found, otherwise null.</returns>
FSJointPose* getJointPoseByJointNumber(const S32 number);
/// <summary> /// <summary>
/// Gets the motion identity for this animation. /// Gets the motion identity for this animation.
/// </summary> /// </summary>
@ -139,9 +146,9 @@ public:
/// </summary> /// </summary>
/// <param name="motionToLoad">The motion whose joint rotations (etc) we want to copy to this.</param> /// <param name="motionToLoad">The motion whose joint rotations (etc) we want to copy to this.</param>
/// <param name="timeToLoadAt">The play-time the animation should be advanced to derive the correct joint state.</param> /// <param name="timeToLoadAt">The play-time the animation should be advanced to derive the correct joint state.</param>
/// <param name="selectedJointNames">If only some of the joints should be animated by this motion, name them here.</param> /// <param name="selectedJointNumbers">If only some of the joints should be animated by this motion, number them here.</param>
/// <returns></returns> /// <returns></returns>
bool loadOtherMotionToBaseOfThisMotion(LLKeyframeMotion* motionToLoad, F32 timeToLoadAt, std::string selectedJointNames); bool loadOtherMotionToBaseOfThisMotion(LLKeyframeMotion* motionToLoad, F32 timeToLoadAt, const std::vector<S32>& selectedJointNumbers);
/// <summary> /// <summary>
/// Tries to get the rotation, position and scale for the supplied joint name at the supplied time. /// Tries to get the rotation, position and scale for the supplied joint name at the supplied time.
@ -164,27 +171,27 @@ public:
/// <summary> /// <summary>
/// Resets the bone priority to zero for the joints named in the supplied string. /// Resets the bone priority to zero for the joints named in the supplied string.
/// </summary> /// </summary>
/// <param name="boneNamesToReset">The string containg bone names (like mPelvis).</param> /// <param name="boneNumbersToReset">The vector containing bone numbers.</param>
void resetBonePriority(std::string boneNamesToReset); void resetBonePriority(const std::vector<S32>& boneNumbersToReset);
/// <summary> /// <summary>
/// Queries whether the supplied motion animates any of the joints named in the supplied string. /// Queries whether the supplied motion animates any of the joints named in the supplied string.
/// </summary> /// </summary>
/// <param name="motionToQuery">The motion to query.</param> /// <param name="motionToQuery">The motion to query.</param>
/// <param name="recapturedJointNames">A string containing all of the joint names.</param> /// <param name="recapturedJointNumbers">A string containing all of the joint numbers.</param>
/// <returns>True if the motion animates any of the bones named, otherwise false.</returns> /// <returns>True if the motion animates any of the bones named, otherwise false.</returns>
bool otherMotionAnimatesJoints(LLKeyframeMotion* motionToQuery, std::string recapturedJointNames); bool otherMotionAnimatesJoints(LLKeyframeMotion* motionToQuery, const std::vector<S32>& recapturedJointNumbers);
/// <summary> /// <summary>
/// Queries whether the this motion animates any of the joints named in the supplied string. /// Queries whether the this motion animates any of the joints named in the supplied string.
/// </summary> /// </summary>
/// <param name="recapturedJointNames">A string containing all of the joint names.</param> /// <param name="recapturedJointNames">A vector containing all of the joint numbers this motion animates.</param>
/// <returns>True if the motion animates any of the bones named, otherwise false.</returns> /// <returns>True if the motion animates any of the bones named, otherwise false.</returns>
/// <remarks> /// <remarks>
/// The most significant thing this method does is provide access to protected properties of an LLPosingMotion. /// The most significant thing this method does is provide access to protected properties of an LLPosingMotion.
/// Thus its most common usage would be to access those properties for an arbitrary animation. /// Thus its most common usage would be to access those properties for an arbitrary animation.
/// </remarks> /// </remarks>
bool motionAnimatesJoints(std::string recapturedJointNames); bool motionAnimatesJoints(const std::vector<S32>& recapturedJointNumbers);
private: private:
/// <summary> /// <summary>
@ -267,7 +274,7 @@ private:
/// <param name="v1">The first vector to compare.</param> /// <param name="v1">The first vector to compare.</param>
/// <param name="v2">The sceond vector to compare.</param> /// <param name="v2">The sceond vector to compare.</param>
/// <returns>true if the vectors are "close enough", otherwise false.</returns> /// <returns>true if the vectors are "close enough", otherwise false.</returns>
bool vectorsNotQuiteEqual(LLVector3 v1, LLVector3 v2) const; bool vectorsNotQuiteEqual(const LLVector3& v1, const LLVector3& v2) const;
/// <summary> /// <summary>
/// Determines if two quaternions are near enough to equal. /// Determines if two quaternions are near enough to equal.

View File

@ -292,10 +292,10 @@ using namespace LL;
#include "nd/ndetw.h" // <FS:ND/> Windows Event Tracing, does nothing on OSX/Linux. #include "nd/ndetw.h" // <FS:ND/> Windows Event Tracing, does nothing on OSX/Linux.
#include "nd/ndlogthrottle.h" #include "nd/ndlogthrottle.h"
#include "aoengine.h"
#include "fsradar.h" #include "fsradar.h"
#include "fsassetblacklist.h" #include "fsassetblacklist.h"
#include "bugsplatattributes.h" #include "bugsplatattributes.h"
// #include "fstelemetry.h" // <FS:Beq> Tracy profiler support
#if LL_LINUX && LL_GTK #if LL_LINUX && LL_GTK
#include "glib.h" #include "glib.h"
@ -2271,6 +2271,8 @@ bool LLAppViewer::cleanup()
LLEnvironment::getInstance()->saveToSettings(); LLEnvironment::getInstance()->saveToSettings();
} }
AOEngine::deleteSingleton();
// Must do this after all panels have been deleted because panels that have persistent rects // Must do this after all panels have been deleted because panels that have persistent rects
// save their rects on delete. // save their rects on delete.
if(mSaveSettingsOnExit) // <FS:Zi> Backup Settings if(mSaveSettingsOnExit) // <FS:Zi> Backup Settings

View File

@ -191,8 +191,8 @@ LLAvatarList::LLAvatarList(const Params& p)
, mRlvCheckShowNames(false) , mRlvCheckShowNames(false)
// [/RLVa:KB] // [/RLVa:KB]
, mShowVoiceVolume(p.show_voice_volume) , mShowVoiceVolume(p.show_voice_volume)
, mShowUsername((bool)gSavedSettings.getBOOL("NameTagShowUsernames")) , mShowUsername(gSavedSettings.getBOOL("NameTagShowUsernames"))
, mShowDisplayName((bool)gSavedSettings.getBOOL("UseDisplayNames")) , mShowDisplayName(gSavedSettings.getBOOL("UseDisplayNames"))
{ {
setCommitOnSelectionChange(true); setCommitOnSelectionChange(true);
@ -219,13 +219,13 @@ LLAvatarList::LLAvatarList(const Params& p)
void LLAvatarList::handleDisplayNamesOptionChanged() void LLAvatarList::handleDisplayNamesOptionChanged()
{ {
// <FS:Ansariel> FIRE-1089: Set the proper name options for the AvatarListItem before we update the list. // <FS:Ansariel> FIRE-1089: Set the proper name options for the AvatarListItem before we update the list.
mShowUsername = (bool)gSavedSettings.getBOOL("NameTagShowUsernames"); mShowUsername = gSavedSettings.getBOOL("NameTagShowUsernames");
mShowDisplayName = (bool)gSavedSettings.getBOOL("UseDisplayNames"); mShowDisplayName = gSavedSettings.getBOOL("UseDisplayNames");
std::vector<LLPanel*> items; std::vector<LLPanel*> items;
getItems(items); getItems(items);
for( std::vector<LLPanel*>::const_iterator it = items.begin(); it != items.end(); it++) for (auto panel : items)
{ {
LLAvatarListItem* item = static_cast<LLAvatarListItem*>(*it); LLAvatarListItem* item = static_cast<LLAvatarListItem*>(panel);
item->showUsername(mShowUsername, false); item->showUsername(mShowUsername, false);
item->showDisplayName(mShowDisplayName, false); item->showDisplayName(mShowDisplayName, false);
} }
@ -265,9 +265,9 @@ void LLAvatarList::updateRlvRestrictions(ERlvBehaviour behavior, ERlvParamType t
{ {
std::vector<LLPanel*> items; std::vector<LLPanel*> items;
getItems(items); getItems(items);
for (std::vector<LLPanel*>::const_iterator it = items.begin(); it != items.end(); it++) for (auto panel : items)
{ {
LLAvatarListItem* item = static_cast<LLAvatarListItem*>(*it); LLAvatarListItem* item = static_cast<LLAvatarListItem*>(panel);
item->updateRlvRestrictions(); item->updateRlvRestrictions();
} }
} }
@ -559,11 +559,11 @@ S32 LLAvatarList::notifyParent(const LLSD& info)
return 1; return 1;
} }
// [SL:KB] - Patch: UI-AvatarListDndShare | Checked: 2011-06-19 (Catznip-2.6.0c) | Added: Catznip-2.6.0c // [SL:KB] - Patch: UI-AvatarListDndShare | Checked: 2011-06-19 (Catznip-2.6.0c) | Added: Catznip-2.6.0c
else if ( (info.has("select")) && (info["select"].isUUID()) ) else if (info.has("select") && info["select"].isUUID())
{ {
const LLSD& sdValue = getSelectedValue(); const LLSD& sdValue = getSelectedValue();
const LLUUID idItem = info["select"].asUUID(); const LLUUID idItem = info["select"].asUUID();
if ( (!sdValue.isDefined()) || ((sdValue.isUUID()) && (sdValue.asUUID() != idItem)) ) if (!sdValue.isDefined() || (sdValue.isUUID() && sdValue.asUUID() != idItem))
{ {
resetSelection(); resetSelection();
selectItemByUUID(info["select"].asUUID()); selectItemByUUID(info["select"].asUUID());
@ -731,18 +731,18 @@ void LLAvatarList::onItemClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask)
// static // static
std::string LLAvatarList::getNameForDisplay(const LLUUID& avatar_id, const LLAvatarName& av_name, bool show_displayname, bool show_username, bool force_use_complete_name, bool rlv_check_shownames) std::string LLAvatarList::getNameForDisplay(const LLUUID& avatar_id, const LLAvatarName& av_name, bool show_displayname, bool show_username, bool force_use_complete_name, bool rlv_check_shownames)
{ {
bool fRlvCanShowName = (!rlv_check_shownames) || (RlvActions::canShowName(RlvActions::SNC_DEFAULT, avatar_id)); const bool fRlvCanShowName = (!rlv_check_shownames) || (RlvActions::canShowName(RlvActions::SNC_DEFAULT, avatar_id));
if (show_displayname && !show_username) if (show_displayname && !show_username)
{ {
return ( (fRlvCanShowName) ? av_name.getDisplayName() : RlvStrings::getAnonym(av_name) ); return (fRlvCanShowName ? av_name.getDisplayName() : RlvStrings::getAnonym(av_name));
} }
else if (!show_displayname && show_username) else if (!show_displayname && show_username)
{ {
return ( (fRlvCanShowName) ? av_name.getUserName() : RlvStrings::getAnonym(av_name) ); return (fRlvCanShowName ? av_name.getUserName() : RlvStrings::getAnonym(av_name));
} }
else else
{ {
return ( (fRlvCanShowName) ? av_name.getCompleteName(false, force_use_complete_name) : RlvStrings::getAnonym(av_name) ); return (fRlvCanShowName ? av_name.getCompleteName(true, force_use_complete_name) : RlvStrings::getAnonym(av_name));
} }
} }
// </FS:Ansariel> // </FS:Ansariel>

View File

@ -368,7 +368,12 @@ void LLFloaterModelPreview::reshape(S32 width, S32 height, bool called_from_pare
{ {
LLFloaterModelUploadBase::reshape(width, height, called_from_parent); LLFloaterModelUploadBase::reshape(width, height, called_from_parent);
LLView* preview_panel = getChild<LLView>("preview_panel"); // <FS:Ansariel> This can get called before the floater is actually built
//LLView* preview_panel = getChild<LLView>("preview_panel");
LLView* preview_panel = findChild<LLView>("preview_panel");
if (!preview_panel)
return;
// </FS:Ansariel>
LLRect rect = preview_panel->getRect(); LLRect rect = preview_panel->getRect();
if (rect != mPreviewRect) if (rect != mPreviewRect)

View File

@ -1129,7 +1129,7 @@ void LLOutfitListBase::refreshList(const LLUUID& category_id)
// <FS:ND> FIRE-6958/VWR-2862; Handle large amounts of outfits, write a least a warning into the logs. // <FS:ND> FIRE-6958/VWR-2862; Handle large amounts of outfits, write a least a warning into the logs.
S32 currentOutfitsAmount = (S32)mRefreshListState.Added.size(); S32 currentOutfitsAmount = (S32)mRefreshListState.Added.size();
constexpr S32 maxSuggestedOutfits = 200; constexpr S32 maxSuggestedOutfits = 1000;
if (currentOutfitsAmount > maxSuggestedOutfits) if (currentOutfitsAmount > maxSuggestedOutfits)
{ {
LL_WARNS() << "Large amount of outfits found: " << currentOutfitsAmount << " this may cause hangs and disconnects" << LL_ENDL; LL_WARNS() << "Large amount of outfits found: " << currentOutfitsAmount << " this may cause hangs and disconnects" << LL_ENDL;

View File

@ -272,7 +272,8 @@ public:
virtual bool handleDoubleClick(S32 x, S32 y, MASK mask) override; virtual bool handleDoubleClick(S32 x, S32 y, MASK mask) override;
virtual void render() override; virtual void render() override;
void setAvatar(LLVOAvatar* avatar) { mManip->setAvatar(avatar); }; void setAvatar(LLVOAvatar* avatar) { mManip->setAvatar(avatar); };
void setJoint( LLJoint * joint ) { mManip->setJoint( joint ); }; void setJoint(LLJoint* joint) { mManip->setJoint(joint); };
void setReferenceFrame(E_PoserReferenceFrame frame) { mManip->setReferenceFrame(frame); };
// Optional override if you have SHIFT/CTRL combos // Optional override if you have SHIFT/CTRL combos
virtual LLTool* getOverrideTool(MASK mask) override; virtual LLTool* getOverrideTool(MASK mask) override;

View File

@ -913,7 +913,27 @@ void handleUsernameFormatOptionChanged(const LLSD& newvalue)
// <FS:Ansariel> Global online status toggle // <FS:Ansariel> Global online status toggle
void handleGlobalOnlineStatusChanged(const LLSD& newvalue) void handleGlobalOnlineStatusChanged(const LLSD& newvalue)
{ {
if (gSavedPerAccountSettings.getBOOL("GlobalOnlineStatusCurrentlyReverting"))
{
gSavedPerAccountSettings.setBOOL("GlobalOnlineStatusCurrentlyReverting", false);
return;
}
bool visible = newvalue.asBoolean(); bool visible = newvalue.asBoolean();
LLSD payload;
payload["visible"] = visible;
LLNotificationsUtil::add("ConfirmGlobalOnlineStatusToggle", LLSD(), payload, applyGlobalOnlineStatusChange);
}
void applyGlobalOnlineStatusChange(const LLSD& notification, const LLSD& response)
{
bool visible = notification["payload"]["visible"].asBoolean();
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if (option != 0)
{
gSavedPerAccountSettings.setBOOL("GlobalOnlineStatusCurrentlyReverting", true);
gSavedPerAccountSettings.setBOOL("GlobalOnlineStatusToggle", !visible);
return;
}
LLAvatarTracker::buddy_map_t all_buddies; LLAvatarTracker::buddy_map_t all_buddies;
LLAvatarTracker::instance().copyBuddyList(all_buddies); LLAvatarTracker::instance().copyBuddyList(all_buddies);

View File

@ -61,4 +61,8 @@ extern LLControlGroup gCrashSettings;
// Set after settings loaded // Set after settings loaded
extern std::string gLastRunVersion; extern std::string gLastRunVersion;
// <FS> Global online status toggle
void applyGlobalOnlineStatusChange(const LLSD& notification, const LLSD& response);
// </FS>
#endif // LL_LLVIEWERCONTROL_H #endif // LL_LLVIEWERCONTROL_H

View File

@ -216,6 +216,7 @@
#include "fsfloaterprotectedfolders.h" #include "fsfloaterprotectedfolders.h"
#include "fsfloaterradar.h" #include "fsfloaterradar.h"
#include "fsfloatersearch.h" #include "fsfloatersearch.h"
#include "fsfloatersplashscreensettings.h"
#include "fsfloaterstatistics.h" #include "fsfloaterstatistics.h"
#include "fsfloaterstreamtitle.h" #include "fsfloaterstreamtitle.h"
#include "fsfloaterteleporthistory.h" #include "fsfloaterteleporthistory.h"
@ -654,6 +655,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("fs_poser", "floater_fs_poser.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterPoser>); // <FS:AR> [FIRE-30873]: Poser LLFloaterReg::add("fs_poser", "floater_fs_poser.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterPoser>); // <FS:AR> [FIRE-30873]: Poser
LLFloaterReg::add("fs_protectedfolders", "floater_fs_protectedfolders.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterProtectedFolders>); LLFloaterReg::add("fs_protectedfolders", "floater_fs_protectedfolders.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterProtectedFolders>);
LLFloaterReg::add("fs_radar", "floater_fs_radar.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterRadar>); LLFloaterReg::add("fs_radar", "floater_fs_radar.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterRadar>);
LLFloaterReg::add("fs_splash_screen_settings", "floater_fs_splash_screen_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterSplashScreenSettings>);
LLFloaterReg::add("fs_streamtitle", "floater_fs_streamtitle.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterStreamTitle>); LLFloaterReg::add("fs_streamtitle", "floater_fs_streamtitle.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterStreamTitle>);
LLFloaterReg::add("fs_streamtitlehistory", "floater_fs_streamtitlehistory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterStreamTitleHistory>); LLFloaterReg::add("fs_streamtitlehistory", "floater_fs_streamtitlehistory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterStreamTitleHistory>);
LLFloaterReg::add("fs_teleporthistory", "floater_fs_teleporthistory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterTeleportHistory>); LLFloaterReg::add("fs_teleporthistory", "floater_fs_teleporthistory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<FSFloaterTeleportHistory>);

View File

@ -2296,12 +2296,50 @@ void menu_create_inventory_item(LLInventoryPanel* panel, LLUUID dest_id, const L
else if ("lsl" == type_name) else if ("lsl" == type_name)
{ {
const LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_LSL_TEXT); const LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_LSL_TEXT);
// <FS:PP> FIRE-36164 Apply the custom "New Script" setting for inventory scripts
bool custom_script_inserted = false;
if (gSavedPerAccountSettings.getBOOL("FSBuildPrefs_UseCustomScript"))
{
if (LLUUID custom_script_id(gSavedPerAccountSettings.getString("FSBuildPrefs_CustomScriptItem")); custom_script_id.notNull())
{
if (auto custom_script = gInventory.getItem(custom_script_id); custom_script && custom_script->getType() == LLAssetType::AT_LSL_TEXT)
{
LLPointer<LLBoostFuncInventoryCallback> cb = NULL;
if (created_cb != NULL)
{
cb = new LLBoostFuncInventoryCallback(create_script_cb);
cb->addOnFireFunc(created_cb);
}
else
{
cb = new LLBoostFuncInventoryCallback(create_script_cb);
}
copy_inventory_item(
gAgent.getID(),
custom_script->getPermissions().getOwner(),
custom_script_id,
parent_id,
NEW_LSL_NAME,
cb);
custom_script_inserted = true;
}
}
}
if (!custom_script_inserted)
{
// </FS:PP>
create_new_item(NEW_LSL_NAME, create_new_item(NEW_LSL_NAME,
parent_id, parent_id,
LLAssetType::AT_LSL_TEXT, LLAssetType::AT_LSL_TEXT,
LLInventoryType::IT_LSL, LLInventoryType::IT_LSL,
PERM_MOVE | PERM_TRANSFER, PERM_MOVE | PERM_TRANSFER,
created_cb); // overridden in create_new_item created_cb); // overridden in create_new_item
// <FS:PP>
}
// </FS:PP>
} }
else if ("notecard" == type_name) else if ("notecard" == type_name)
{ {

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<floater name="splash_screen_settings" title="Giriş Ekranı Seçimləri">
<text name="description">
Giriş ekranının göstərilmə seçimlərini burada tənzimləyin. Bu seçimlər baxış proqramını növbəti dəfə yenidən başlatdığınız zaman qüvvəyə minəcək.
</text>
<text name="section_visibility">
Bölmənin Görünürlüyü:
</text>
<check_box label="Üst paneli gizlət" name="hide_top_bar" />
<check_box label="Bloqlar bölməsini gizlət" name="hide_blogs" />
<check_box label="Təyinat yerlərini gizlət" name="hide_destinations" />
<text name="section_accessibility">
Əlçatanlıq Seçimləri:
</text>
<check_box label="Boz rəng rejimini aktivləşdir" name="use_gray_mode" />
<check_box label="Yüksək kontrast rejimini aktivləşdir" name="use_high_contrast" />
<check_box label="&quot;BÖYÜK HƏRİFLƏR&quot; rejimini aktivləşdirin" name="use_all_caps" />
<check_box label="Daha böyük şriftlərdən istifadə edin" name="use_larger_fonts"/>
<check_box label="Şəffaflıq effektlərini söndürün" name="no_transparency" />
</floater>

View File

@ -259,7 +259,7 @@
name="mesh_preview_degenerate_edge_color"/> name="mesh_preview_degenerate_edge_color"/>
<color_swatch label="Pis doldurma" <color_swatch label="Pis doldurma"
tool_tip="Önizləmə pəncərəsində degenerativ (nazik) üçbucaqların rəngini doldurun" tool_tip="Önizləmə pəncərəsində degenerativ (nazik) üçbucaqların rəngini doldurun"
name="mesh_degenerate_fill_color"/> name="mesh_preview_degenerate_fill_color"/>
</panel> </panel>
</tab_container> </tab_container>
<panel name="weights_and_warning_panel"> <panel name="weights_and_warning_panel">

View File

@ -186,7 +186,7 @@
<panel name="joints_parent_panel"> <panel name="joints_parent_panel">
<tab_container name="joints_tabs"> <tab_container name="joints_tabs">
<panel title="Beweg." name="positionRotation_panel"> <panel title="Beweg." name="positionRotation_panel">
<panel name="title"> <panel name="move_tab_panel">
<text name="av_position_updown_label"> <text name="av_position_updown_label">
Hoch/Runter: Hoch/Runter:
</text> </text>
@ -245,7 +245,7 @@
<scroll_list.columns label="Auswahl..." name="name"/> <scroll_list.columns label="Auswahl..." name="name"/>
</scroll_list> </scroll_list>
<button name="refresh_avatars" tool_tip="Liste der Avatar und Animeshe aktualisieren"/> <button name="refresh_avatars" tool_tip="Liste der Avatar und Animeshe aktualisieren"/>
<button label="Posieren starten" label_selected="Posieren stoppen" tool_tip="Posieren des ausgewählten Avatar oder Animesh starten sofern berechtigt" name="start_stop_posing_button"/> <button label="Posieren starten" label_selected="Posieren stoppen" tool_tip="Posieren des ausgewählten Avatar oder Animesh starten sofern berechtigt." name="start_stop_posing_button"/>
<button label="Auf T-Pose setzen" tool_tip="Klicken, um den ausgewählten Avatar auf eine T-Pose zu setzen" name="set_t_pose_button"/> <button label="Auf T-Pose setzen" tool_tip="Klicken, um den ausgewählten Avatar auf eine T-Pose zu setzen" name="set_t_pose_button"/>
</panel> </panel>
<panel title="Einst." name="settings_panel"> <panel title="Einst." name="settings_panel">
@ -306,6 +306,9 @@
<button name="toggle_LockWorldRotation" tool_tip="Rotation In-World für die ausgewählten Glieder sperren. Gesperrte Glieder behalten dieselbe Rotation, wenn ihre Eltern-Glieder bewegt werden. Beispiel: Wenn die Augen gesperrt sind und der Kopf gedreht wird, schauen die Augen weiterhin (fast) in dieselbe Richtung."/> <button name="toggle_LockWorldRotation" tool_tip="Rotation In-World für die ausgewählten Glieder sperren. Gesperrte Glieder behalten dieselbe Rotation, wenn ihre Eltern-Glieder bewegt werden. Beispiel: Wenn die Augen gesperrt sind und der Kopf gedreht wird, schauen die Augen weiterhin (fast) in dieselbe Richtung."/>
<button label="Spieg." name="button_toggleMirrorRotation" tool_tip="Änderungen an gegenüberliegendem Gelenk spiegeln."/> <button label="Spieg." name="button_toggleMirrorRotation" tool_tip="Änderungen an gegenüberliegendem Gelenk spiegeln."/>
<button label="Sym." name="button_toggleSympatheticRotation" tool_tip="Gegenüberliegendes Gelenk gleichermaßen anpassen."/> <button label="Sym." name="button_toggleSympatheticRotation" tool_tip="Gegenüberliegendes Gelenk gleichermaßen anpassen."/>
<button label="W" name="poser_world_frame_toggle" tool_tip="Rotationsänderung relativ zur Welt durchführen"/>
<button label="A" name="poser_avatar_frame_toggle" tool_tip="Rotationsänderung relativ zum Avatar durchführen"/>
<button label="B" name="poser_screen_frame_toggle" tool_tip="Rotationsänderung relativ zum Bildschirm durchführen"/>
<button label="Kopie L &gt; P" name="button_symmetrize_left_to_right" tool_tip="Klicken, um Änderung von linker Seite zur rechten Seite zu kopieren." /> <button label="Kopie L &gt; P" name="button_symmetrize_left_to_right" tool_tip="Klicken, um Änderung von linker Seite zur rechten Seite zu kopieren." />
<button label="Kopie P &gt; L" name="button_symmetrize_right_to_left" tool_tip="Klicken, um Änderung von rechter Seite zur linken Seite zu kopieren." /> <button label="Kopie P &gt; L" name="button_symmetrize_right_to_left" tool_tip="Klicken, um Änderung von rechter Seite zur linken Seite zu kopieren." />
</panel> </panel>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<floater name="splash_screen_settings" title="Splash Screen Einstellungen">
<text name="description">
Konfigurieren Sie hier die Anzeigeoptionen für den Splash Screen. Diese Einstellungen werden beim nächsten Neustart des Viewers wirksam.
</text>
<text name="section_visibility">
Sichtbarkeit des Bereichs:
</text>
<check_box label="Obere Leiste ausblenden" name="hide_top_bar" />
<check_box label="Blogbereich ausblenden" name="hide_blogs" />
<check_box label="Ziele ausblenden" name="hide_destinations" />
<text name="section_accessibility">
Barrierefreiheitsoptionen:
</text>
<check_box label="Graustufenmodus aktivieren" name="use_gray_mode" />
<check_box label="Modus für hohen Kontrast aktivieren" name="use_high_contrast" />
<check_box label="„GROSSBUCHSTABEN“-Modus aktivieren" name="use_all_caps" />
<check_box label="Größere Schriftarten verwenden" name="use_larger_fonts"/>
<check_box label="Transparenzeffekte deaktivieren" name="no_transparency" />
</floater>

View File

@ -481,7 +481,7 @@
Physik-Probleme: Physik-Probleme:
</text> </text>
<color_swatch label="Schlechte Kante" tool_tip="Farbe für degenerierte (dünne) Dreiecks-Kanten im Vorschau-Fenster" name="mesh_preview_degenerate_edge_color"/> <color_swatch label="Schlechte Kante" tool_tip="Farbe für degenerierte (dünne) Dreiecks-Kanten im Vorschau-Fenster" name="mesh_preview_degenerate_edge_color"/>
<color_swatch label="Schlechte Dreiecke" tool_tip="Füllfarbe für degenerierte (dünne) Dreiecke im Vorschau-Fenster" name="mesh_degenerate_fill_color"/> <color_swatch label="Schlechte Dreiecke" tool_tip="Füllfarbe für degenerierte (dünne) Dreiecke im Vorschau-Fenster" name="mesh_preview_degenerate_fill_color"/>
</panel> </panel>
</tab_container> </tab_container>
<panel name="weights_and_warning_panel"> <panel name="weights_and_warning_panel">

View File

@ -18,6 +18,7 @@
<menu_item_call label="Bezahlen" name="Pay"/> <menu_item_call label="Bezahlen" name="Pay"/>
<menu_item_call label="Name in Zwischenablage kopieren" name="copy name"/> <menu_item_call label="Name in Zwischenablage kopieren" name="copy name"/>
<menu_item_call label="Url in Zwischenablage kopieren" name="copy url"/> <menu_item_call label="Url in Zwischenablage kopieren" name="copy url"/>
<menu_item_call label="Erwähnung-Url in Zwischenablage kopieren" name="mention_copy_label"/>
<menu_item_call label="Missbrauch melden" name="Report Abuse"/> <menu_item_call label="Missbrauch melden" name="Report Abuse"/>
<menu_item_check label="Voice blockieren" name="Block Unblock"/> <menu_item_check label="Voice blockieren" name="Block Unblock"/>
<menu_item_check label="Chat blockieren" name="Mute Text"/> <menu_item_check label="Chat blockieren" name="Mute Text"/>
@ -28,4 +29,5 @@
<menu_item_call label="Stummschaltung für diesen Teilnehmer aufheben" name="ModerateVoiceUnMuteSelected"/> <menu_item_call label="Stummschaltung für diesen Teilnehmer aufheben" name="ModerateVoiceUnMuteSelected"/>
</context_menu> </context_menu>
<menu_item_call label="Mitglied verbannen" name="BanMember"/> <menu_item_call label="Mitglied verbannen" name="BanMember"/>
<menu_item_call label="Einwohner in Chat erwähnen" name="mention_in_chat"/>
</toggleable_menu> </toggleable_menu>

View File

@ -12,7 +12,8 @@
<menu_item_call label="Freund verfolgen" name="track_agent"/> <menu_item_call label="Freund verfolgen" name="track_agent"/>
<menu_item_call label="Freund entfernen..." name="remove_friend"/> <menu_item_call label="Freund entfernen..." name="remove_friend"/>
<menu_item_call label="Name in Zwischenablage kopieren" name="url_copy_label"/> <menu_item_call label="Name in Zwischenablage kopieren" name="url_copy_label"/>
<menu_item_call label="SLurl in die Zwischenablage kopieren" name="url_copy"/> <menu_item_call label="Url in die Zwischenablage kopieren" name="url_copy"/>
<menu_item_call label="Erwähnung-Url in Zwischenablage kopieren" name="mention_copy_label"/>
<menu name="options" label="Optionen..."> <menu name="options" label="Optionen...">
<menu_item_check label="Benutzername anzeigen" name="show_username"/> <menu_item_check label="Benutzername anzeigen" name="show_username"/>
<menu_item_check label="Anzeigename anzeigen" name="show_displayname"/> <menu_item_check label="Anzeigename anzeigen" name="show_displayname"/>

View File

@ -23,4 +23,6 @@
<menu_item_call label="Alle freischalten" name="ModerateVoiceUnmute"/> <menu_item_call label="Alle freischalten" name="ModerateVoiceUnmute"/>
</context_menu> </context_menu>
<menu_item_call label="Mitglied verbannen" name="BanMember"/> <menu_item_call label="Mitglied verbannen" name="BanMember"/>
<menu_item_call label="Erwähnung-Url in Zwischenablage kopieren" name="mention_copy_label"/>
<menu_item_call label="Einwohner in Chat erwähnen" name="mention_in_chat"/>
</context_menu> </context_menu>

View File

@ -20,5 +20,7 @@
<menu_item_call label="Aus Gruppe entfernen" name="BanEjectMember"/> <menu_item_call label="Aus Gruppe entfernen" name="BanEjectMember"/>
</context_menu> </context_menu>
<menu_item_call label="Name in Zwischenablage kopieren" name="url_copy_label"/> <menu_item_call label="Name in Zwischenablage kopieren" name="url_copy_label"/>
<menu_item_call label="SLurl in die Zwischenablage kopieren" name="url_copy"/> <menu_item_call label="Url in die Zwischenablage kopieren" name="url_copy"/>
<menu_item_call label="Erwähnung-Url in Zwischenablage kopieren" name="mention_copy_label"/>
<menu_item_call label="Einwohner in Chat erwähnen" name="mention_in_chat"/>
</context_menu> </context_menu>

View File

@ -5844,6 +5844,12 @@ Welche Bezeichnung soll für die Region
Neue Auswahl kann nicht erstellt werden, da bereits die maximale Anzahl an Auswahlen erstellt wurde. Neue Auswahl kann nicht erstellt werden, da bereits die maximale Anzahl an Auswahlen erstellt wurde.
<usetemplate name="okbutton" yestext="OK"/> <usetemplate name="okbutton" yestext="OK"/>
</notification> </notification>
<notification name="ConfirmGlobalOnlineStatusToggle">
Sind Sie sicher, dass Sie die Sichtbarkeit Ihres Online-Status für alle Ihre Freunde auf einmal ändern möchten?
Abhängig von der Serverauslastung kann es einen Moment dauern, bis das Umschalten der Sichtbarkeit des Online-Status effektiv wird, und es kann zeitweise zu Problemen mit Ihrem Online-Status für Ihre Freunde führen.
<usetemplate name="okcancelbuttons" notext="Abbrechen" yestext="OK"/>
</notification>
<notification name="GlobalOnlineStatusToggle"> <notification name="GlobalOnlineStatusToggle">
Abhängig von der Serverauslastung kann es einen Moment dauern, bis das Umschalten der Sichtbarkeit des Online-Status effektiv wird. Abhängig von der Serverauslastung kann es einen Moment dauern, bis das Umschalten der Sichtbarkeit des Online-Status effektiv wird.
<usetemplate ignoretext="Weise mich darauf hin, dass das Umschalten der Sichtbarkeit des Online-Status etwas dauern kann." name="okignore" yestext="OK"/> <usetemplate ignoretext="Weise mich darauf hin, dass das Umschalten der Sichtbarkeit des Online-Status etwas dauern kann." name="okignore" yestext="OK"/>
@ -5915,7 +5921,7 @@ https://wiki.firestormviewer.org/antivirus_whitelisting
<usetemplate name="okcancelbuttons" notext="Abbrechen" yestext="Okay"/> <usetemplate name="okcancelbuttons" notext="Abbrechen" yestext="Okay"/>
</notification> </notification>
<notification name="FSLargeOutfitsWarningInThisSession"> <notification name="FSLargeOutfitsWarningInThisSession">
Eine große Anzahl an Outfits wurde erkannt: [AMOUNT]. Dies kann zu einem Blockieren des Viewers oder zu Verbindungsabbrüchen führen. Ziehen Sie eine Reduzierung der Outfits für eine bessere Performance in Betracht (unter [MAX]). Eine große Anzahl an Outfits wurde erkannt: [AMOUNT]. Dies kann zu einem Blockieren des Viewers oder zu Verbindungsabbrüchen führen. Ziehen Sie eine Reduzierung der Outfits für eine bessere Performance in Betracht (unter [MAX]). DIES IST NUR EIN VORSCHLAG - wenn Ihr Computer normal funktioniert, können Sie diesen Vorschlag bedenkenlos ignorieren.
<usetemplate ignoretext="Warnung bei zu vielen Outfits" name="okignore" yestext="OK" /> <usetemplate ignoretext="Warnung bei zu vielen Outfits" name="okignore" yestext="OK" />
</notification> </notification>
<notification name="PrimfeedLoginRequestFailed"> <notification name="PrimfeedLoginRequestFailed">

View File

@ -7303,4 +7303,22 @@ Ihre aktuelle Position: [AVATAR_POS]
<string name="ImportSuccessful"> <string name="ImportSuccessful">
[COUNT] Einträge erfolgreich verarbeitet. [COUNT] Einträge erfolgreich verarbeitet.
</string> </string>
<string name="Flood">
Füllen
</string>
<string name="Surface Only">
Nur Oberfläche
</string>
<string name="Raycast">
Raycast
</string>
<string name="Very High">
Sehr Hoch
</string>
<string name="Ultra">
Ultra
</string>
<string name="Maximum">
Maximum
</string>
</strings> </strings>

View File

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater <floater
positioning="None" positioning="None"
save_rect="true" save_rect="true"
height="330" height="330"
layout="topleft" layout="topleft"
name="floater_poser" name="floater_poser"
title="Avatar and Animesh Poser" title="Avatar and Animesh Poser"
help_topic="poser" help_topic="poser"
width="430"> width="450">
<string name="icon_category" translate="false">Inv_BodyShape</string> <string name="icon_category" translate="false">Inv_BodyShape</string>
<string name="icon_bone" translate="false"></string> <string name="icon_bone" translate="false"></string>
<string name="icon_object" translate="false">Inv_Object</string> <string name="icon_object" translate="false">Inv_Object</string>
@ -83,8 +83,6 @@ width="430">
<string name="joint_transform_mFaceCheekLowerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string> <string name="joint_transform_mFaceCheekLowerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
<string name="joint_transform_mFaceCheekUpperRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string> <string name="joint_transform_mFaceCheekUpperRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
<string name="joint_transform_mFaceCheekLowerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string> <string name="joint_transform_mFaceCheekLowerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
<string name="joint_transform_mFaceLipUpperLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
<string name="joint_transform_mFaceLipUpperRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
<string name="joint_transform_mHandThumb1Left" translate="false">SWAP_ROLL_AND_PITCH</string> <string name="joint_transform_mHandThumb1Left" translate="false">SWAP_ROLL_AND_PITCH</string>
<string name="joint_transform_mHandThumb1Right" translate="false">SWAP_ROLL_AND_PITCH NEGATE_PITCH</string> <string name="joint_transform_mHandThumb1Right" translate="false">SWAP_ROLL_AND_PITCH NEGATE_PITCH</string>
<string name="joint_transform_mHandThumb2Left" translate="false">SWAP_ROLL_AND_PITCH</string> <string name="joint_transform_mHandThumb2Left" translate="false">SWAP_ROLL_AND_PITCH</string>
@ -160,6 +158,105 @@ width="430">
<string name="joint_transform_mFaceForeheadCenter" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string> <string name="joint_transform_mFaceForeheadCenter" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
<string name="joint_transform_mFaceForeheadRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string> <string name="joint_transform_mFaceForeheadRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_PITCH</string>
<!-- Begins with joint_transform_ then has an internal joint name, ONE swap choice of:
SWAP_NOTHING, SWAP_YAW_AND_ROLL, SWAP_YAW_AND_PITCH , SWAP_ROLL_AND_PITCH, SWAP_X2Y_Y2Z_Z2X, SWAP_X2Z_Y2X_Z2Y -->
<string name="joint_frame_mPelvis" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mTorso" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mChest" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mNeck" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHead" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mCollarLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mShoulderLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mElbowLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mWristLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mCollarRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mShoulderRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mElbowRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mWristRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mHipLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mKneeLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mAnkleLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mFootLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mToeLeft" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mHipRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mKneeRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mAnkleRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mFootRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mToeRight" translate="false">NEGATE_ROLL NEGATE_PITCH</string>
<string name="joint_frame_mHandThumb1Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandThumb1Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandThumb2Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandThumb2Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandThumb3Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandThumb3Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandIndex1Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandIndex1Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandIndex2Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandIndex2Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandIndex3Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandIndex3Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandMiddle1Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandMiddle1Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandMiddle2Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandMiddle2Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandMiddle3Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandMiddle3Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandRing1Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandRing1Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandRing2Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandRing2Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandRing3Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandRing3Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandPinky1Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandPinky1Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandPinky2Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandPinky2Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandPinky3Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mHandPinky3Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mEyeRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mEyeLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceTeethLower" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceTeethUpper" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceLipCornerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceLipCornerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceLipUpperLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceLipUpperCenter" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceLipUpperRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceLipLowerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceLipLowerCenter" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceLipLowerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEyeLidUpperLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEyeLidUpperRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEyecornerInnerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEyecornerInnerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEyeLidLowerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEyeLidLowerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEyebrowOuterLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEyebrowOuterRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEyebrowCenterLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEyebrowCenterRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEyebrowInnerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEyebrowInnerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceCheekUpperLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceCheekLowerLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceCheekUpperRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceCheekLowerRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceTongueBase" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceTongueTip" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEar1Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEar2Left" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEar1Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceEar2Right" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceNoseBase" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceNoseBridge" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceNoseLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceNoseCenter" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceNoseRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceForeheadLeft" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceForeheadCenter" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceForeheadRight" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<string name="joint_frame_mFaceJaw" translate="false">SWAP_YAW_AND_ROLL NEGATE_ALL</string>
<!-- For the joints/bones/thingos that are apt to Gimbal Lock, we may instead apply rotations as deltas by default, which never lock --> <!-- For the joints/bones/thingos that are apt to Gimbal Lock, we may instead apply rotations as deltas by default, which never lock -->
<string name="joint_delta_rotate_mHipLeft">true</string> <string name="joint_delta_rotate_mHipLeft">true</string>
<string name="joint_delta_rotate_mHipRight">true</string> <string name="joint_delta_rotate_mHipRight">true</string>
@ -416,7 +513,7 @@ width="430">
layout="topleft" layout="topleft"
mouse_opaque="false" mouse_opaque="false"
left="5" left="5"
name="title" name="move_tab_panel"
top="0" top="0"
width="235"> width="235">
<text follows="left|top" <text follows="left|top"
@ -873,7 +970,7 @@ width="430">
name="refresh_avatars" name="refresh_avatars"
tool_tip="Refresh the list of avatars and animeshes" tool_tip="Refresh the list of avatars and animeshes"
width="20" width="20"
top="232" top="221"
left="3"> left="3">
<button.commit_callback <button.commit_callback
function="Poser.RefreshAvatars"/> function="Poser.RefreshAvatars"/>
@ -894,7 +991,7 @@ width="430">
flash_color="0.7 0.7 1 1" flash_color="0.7 0.7 1 1"
button_flash_count="64" button_flash_count="64"
button_flash_rate="0.5" button_flash_rate="0.5"
tool_tip="Start posing the selected avatar or animesh, if you are allowed to" tool_tip="Start posing the selected avatar or animesh, if you are allowed to."
name="start_stop_posing_button" name="start_stop_posing_button"
width="150"> width="150">
<button.commit_callback <button.commit_callback
@ -1018,7 +1115,7 @@ width="430">
label="Show joint markers" label="Show joint markers"
follows="left|top" follows="left|top"
left="5" left="5"
tool_tip="Show small indicators to aid joint selection when visually posing." tool_tip="Show small indicators to aid joint selection when posing."
top_pad="5" top_pad="5"
width="134" /> width="134" />
<check_box <check_box
@ -1060,7 +1157,7 @@ width="430">
name="joint_manipulation_panel" name="joint_manipulation_panel"
enabled="true" enabled="true"
top="0" top="0"
width="215" width="235"
left_pad="0"> left_pad="0">
<tab_container <tab_container
follows="all" follows="all"
@ -1073,7 +1170,7 @@ width="430">
tab_group="1" tab_group="1"
tab_position="top" tab_position="top"
top_pad="0" top_pad="0"
width="215"> width="235">
<panel <panel
follows="left|right|top|bottom" follows="left|right|top|bottom"
height="265" height="265"
@ -1084,7 +1181,7 @@ width="430">
name="trackball_panel" name="trackball_panel"
enabled="false" enabled="false"
top="0" top="0"
width="215" width="235"
left="0"> left="0">
<fs_virtual_trackpad <fs_virtual_trackpad
name="limb_rotation" name="limb_rotation"
@ -1092,7 +1189,7 @@ width="430">
top="2" top="2"
height="160" height="160"
width="160" width="160"
left="15" left="30"
tool_tip="Change the rotation of the currently selected body part(s). Hold Ctrl to move slow. Roll the wheel to adjust the 3rd axis. Use Shift or Alt to swap which rotations change" tool_tip="Change the rotation of the currently selected body part(s). Hold Ctrl to move slow. Roll the wheel to adjust the 3rd axis. Use Shift or Alt to swap which rotations change"
pinch_mode="false" pinch_mode="false"
infinite_scroll_mode="true"/> infinite_scroll_mode="true"/>
@ -1103,7 +1200,7 @@ width="430">
layout="topleft" layout="topleft"
left="5" left="5"
top_pad="2" top_pad="2"
width="200"> width="220">
Up/Down: Up/Down:
</text> </text>
<slider <slider
@ -1120,7 +1217,7 @@ width="430">
max_val="180" max_val="180"
name="limb_pitch_slider" name="limb_pitch_slider"
top_pad="2" top_pad="2"
width="130" > width="150">
<slider.commit_callback <slider.commit_callback
function="Poser.CommitSlider" function="Poser.CommitSlider"
parameter="4"/> parameter="4"/>
@ -1147,7 +1244,7 @@ width="430">
layout="topleft" layout="topleft"
left="5" left="5"
top_pad="2" top_pad="2"
width="200"> width="220">
Left/Right: Left/Right:
</text> </text>
<slider <slider
@ -1164,7 +1261,7 @@ width="430">
max_val="180" max_val="180"
name="limb_yaw_slider" name="limb_yaw_slider"
top_pad="2" top_pad="2"
width="130" > width="150">
<slider.commit_callback <slider.commit_callback
function="Poser.CommitSlider" function="Poser.CommitSlider"
parameter="5"/> parameter="5"/>
@ -1191,7 +1288,7 @@ width="430">
layout="topleft" layout="topleft"
left="5" left="5"
top_pad="2" top_pad="2"
width="200"> width="220">
Roll: Roll:
</text> </text>
<slider <slider
@ -1208,7 +1305,7 @@ width="430">
max_val="180" max_val="180"
name="limb_roll_slider" name="limb_roll_slider"
top_pad="2" top_pad="2"
width="130" > width="150">
<slider.commit_callback <slider.commit_callback
function="Poser.CommitSlider" function="Poser.CommitSlider"
parameter="6"/> parameter="6"/>
@ -1238,7 +1335,7 @@ width="430">
title="Move / Scale" title="Move / Scale"
name="position_panel" name="position_panel"
top="0" top="0"
width="170"> width="190">
<text <text
follows="left|top" follows="left|top"
name="pos_x_label" name="pos_x_label"
@ -1246,7 +1343,7 @@ width="430">
layout="topleft" layout="topleft"
left="5" left="5"
top_pad="2" top_pad="2"
width="200"> width="220">
Position X: Position X:
</text> </text>
<slider <slider
@ -1263,7 +1360,7 @@ width="430">
min_val="-1.5" min_val="-1.5"
name="Advanced_Position_X" name="Advanced_Position_X"
top_pad="2" top_pad="2"
width="130" > width="150">
<slider.commit_callback <slider.commit_callback
function="Poser.CommitSlider" function="Poser.CommitSlider"
parameter="7"/> parameter="7"/>
@ -1290,7 +1387,7 @@ width="430">
layout="topleft" layout="topleft"
left="5" left="5"
top_pad="2" top_pad="2"
width="200"> width="220">
Position Y: Position Y:
</text> </text>
<slider <slider
@ -1307,7 +1404,7 @@ width="430">
min_val="-1.5" min_val="-1.5"
name="Advanced_Position_Y" name="Advanced_Position_Y"
top_pad="2" top_pad="2"
width="130" > width="150">
<slider.commit_callback <slider.commit_callback
function="Poser.CommitSlider" function="Poser.CommitSlider"
parameter="8"/> parameter="8"/>
@ -1334,7 +1431,7 @@ width="430">
layout="topleft" layout="topleft"
left="5" left="5"
top_pad="2" top_pad="2"
width="200"> width="220">
Position Z: Position Z:
</text> </text>
<slider <slider
@ -1351,7 +1448,7 @@ width="430">
min_val="-1.5" min_val="-1.5"
name="Advanced_Position_Z" name="Advanced_Position_Z"
top_pad="2" top_pad="2"
width="130" > width="150">
<slider.commit_callback <slider.commit_callback
function="Poser.CommitSlider" function="Poser.CommitSlider"
parameter="9"/> parameter="9"/>
@ -1378,7 +1475,7 @@ width="430">
layout="topleft" layout="topleft"
left="5" left="5"
top_pad="15" top_pad="15"
width="200"> width="220">
Scale X: Scale X:
</text> </text>
<slider <slider
@ -1395,7 +1492,7 @@ width="430">
min_val="-1.5" min_val="-1.5"
name="Advanced_Scale_X" name="Advanced_Scale_X"
top_pad="2" top_pad="2"
width="130" > width="150">
<slider.commit_callback <slider.commit_callback
function="Poser.CommitSlider" function="Poser.CommitSlider"
parameter="10"/> parameter="10"/>
@ -1422,7 +1519,7 @@ width="430">
layout="topleft" layout="topleft"
left="5" left="5"
top_pad="2" top_pad="2"
width="200"> width="220">
Scale Y: Scale Y:
</text> </text>
<slider <slider
@ -1439,7 +1536,7 @@ width="430">
min_val="-1.5" min_val="-1.5"
name="Advanced_Scale_Y" name="Advanced_Scale_Y"
top_pad="2" top_pad="2"
width="130" > width="150">
<slider.commit_callback <slider.commit_callback
function="Poser.CommitSlider" function="Poser.CommitSlider"
parameter="11"/> parameter="11"/>
@ -1466,7 +1563,7 @@ width="430">
layout="topleft" layout="topleft"
left="5" left="5"
top_pad="2" top_pad="2"
width="200"> width="220">
Scale Z: Scale Z:
</text> </text>
<slider <slider
@ -1483,7 +1580,7 @@ width="430">
min_val="-1.5" min_val="-1.5"
name="Advanced_Scale_Z" name="Advanced_Scale_Z"
top_pad="2" top_pad="2"
width="130" > width="150">
<slider.commit_callback <slider.commit_callback
function="Poser.CommitSlider" function="Poser.CommitSlider"
parameter="12"/> parameter="12"/>
@ -1514,7 +1611,7 @@ width="430">
left="0" left="0"
top_pad="2" top_pad="2"
name="trackball_button_panel" name="trackball_button_panel"
width="232"> width="252">
<button <button
height="21" height="21"
follows="top|left" follows="top|left"
@ -1583,7 +1680,7 @@ width="430">
left_pad="1" left_pad="1"
top_delta="0" top_delta="0"
tool_tip="Lock rotation in the world for the selected limb(s). Limbs that are world-locked keep the same rotation in-world when their parent-limbs move. Eg: lock your eyes and turn your head, your eyes keep looking (mostly) in the same direction." tool_tip="Lock rotation in the world for the selected limb(s). Limbs that are world-locked keep the same rotation in-world when their parent-limbs move. Eg: lock your eyes and turn your head, your eyes keep looking (mostly) in the same direction."
width="18" > width="18">
</button> </button>
<button <button
follows="left|top" follows="left|top"
@ -1602,7 +1699,7 @@ width="430">
tool_tip="Mirror changes to the opposite joint." tool_tip="Mirror changes to the opposite joint."
left_pad="1" left_pad="1"
top_delta="0" top_delta="0"
width="43" > width="53">
<button.commit_callback <button.commit_callback
function="Poser.ToggleMirrorChanges"/> function="Poser.ToggleMirrorChanges"/>
</button> </button>
@ -1623,34 +1720,112 @@ width="430">
tool_tip="Copy changes to the opposite joint." tool_tip="Copy changes to the opposite joint."
left_pad="1" left_pad="1"
top_delta="0" top_delta="0"
width="43" > width="53">
<button.commit_callback <button.commit_callback
function="Poser.ToggleSympatheticChanges"/> function="Poser.ToggleSympatheticChanges"/>
</button> </button>
<button
follows="left|top"
height="18"
is_toggle="true"
layout="topleft"
label="W"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
button_flash_enable="true"
flash_color="0.7 0.7 1 1"
button_flash_count="64"
button_flash_rate="0.5"
name="poser_world_frame_toggle"
left_pad="1"
left="2"
top_pad="0"
tool_tip="Make rotational changes relative to World"
width="18">
<button.commit_callback
function="Poser.ToggleRotationFrame"/>
</button>
<button
follows="left|top"
height="18"
is_toggle="true"
layout="topleft"
label="A"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
button_flash_enable="true"
flash_color="0.7 0.7 1 1"
button_flash_count="64"
button_flash_rate="0.5"
name="poser_avatar_frame_toggle"
left_pad="0"
top_delta="0"
tool_tip="Make rotational changes relative to Avatar"
width="15">
<button.commit_callback
function="Poser.ToggleRotationFrame"/>
</button>
<button
follows="left|top"
height="18"
is_toggle="true"
layout="topleft"
label="S"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
button_flash_enable="true"
flash_color="0.7 0.7 1 1"
button_flash_count="64"
button_flash_rate="0.5"
name="poser_screen_frame_toggle"
left_pad="0"
top_delta="0"
tool_tip="Make rotational changes relative to Screen"
width="15">
<button.commit_callback
function="Poser.ToggleRotationFrame"/>
</button>
<button <button
follows="left|top" follows="left|top"
height="18" height="18"
layout="topleft" layout="topleft"
label="Copy L &gt; R" label="Copy L &gt; R"
name="button_symmetrize_left_to_right" name="button_symmetrize_left_to_right"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
button_flash_enable="true"
flash_color="0.7 0.7 1 1"
button_flash_count="64"
button_flash_rate="0.5"
tool_tip="Click to copy change from left side to right side." tool_tip="Click to copy change from left side to right side."
left="14" left_pad="1"
top_pad="0" top_delta="0"
width="70" > width="77">
<button.commit_callback <button.commit_callback
function="Poser.Symmetrize" function="Poser.Symmetrize"
parameter="1"/> parameter="1"/>
</button> </button>
<button <button
follows="left|top" follows="left|top"
height="19" height="18"
layout="topleft" layout="topleft"
label="Copy R &gt; L" label="Copy R &gt; L"
name="button_symmetrize_right_to_left" name="button_symmetrize_right_to_left"
image_hover_unselected="Toolbar_Middle_Over"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
button_flash_enable="true"
flash_color="0.7 0.7 1 1"
button_flash_count="64"
button_flash_rate="0.5"
tool_tip="Click to copy change from right side to left side." tool_tip="Click to copy change from right side to left side."
left_pad="23" left_pad="1"
top_delta="0" top_delta="0"
width="70" > width="77">
<button.commit_callback <button.commit_callback
function="Poser.Symmetrize" function="Poser.Symmetrize"
parameter="2"/> parameter="2"/>
@ -1795,7 +1970,7 @@ width="430">
left_pad="0" left_pad="0"
top_delta="0" top_delta="0"
name="button_spacer_panel" name="button_spacer_panel"
width="80"/> width="82"/>
<button <button
follows="left|top" follows="left|top"
height="21" height="21"
@ -1816,7 +1991,7 @@ width="430">
left_pad="1" left_pad="1"
top_delta="0" top_delta="0"
tool_tip="Load, save and manage the poses you make" tool_tip="Load, save and manage the poses you make"
width="105" > width="125" >
<button.commit_callback <button.commit_callback
function="Poser.ToggleLoadSavePanel"/> function="Poser.ToggleLoadSavePanel"/>
</button> </button>

View File

@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<floater
name="splash_screen_settings"
title="Splash Screen Settings"
width="350"
height="320"
min_width="350"
min_height="300"
save_rect="true"
single_instance="true"
can_resize="false"
can_minimize="true"
can_close="true"
can_drag_on_left="false">
<text
follows="left|top"
height="40"
layout="topleft"
left="10"
name="description"
top="10"
word_wrap="true"
width="330">
Configure splash screen display options here. These settings will take effect after the next viewer restart.
</text>
<text
follows="left|top"
height="16"
layout="topleft"
left="10"
name="section_visibility"
top_pad="10"
width="330"
font="SansSerifBold">
Section Visibility:
</text>
<check_box
control_name="FSSplashScreenHideTopBar"
height="20"
label="Hide top bar"
layout="topleft"
left="20"
name="hide_top_bar"
top_pad="5"
width="310"/>
<check_box
control_name="FSSplashScreenHideBlogs"
height="20"
label="Hide blogs section"
layout="topleft"
left="20"
name="hide_blogs"
top_pad="2"
width="310"/>
<check_box
control_name="FSSplashScreenHideDestinations"
height="20"
label="Hide destinations section"
layout="topleft"
left="20"
name="hide_destinations"
top_pad="2"
width="310"/>
<text
follows="left|top"
height="16"
layout="topleft"
left="10"
name="section_accessibility"
top_pad="15"
width="330"
font="SansSerifBold">
Accessibility Options:
</text>
<check_box
control_name="FSSplashScreenUseGrayMode"
height="20"
label="Enable grayscale mode"
layout="topleft"
left="20"
name="use_gray_mode"
top_pad="5"
width="310"/>
<check_box
control_name="FSSplashScreenUseHighContrast"
height="20"
label="Enable high contrast mode"
layout="topleft"
left="20"
name="use_high_contrast"
top_pad="2"
width="310"/>
<check_box
control_name="FSSplashScreenUseAllCaps"
height="20"
label="Enable all caps mode"
layout="topleft"
left="20"
name="use_all_caps"
top_pad="2"
width="310"/>
<check_box
control_name="FSSplashScreenUseLargerFonts"
height="20"
label="Use larger fonts"
layout="topleft"
left="20"
name="use_larger_fonts"
top_pad="2"
width="310"/>
<check_box
control_name="FSSplashScreenNoTransparency"
height="20"
label="Disable transparency effects"
layout="topleft"
left="20"
name="no_transparency"
top_pad="2"
width="310"/>
</floater>

View File

@ -1559,7 +1559,6 @@
height="306" height="306"
layout="topleft" layout="topleft"
left="3" left="3"
ignore_tab="false"
right="-21" right="-21"
name="log_tab_border" name="log_tab_border"
top_pad="0" top_pad="0"
@ -1605,7 +1604,6 @@
height="306" height="306"
layout="topleft" layout="topleft"
left="3" left="3"
ignore_tab="false"
right="-21" right="-21"
name="mesh_preview_settings_tab_border" name="mesh_preview_settings_tab_border"
top_pad="0" top_pad="0"
@ -1952,7 +1950,7 @@
can_apply_immediately="true" can_apply_immediately="true"
label="Bad Triangle Fill" label="Bad Triangle Fill"
tool_tip="Fill color for degenerate (thin) triangles in preview window on the Mesh uploader" tool_tip="Fill color for degenerate (thin) triangles in preview window on the Mesh uploader"
name="mesh_degenerate_fill_color"/> name="mesh_preview_degenerate_fill_color"/>
</panel> </panel>
</tab_container> </tab_container>
<panel <panel
@ -2213,7 +2211,6 @@ Analysed:
Preview controls Preview controls
</text> </text>
<combo_box <combo_box
can_resize="false"
follows="top|left" follows="top|left"
left="-85" left="-85"
top_delta="-2" top_delta="-2"
@ -2236,7 +2233,6 @@ Analysed:
left="3" left="3"
name="preview_controls_border" name="preview_controls_border"
top_pad="5" top_pad="5"
halign="center"
width="250"/> width="250"/>
<check_box <check_box
follows="top|left" follows="top|left"

View File

@ -129,6 +129,14 @@
name="copy url"> name="copy url">
<on_click function="AvatarIcon.Action" parameter="copy_url" /> <on_click function="AvatarIcon.Action" parameter="copy_url" />
</menu_item_call> </menu_item_call>
<!-- <FS:Zi> Add menu items to copy and/or insert mention URIs into chat -->
<menu_item_call
label="Copy Mention URI to clipboard"
layout="topleft"
name="mention_copy_label">
<on_click function="Mention.CopyURI" />
</menu_item_call>
<!-- </FS:Zi> -->
<menu_item_separator layout="topleft" name="separator_block"/> <menu_item_separator layout="topleft" name="separator_block"/>
<menu_item_call <menu_item_call
label="Report Abuse" label="Report Abuse"
@ -195,4 +203,16 @@
<on_click function="AvatarIcon.Action" parameter="ban_member" /> <on_click function="AvatarIcon.Action" parameter="ban_member" />
<on_enable function="AvatarIcon.Enable" parameter="can_ban_member" /> <on_enable function="AvatarIcon.Enable" parameter="can_ban_member" />
</menu_item_call> </menu_item_call>
<!-- <FS:Zi> Add menu items to copy and/or insert mention URIs into chat -->
<menu_item_separator
name="MentionURISeparator"
layout="topleft" />
<menu_item_call
label="Mention User in Chat"
layout="topleft"
name="mention_in_chat">
<menu_item_call.on_click
function="Mention.Chat" />
</menu_item_call>
<!-- </FS:Zi> -->
</toggleable_menu> </toggleable_menu>

View File

@ -115,6 +115,15 @@
<menu_item_call.on_click <menu_item_call.on_click
function="Contacts.Friends.CopyUrl"/> function="Contacts.Friends.CopyUrl"/>
</menu_item_call> </menu_item_call>
<!-- <FS:Zi> Add menu items to copy and/or insert mention URIs into chat -->
<menu_item_call
label="Copy Mention URI to clipboard"
layout="topleft"
name="mention_copy_label">
<menu_item_call.on_click
function="Mention.CopyURI" />
</menu_item_call>
<!-- </FS:Zi> -->
<menu_item_separator/> <menu_item_separator/>
<menu name="options" label="Options..."> <menu name="options" label="Options...">
<menu_item_check <menu_item_check

View File

@ -241,4 +241,21 @@
<on_click function="Avatar.BanMember" parameter="ban_member" /> <on_click function="Avatar.BanMember" parameter="ban_member" />
<on_enable function="ParticipantList.EnableItem" parameter="can_ban_member" /> <on_enable function="ParticipantList.EnableItem" parameter="can_ban_member" />
</menu_item_call> </menu_item_call>
<!-- <FS:Zi> Add menu items to copy and/or insert mention URIs into chat -->
<menu_item_separator layout="topleft" name="MentionURISeparator"/>
<menu_item_call
label="Copy Mention URI to clipboard"
layout="topleft"
name="mention_copy_label">
<menu_item_call.on_click
function="Mention.CopyURI" />
</menu_item_call>
<menu_item_call
label="Mention User in Chat"
layout="topleft"
name="mention_in_chat">
<menu_item_call.on_click
function="Mention.Chat" />
</menu_item_call>
<!-- </FS:Zi> -->
</context_menu> </context_menu>

View File

@ -177,4 +177,23 @@
<menu_item_call.on_click <menu_item_call.on_click
function="Url.CopyUrl" /> function="Url.CopyUrl" />
</menu_item_call> </menu_item_call>
<!-- <FS:Zi> Add menu items to copy and/or insert mention URIs into chat -->
<menu_item_call
label="Copy Mention URI to clipboard"
layout="topleft"
name="mention_copy_label">
<menu_item_call.on_click
function="Mention.CopyURI" />
</menu_item_call>
<menu_item_separator
name="MentionURISeparator"
layout="topleft" />
<menu_item_call
label="Mention User in Chat"
layout="topleft"
name="mention_in_chat">
<menu_item_call.on_click
function="Mention.Chat" />
</menu_item_call>
<!-- </FS:Zi> -->
</context_menu> </context_menu>

View File

@ -14144,6 +14144,19 @@ the region &quot;[REGION]&quot;?
name="okbutton" name="okbutton"
yestext="OK"/> yestext="OK"/>
</notification> </notification>
<notification
icon="alertmodal.tga"
name="ConfirmGlobalOnlineStatusToggle"
type="alertmodal">
Are you sure you want to change your online status visibility for all friends at once?
Due to server load, this mass change can take a while to become effective and may cause temporary issues for some friends seeing your online status.
<tag>confirm</tag>
<usetemplate
name="okcancelbuttons"
notext="Cancel"
yestext="OK"/>
</notification>
<notification <notification
icon="alertmodal.tga" icon="alertmodal.tga"
name="GlobalOnlineStatusToggle" name="GlobalOnlineStatusToggle"
@ -14815,7 +14828,7 @@ https://wiki.firestormviewer.org/antivirus_whitelisting
name="FSLargeOutfitsWarningInThisSession" name="FSLargeOutfitsWarningInThisSession"
type="alertmodal"> type="alertmodal">
<unique/> <unique/>
A large number of outfits were detected: [AMOUNT]. This may cause viewer hangs or disconnects. Consider reducing the number of outfits for better performance (below [MAX]). A large number of outfits were detected: [AMOUNT]. This may cause viewer hangs or disconnects. Consider reducing the number of outfits for better performance (below [MAX]). THIS IS ONLY A SUGGESTION - if your computer is functioning normally, you can safely ignore it.
<usetemplate <usetemplate
ignoretext="Outfit count warning" ignoretext="Outfit count warning"
name="okignore" name="okignore"

View File

@ -3333,4 +3333,12 @@ Your current position: [AVATAR_POS]
<string name="ImportListTooLarge">Too many entries. The CSV file has [COUNT] entries and there are [MAX] slots available.</string> <string name="ImportListTooLarge">Too many entries. The CSV file has [COUNT] entries and there are [MAX] slots available.</string>
<string name="ImportSuccessful">Successfully processed [COUNT] entries.</string> <string name="ImportSuccessful">Successfully processed [COUNT] entries.</string>
<!-- VHACD enum strings -->
<string name="Flood">Flood</string>
<string name="Surface Only">Surface Only</string>
<string name="Raycast">Raycast</string>
<string name="Very High">Very High</string>
<string name="Ultra">Ultra</string>
<string name="Maximum">Maximum</string>
</strings> </strings>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<floater name="splash_screen_settings" title="Ajustes de la Pantalla de Inicio">
<text name="description">
Configure aquí las opciones de visualización de la pantalla de inicio. Estos ajustes tendrán efecto después del próximo reinicio del visor.
</text>
<text name="section_visibility">
Visibilidad de la sección:
</text>
<check_box label="Ocultar barra superior" name="hide_top_bar" />
<check_box label="Ocultar sección de blogs" name="hide_blogs" />
<check_box label="Ocultar destinos" name="hide_destinations" />
<text name="section_accessibility">
Opciones de Accesibilidad:
</text>
<check_box label="Habilitar el modo de escala de grises" name="use_gray_mode" />
<check_box label="Habilitar el modo de alto contraste" name="use_high_contrast" />
<check_box label="Habilitar el modo &quot;TODO EN MAYÚSCULAS&quot;" name="use_all_caps" />
<check_box label="Usa fuentes más grandes" name="use_larger_fonts"/>
<check_box label="Deshabilitar efectos de transparencia" name="no_transparency" />
</floater>

View File

@ -181,7 +181,7 @@
<panel name="joints_parent_panel"> <panel name="joints_parent_panel">
<tab_container name="joints_tabs"> <tab_container name="joints_tabs">
<panel title="Dépl." name="positionRotation_panel"> <panel title="Dépl." name="positionRotation_panel">
<panel name="title"> <panel name="move_tab_panel">
<text name="av_position_updown_label"> <text name="av_position_updown_label">
Haut/bas : Haut/bas :
</text> </text>
@ -248,7 +248,7 @@
<check_box name="also_save_bvh_checkbox" label="Ajouter BVH à l'enregis.**" tool_tip="Lorsque vous enregistrez votre pose, écrivez également un fichier BVH, qui peut être téléchargé via 'Construire > Charger > Animation' pour poser vous-même ou d'autres personnes dans le monde. Pour ce faire, les articulations doivent remettre leur « base » à zéro, car le BVH nécessite que l'on travaille sur l'original."/> <check_box name="also_save_bvh_checkbox" label="Ajouter BVH à l'enregis.**" tool_tip="Lorsque vous enregistrez votre pose, écrivez également un fichier BVH, qui peut être téléchargé via 'Construire > Charger > Animation' pour poser vous-même ou d'autres personnes dans le monde. Pour ce faire, les articulations doivent remettre leur « base » à zéro, car le BVH nécessite que l'on travaille sur l'original."/>
<check_box name="confirm_overwrite_on_save_checkbox" label="Confirmer l'écrasement" tool_tip="Lorsque vous enregistrez une pose, si le fichier existe déjà, vous devez cliquer à nouveau sur le bouton d'enregistrement pour confirmer que vous êtes sûr de vouloir l'écraser."/> <check_box name="confirm_overwrite_on_save_checkbox" label="Confirmer l'écrasement" tool_tip="Lorsque vous enregistrez une pose, si le fichier existe déjà, vous devez cliquer à nouveau sur le bouton d'enregistrement pour confirmer que vous êtes sûr de vouloir l'écraser."/>
<check_box name="natural_direction_checkbox" label="Utiliser l'alignement naturel" tool_tip="Le squelette a des rotations d'articulations non naturelles par défaut. Cela complique la pose. Si cette option est cochée, les articulations tourneront de manière plus naturelle."/> <check_box name="natural_direction_checkbox" label="Utiliser l'alignement naturel" tool_tip="Le squelette a des rotations d'articulations non naturelles par défaut. Cela complique la pose. Si cette option est cochée, les articulations tourneront de manière plus naturelle."/>
<check_box name="show_joint_markers_checkbox" label="Aff. marqueurs de jointure" tool_tip="Afficher de petits indicateurs pour faciliter la sélection des articulations lors de la pose visuelle."/> <check_box name="show_joint_markers_checkbox" label="Aff. marqueurs de jointure" tool_tip="Afficher de petits indicateurs pour faciliter la sélection des articulations lors de la pose."/>
</panel> </panel>
</tab_container> </tab_container>
<button name="toggleVisualManipulators" tool_tip="Activer et désactiver les manipulateurs visuels"/> <button name="toggleVisualManipulators" tool_tip="Activer et désactiver les manipulateurs visuels"/>
@ -295,6 +295,9 @@
<button name="delta_mode_toggle" tool_tip="Si vous modifiez plusieurs articulations, chacune d'entre elles sera modifiée de la même manière, au lieu de toutes les modifier du même coup. Sert également à déverrouiller le Gimbal Lock." /> <button name="delta_mode_toggle" tool_tip="Si vous modifiez plusieurs articulations, chacune d'entre elles sera modifiée de la même manière, au lieu de toutes les modifier du même coup. Sert également à déverrouiller le Gimbal Lock." />
<button label="Miroir" name="button_toggleMirrorRotation" tool_tip="Changer l'articulation opposée, comme dans un miroir." /> <button label="Miroir" name="button_toggleMirrorRotation" tool_tip="Changer l'articulation opposée, comme dans un miroir." />
<button label="Sym." name="button_toggleSympatheticRotation" tool_tip="Copier les modifications sur l'articulation opposée." /> <button label="Sym." name="button_toggleSympatheticRotation" tool_tip="Copier les modifications sur l'articulation opposée." />
<button label="M" name="poser_world_frame_toggle" tool_tip="Effectuer des changements de rotation par rapport au Monde"/>
<button label="A" name="poser_avatar_frame_toggle" tool_tip="Effectuer des changements de rotation par rapport à l'Avatar"/>
<button label="É" name="poser_screen_frame_toggle" tool_tip="Effectuer des changements de rotation par rapport à l'Écran"/>
<button label="Cop. G &gt; D" name="button_symmetrize_left_to_right" tool_tip="Cliquez pour copier la modification du côté gauche vers le côté droit."/> <button label="Cop. G &gt; D" name="button_symmetrize_left_to_right" tool_tip="Cliquez pour copier la modification du côté gauche vers le côté droit."/>
<button label="Cop. D &gt; G" name="button_symmetrize_right_to_left" tool_tip="Cliquez pour copier la modification du côté droit vers le côté gauche."/> <button label="Cop. D &gt; G" name="button_symmetrize_right_to_left" tool_tip="Cliquez pour copier la modification du côté droit vers le côté gauche."/>
</panel> </panel>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<floater name="splash_screen_settings" title="Réglages de l'écran d'accueil">
<text name="description">
Configurez ici les options d'affichage de l'écran d'accueil. Ces réglages prendront effet après le prochain redémarrage du client.
</text>
<text name="section_visibility">
Visibilité des sections :
</text>
<check_box label="Masquer la barre supérieure" name="hide_top_bar" />
<check_box label="Masquer la section des blogs" name="hide_blogs" />
<check_box label="Masquer les destinations" name="hide_destinations" />
<text name="section_accessibility">
Options d'accessibilité :
</text>
<check_box label="Activer le mode niveaux de gris" name="use_gray_mode" />
<check_box label="Activer le mode contraste élevé" name="use_high_contrast" />
<check_box label="Activer le mode &quot;TOUT EN MAJUSCULES&quot;" name="use_all_caps" />
<check_box label="Utiliser des polices plus grandes" name="use_larger_fonts"/>
<check_box label="Désactiver les effets de transparence" name="no_transparency" />
</floater>

View File

@ -16,7 +16,7 @@
<radio_item label="Ouvre une nouvelle fenêtre" name="true"/> <radio_item label="Ouvre une nouvelle fenêtre" name="true"/>
</radio_group> </radio_group>
<text name="find_original_txt"> <text name="find_original_txt">
Cliquer sur "Afficher dans l'inventaire" ou "Trouver l'original": Clic "Afficher dans l'inventaire" ou "Trouver l'original":
</text> </text>
<radio_group name="find_original_settings"> <radio_group name="find_original_settings">
<radio_item label="Afficher dans la fenêtre d'inventaire principale" name="false"/> <radio_item label="Afficher dans la fenêtre d'inventaire principale" name="false"/>
@ -29,7 +29,7 @@
<check_box label="Étoile sur les dossiers contenant un favori" name="favorite_hollow_star"/> <check_box label="Étoile sur les dossiers contenant un favori" name="favorite_hollow_star"/>
<check_box label="Texte coloré" name="favorites_color"/> <check_box label="Texte coloré" name="favorites_color"/>
<text name="attachment_return_txt"> <text name="attachment_return_txt">
Appuyer sur Entrée sur un élément attaché à l'avatar Appuyer sur Entrée ou cliquer deux fois sur un élément attaché à l'avatar
</text> </text>
<combo_box name="attach_combo"> <combo_box name="attach_combo">
<combo_box.item label="Ajoute l'élément (recommandé)" name="1"/> <combo_box.item label="Ajoute l'élément (recommandé)" name="1"/>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<floater name="Marketplace" title="Marketplace">
<layout_stack name="stack1">
<layout_panel name="nav_controls">
<button tool_tip="Revenir en arrière" name="back"/>
<button tool_tip="Aller vers l'avant" name="forward"/>
<button tool_tip="Arrêter la navigation" name="stop"/>
<button tool_tip="Recharger la page" name="reload"/>
<combo_box name="address" tool_tip="Entrez l'URL ici"/>
<icon name="media_secure_lock_flag" tool_tip="Navigation sécurisée"/>
<button tool_tip="Ouvrir l'URL dans le navigateur de votre ordinateur" name="popexternal"/>
</layout_panel>
</layout_stack>
</floater>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <?xml version="1.0" encoding="utf-8" standalone="yes"?>
<floater name="Model Preview" title="CHARGEMENT DU MODELE"> <floater name="Model Preview" title="Chargement du modèle">
<string name="no_havok">téléchargement du maillage avec physique</string> <string name="no_havok">téléchargement du maillage avec physique</string>
<string name="status_parse_error">Erreur : Problème d&apos;analyse du modèle ; reportez-vous au journal pour plus de détails.</string> <string name="status_parse_error">Erreur : Problème d&apos;analyse du modèle ; reportez-vous au journal pour plus de détails.</string>
<string name="status_bind_shape_orientation">Avertissement : la matrice de formes de reliure n&apos;est pas dans l&apos;orientation standard X-avant.</string> <string name="status_bind_shape_orientation">Avertissement : la matrice de formes de reliure n&apos;est pas dans l&apos;orientation standard X-avant.</string>
@ -217,6 +217,28 @@
<button label="Analyser" name="Decompose"/> <button label="Analyser" name="Decompose"/>
<button label="Annuler" name="decompose_cancel"/> <button label="Annuler" name="decompose_cancel"/>
</panel> </panel>
<panel name="physics analysis vhacd">
<text name="method_label">
Étape 2 : Convertir en hulls (facultatif)
</text>
<text name="fill_mode_label">
Mode remplissage :
</text>
<text name="resolution_label">
Résolution :
</text>
<text name="hulls_per_mesh_label">
Hulls par Mesh :
</text>
<text name="vertices_per_hull_label">
Vertices par hull :
</text>
<text name="tolerance_label">
Tolérance erreurs :
</text>
<button label="Analyser" name="Analyze"/>
<button label="Annuler" name="analyze_cancel"/>
</panel>
<panel name="physics simplification"> <panel name="physics simplification">
<text name="second_step_label"> <text name="second_step_label">
Étape 3 : Simplifier Étape 3 : Simplifier
@ -364,7 +386,7 @@
Pb des physiques : Pb des physiques :
</text> </text>
<color_swatch label="Mauvaises arêtes" tool_tip="Couleur des arêtes pour les triangles dégénérés (fin) dans l'aperçu de l'uploader" name="mesh_preview_degenerate_edge_color"/> <color_swatch label="Mauvaises arêtes" tool_tip="Couleur des arêtes pour les triangles dégénérés (fin) dans l'aperçu de l'uploader" name="mesh_preview_degenerate_edge_color"/>
<color_swatch label="Mauvais triangles" tool_tip="Couleur de remplissage pour les triangles dégénérés (fin) dans l'aperçu de l'uploader" name="mesh_degenerate_fill_color"/> <color_swatch label="Mauvais triangles" tool_tip="Couleur de remplissage pour les triangles dégénérés (fin) dans l'aperçu de l'uploader" name="mesh_preview_degenerate_fill_color"/>
</panel> </panel>
</tab_container> </tab_container>
<panel name="weights_and_warning_panel"> <panel name="weights_and_warning_panel">

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<floater name="Search" title="Recherche">
<layout_stack name="stack1">
<layout_panel name="nav_controls">
<button tool_tip="Revenir en arrière" name="back"/>
<button tool_tip="Aller vers l'avant" name="forward"/>
<button tool_tip="Arrêter la navigation" name="stop"/>
<button tool_tip="Recharger la page" name="reload"/>
<combo_box name="address" tool_tip="Entrez l'URL ici"/>
<icon name="media_secure_lock_flag" tool_tip="Navigation sécurisée"/>
<button tool_tip="Ouvrir l'URL dans le navigateur de votre ordinateur" name="popexternal"/>
</layout_panel>
</layout_stack>
</floater>

View File

@ -9,6 +9,7 @@
<menu_item_call label="Inviter dans un groupe" name="Invite..."/> <menu_item_call label="Inviter dans un groupe" name="Invite..."/>
<menu_item_call label="Réinitialiser le squelette" name="Reset Skeleton"/> <menu_item_call label="Réinitialiser le squelette" name="Reset Skeleton"/>
<menu_item_call label="Réinitialiser le squelette et les animations" name="Reset Skeleton And Animations"/> <menu_item_call label="Réinitialiser le squelette et les animations" name="Reset Skeleton And Animations"/>
<menu_item_call label="Réinitialiser le LOD du mesh" name="Reset Mesh LOD"/>
<context_menu label="Dérangement" name="Annoyance"> <context_menu label="Dérangement" name="Annoyance">
<menu_item_call label="Ignorer" name="Avatar Mute"/> <menu_item_call label="Ignorer" name="Avatar Mute"/>
<menu_item_call label="Cesser d'ignorer" name="Avatar Unmute"/> <menu_item_call label="Cesser d'ignorer" name="Avatar Unmute"/>

View File

@ -18,6 +18,7 @@
<menu_item_call label="Voltigement" name="Hover Height"/> <menu_item_call label="Voltigement" name="Hover Height"/>
<menu_item_call label="Réinitialiser le squelette" name="Reset Skeleton"/> <menu_item_call label="Réinitialiser le squelette" name="Reset Skeleton"/>
<menu_item_call label="Réinitialiser le squelette et les animations" name="Reset Skeleton And Animations"/> <menu_item_call label="Réinitialiser le squelette et les animations" name="Reset Skeleton And Animations"/>
<menu_item_call label="Réinitialiser le LOD du mesh" name="Reset Mesh LOD"/>
<menu_item_call label="Réinitialiser le LOD du maillage" name="Reset Mesh LOD"/> <menu_item_call label="Réinitialiser le LOD du maillage" name="Reset Mesh LOD"/>
</context_menu> </context_menu>
<context_menu label="Enlever" name="Take Off &gt;"> <context_menu label="Enlever" name="Take Off &gt;">

View File

@ -18,7 +18,16 @@
<menu_item_call label="Payer" name="Pay"/> <menu_item_call label="Payer" name="Pay"/>
<menu_item_call label="Copier le nom" name="copy name"/> <menu_item_call label="Copier le nom" name="copy name"/>
<menu_item_call label="Copier l'URL" name="copy url"/> <menu_item_call label="Copier l'URL" name="copy url"/>
<menu_item_call label="Copier l'URI de la mention" name="mention_copy_label"/>
<menu_item_call label="Signaler une infraction" name="Report Abuse"/> <menu_item_call label="Signaler une infraction" name="Report Abuse"/>
<menu_item_check label="Muter" name="Block Unblock"/> <menu_item_check label="Bloquer la voix" name="Block Unblock"/>
<menu_item_check label="Ignorer" name="Mute Text"/> <menu_item_check label="Bloquer le texte" name="Mute Text"/>
<context_menu label="Options du modérateur" name="Moderator Options">
<menu_item_call label="Autorisez les échanges écrits" name="AllowTextChat"/>
<menu_item_call label="Interdire les échanges écrits" name="ForbidTextChat"/>
<menu_item_call label="Bloquer ce participant" name="ModerateVoiceMuteSelected"/>
<menu_item_call label="Débloquer ce participant" name="ModerateVoiceUnMuteSelected"/>
</context_menu>
<menu_item_call label="Bannir ce membre" name="BanMember"/>
<menu_item_call label="Mentionner le résident dans le chat" name="mention_in_chat"/>
</menu> </menu>

View File

@ -9,6 +9,7 @@
<menu_item_call label="Inviter dans un groupe" name="Invite..."/> <menu_item_call label="Inviter dans un groupe" name="Invite..."/>
<menu_item_call label="Réinitialiser le squelette" name="Reset Skeleton"/> <menu_item_call label="Réinitialiser le squelette" name="Reset Skeleton"/>
<menu_item_call label="Réinitialiser le squelette et les animations" name="Reset Skeleton And Animations"/> <menu_item_call label="Réinitialiser le squelette et les animations" name="Reset Skeleton And Animations"/>
<menu_item_call label="Réinitialiser le LOD du mesh" name="Reset Mesh LOD"/>
<context_menu label="Dérangement" name="Remove"> <context_menu label="Dérangement" name="Remove">
<menu_item_call label="Ignorer" name="Avatar Mute"/> <menu_item_call label="Ignorer" name="Avatar Mute"/>
<menu_item_call label="Cesser d'ignorer" name="Avatar Unmute"/> <menu_item_call label="Cesser d'ignorer" name="Avatar Unmute"/>

View File

@ -25,7 +25,7 @@
<menu_item_call label="Activer le groupe" name="activate_group"/> <menu_item_call label="Activer le groupe" name="activate_group"/>
<menu_item_call label="Quitter le groupe" name="leave_group"/> <menu_item_call label="Quitter le groupe" name="leave_group"/>
<context_menu label="Options de modération" name="Moderator Options"> <context_menu label="Options de modération" name="Moderator Options">
<menu_item_check label="Autoriser le chat écrit" name="AllowTextChat"/> <menu_item_check label="Autoriser les échanges écrits" name="AllowTextChat"/>
<menu_item_call label="Muter ce participant" name="ModerateVoiceMuteSelected"/> <menu_item_call label="Muter ce participant" name="ModerateVoiceMuteSelected"/>
<menu_item_call label="Ecouter ce participant" name="ModerateVoiceUnMuteSelected"/> <menu_item_call label="Ecouter ce participant" name="ModerateVoiceUnMuteSelected"/>
<menu_item_call label="Muter tout le monde" name="ModerateVoiceMute"/> <menu_item_call label="Muter tout le monde" name="ModerateVoiceMute"/>

View File

@ -12,7 +12,8 @@
<menu_item_call label="Suivre" name="track_agent"/> <menu_item_call label="Suivre" name="track_agent"/>
<menu_item_call label="Supprimer cet ami" name="remove_friend"/> <menu_item_call label="Supprimer cet ami" name="remove_friend"/>
<menu_item_call label="Copier le nom" name="url_copy_label"/> <menu_item_call label="Copier le nom" name="url_copy_label"/>
<menu_item_call label="Copier l'adresse" name="url_copy"/> <menu_item_call label="Copier l'URL" name="url_copy"/>
<menu_item_call label="Copier l'URI de la mention" name="mention_copy_label"/>
<menu name="options" label="Options..."> <menu name="options" label="Options...">
<menu_item_check label="Afficher la colonne Nom d'utilisateur" name="show_username"/> <menu_item_check label="Afficher la colonne Nom d'utilisateur" name="show_username"/>
<menu_item_check label="Afficher la colonne Nom d'affichage" name="show_displayname"/> <menu_item_check label="Afficher la colonne Nom d'affichage" name="show_displayname"/>

View File

@ -4,4 +4,5 @@
<menu_item_call label="Copier" name="Copy"/> <menu_item_call label="Copier" name="Copy"/>
<menu_item_call label="Coller" name="Paste"/> <menu_item_call label="Coller" name="Paste"/>
<menu_item_call label="Ouvrir l'inspecteur Web" name="open_webinspector"/> <menu_item_call label="Ouvrir l'inspecteur Web" name="open_webinspector"/>
<menu_item_call label="Afficher le code" name="show_page_source"/>
</context_menu> </context_menu>

View File

@ -2,6 +2,7 @@
<context_menu name="Object Pie"> <context_menu name="Object Pie">
<menu_item_call label="Toucher" name="Object Touch"/> <menu_item_call label="Toucher" name="Object Touch"/>
<menu_item_call label="Modifier" name="Edit..."/> <menu_item_call label="Modifier" name="Edit..."/>
<menu_item_call label="Modifier le matériau PBR" name="EditGLTFMaterial"/>
<menu_item_call label="Construire" name="Build"/> <menu_item_call label="Construire" name="Build"/>
<menu_item_call label="Ouvrir" name="Open"/> <menu_item_call label="Ouvrir" name="Open"/>
<menu_item_call label="S'asseoir ici" name="Object Sit"/> <menu_item_call label="S'asseoir ici" name="Object Sit"/>
@ -21,7 +22,7 @@
<menu_item_call label="Supprimer les scripts" name="Remove Scripts From Selection"/> <menu_item_call label="Supprimer les scripts" name="Remove Scripts From Selection"/>
</context_menu> </context_menu>
<menu_item_call label="Zoomer" name="Zoom In"/> <menu_item_call label="Zoomer" name="Zoom In"/>
<menu_item_call label="Afficher dans les ensembles définis" name="show_in_linksets"/> <menu_item_call label="Afficher dans les objets de la région" name="show_in_linksets"/>
<menu_item_call label="Show in characters" name="show_in_characters"/> <menu_item_call label="Show in characters" name="show_in_characters"/>
<context_menu label="Mettre" name="Put On"> <context_menu label="Mettre" name="Put On">
<menu_item_call label="Porter" name="Wear"/> <menu_item_call label="Porter" name="Wear"/>

View File

@ -3,6 +3,7 @@
<menu_item_call label="Propriétés de l'objet" name="Object Profile"/> <menu_item_call label="Propriétés de l'objet" name="Object Profile"/>
<menu_item_call label="Ignorer" name="Block"/> <menu_item_call label="Ignorer" name="Block"/>
<menu_item_call label="Ne plus ignorer" name="Unblock"/> <menu_item_call label="Ne plus ignorer" name="Unblock"/>
<menu_item_call label="Zoom avant" name="zoom_in"/>
<menu_item_call label="Voir sur la carte" name="show_on_map"/> <menu_item_call label="Voir sur la carte" name="show_on_map"/>
<menu_item_call label="Se téléporter à l'emplacement de l'objet" name="teleport_to_object"/> <menu_item_call label="Se téléporter à l'emplacement de l'objet" name="teleport_to_object"/>
</menu> </menu>

View File

@ -11,15 +11,18 @@
<menu_item_check label="Trier par nom" name="SortByName"/> <menu_item_check label="Trier par nom" name="SortByName"/>
<menu_item_check label="Trier par dernière activité" name="SortByRecentSpeakers"/> <menu_item_check label="Trier par dernière activité" name="SortByRecentSpeakers"/>
<menu_item_check label="Afficher les icônes des personnes" name="View Icons"/> <menu_item_check label="Afficher les icônes des personnes" name="View Icons"/>
<menu_item_check label="Ignorer" name="Block/Unblock"/> <menu_item_check label="Bloquer" name="Block/Unblock"/>
<menu_item_check label="Ignorer" name="MuteText"/> <menu_item_check label="Ignorer" name="MuteText"/>
<context_menu label="Options du modérateur" name="Moderator Options"> <context_menu label="Options du modérateur" name="Moderator Options">
<menu_item_call label="Éjecter du groupe" name="EjectSelected"/> <menu_item_call label="Éjecter du groupe" name="EjectSelected"/>
<menu_item_check label="Autoriser le chat écrit" name="AllowTextChat"/> <menu_item_check label="Autoriser les échanges écrits" name="AllowTextChat"/>
<menu_item_call label="Interdire les échanges écrits" name="ForbidTextChat"/>
<menu_item_call label="Muter ce participant" name="ModerateVoiceMuteSelected"/> <menu_item_call label="Muter ce participant" name="ModerateVoiceMuteSelected"/>
<menu_item_call label="Écouter à nouveau ce participant" name="ModerateVoiceUnMuteSelected"/> <menu_item_call label="Écouter à nouveau ce participant" name="ModerateVoiceUnMuteSelected"/>
<menu_item_call label="Muter tout le monde" name="ModerateVoiceMute"/> <menu_item_call label="Muter tout le monde" name="ModerateVoiceMute"/>
<menu_item_call label="Écouter à nouveau tout le monde" name="ModerateVoiceUnmute"/> <menu_item_call label="Écouter à nouveau tout le monde" name="ModerateVoiceUnmute"/>
</context_menu> </context_menu>
<menu_item_call label="Bannir le membre" name="BanMember"/> <menu_item_call label="Bannir le membre" name="BanMember"/>
<menu_item_call label="Copier l'URI de la mention" name="mention_copy_label"/>
<menu_item_call label="Mentioner le résident dans la discussion" name="mention_in_chat"/>
</context_menu> </context_menu>

View File

@ -13,6 +13,14 @@
<menu_item_call label="Supprimer cet ami" name="remove_friend"/> <menu_item_call label="Supprimer cet ami" name="remove_friend"/>
<menu_item_call label="Signaler une infraction" name="report_abuse"/> <menu_item_call label="Signaler une infraction" name="report_abuse"/>
<menu_item_check label="Ignorer" name="Block"/> <menu_item_check label="Ignorer" name="Block"/>
<context_menu label="Moderator Options" name="GroupModerationSubmenu">
<menu_item_call label="Autoriser les échanges écrits" name="AllowGroupChat"/>
<menu_item_call label="Interdire les échanges écrits" name="ForbidGroupChat"/>
<menu_item_call label="Bannir le membre" name="BanGroupMember"/>
<menu_item_call label="Éjecter le membre" name="BanEjectMember"/>
</context_menu>
<menu_item_call label="Copier le nom" name="url_copy_label"/> <menu_item_call label="Copier le nom" name="url_copy_label"/>
<menu_item_call label="Copier l'adresse" name="url_copy"/> <menu_item_call label="Copier l'URL" name="url_copy"/>
<menu_item_call label="Copier l'URI de la mention" name="mention_copy_label"/>
<menu_item_call label="Mentioner le résident dans la discussion" name="mention_in_chat"/>
</context_menu> </context_menu>

View File

@ -3,6 +3,7 @@
<menu_item_call label="Profil de l'objet" name="show_object"/> <menu_item_call label="Profil de l'objet" name="show_object"/>
<menu_item_call label="Ignorer" name="block_object"/> <menu_item_call label="Ignorer" name="block_object"/>
<menu_item_call label="Ne plus ignorer" name="unblock_object"/> <menu_item_call label="Ne plus ignorer" name="unblock_object"/>
<menu_item_call label="Zoom avant" name="zoom_in"/>
<menu_item_call label="Voir sur la carte" name="show_on_map"/> <menu_item_call label="Voir sur la carte" name="show_on_map"/>
<menu_item_call label="Se téléporter à l'emplacement de l'objet" name="teleport_to_object"/> <menu_item_call label="Se téléporter à l'emplacement de l'objet" name="teleport_to_object"/>
<menu_item_call label="Copier le nom de l'objet dans le presse-papiers" name="url_copy_label"/> <menu_item_call label="Copier le nom de l'objet dans le presse-papiers" name="url_copy_label"/>

View File

@ -882,6 +882,15 @@ Assurez-vous que le fichier a l&apos;extension correcte.
Impossible de lire le fichier son chargé : Impossible de lire le fichier son chargé :
[FILE] [FILE]
</notification> </notification>
<notification name="ModelUploaderMissingPhysicsApple">
Le chargement de modèles n'est pas encore disponible sur Apple Silicon, mais sera pris en charge dans une prochaine version.
Solution de contournement : cliquez avec le bouton droit sur l'application [APP_NAME] dans le Finder, sélectionnez
« Obtenir des informations », puis cochez « Ouvrir avec Rosetta ».
</notification>
<notification name="ModelUploaderMissingPhysics">
La bibliothèque physique n'est pas présente, certaines fonctionnalités du programme de chargement de modèles peuvent ne pas fonctionner ou ne pas fonctionner correctement.
</notification>
<notification name="SoundFileNotRIFF"> <notification name="SoundFileNotRIFF">
Il semble que le fichier ne soit pas un fichier RIFF WAVE : Il semble que le fichier ne soit pas un fichier RIFF WAVE :
[FILE] [FILE]
@ -5187,6 +5196,10 @@ Ils peuvent utiliser http://opensimulator.org/wiki/inventory pour résoudre les
Impossible de déplacer les fichiers. Chemin précédent rétabli. Impossible de déplacer les fichiers. Chemin précédent rétabli.
<usetemplate ignoretext="Impossible de déplacer les fichiers. Chemin précédent rétabli." name="okignore" yestext="OK"/> <usetemplate ignoretext="Impossible de déplacer les fichiers. Chemin précédent rétabli." name="okignore" yestext="OK"/>
</notification> </notification>
<notification name="PreferenceQualityWithLowMemory">
Votre système dispose de [TOTAL_MEM] Mo de mémoire, ce qui peut s'avérer insuffisant pour exécuter la visionneuse avec des paramètres élevés et risque d'entraîner des problèmes.
<usetemplate name="okcancelbuttons" notext="Annuler" yestext="Continuer"/>
</notification>
<notification name="DefaultObjectPermissions"> <notification name="DefaultObjectPermissions">
Problème lors de l&apos;enregistrement des droits d&apos;objet par défaut : [REASON]. Réessayez de définir les droits par défaut ultérieurement. Problème lors de l&apos;enregistrement des droits d&apos;objet par défaut : [REASON]. Réessayez de définir les droits par défaut ultérieurement.
<usetemplate name="okbutton" yestext="OK"/> <usetemplate name="okbutton" yestext="OK"/>
@ -5404,6 +5417,12 @@ la région &quot;[REGION]&quot; ?
Impossible de créer un autre favori car le nombre maximum de favoris a déjà été créé. Impossible de créer un autre favori car le nombre maximum de favoris a déjà été créé.
<usetemplate name="okbutton" yestext="OK"/> <usetemplate name="okbutton" yestext="OK"/>
</notification> </notification>
<notification name="ConfirmGlobalOnlineStatusToggle">
Êtes-vous sûr de vouloir modifier la visibilité de votre statut en ligne pour tous vos amis à la fois ?
En raison de la charge du serveur, cette modification en masse peut prendre un certain temps avant de prendre effet et peut entraîner des problèmes temporaires pour certains amis qui voient votre statut en ligne.
<usetemplate name="okcancelbuttons" notext="Annuler" yestext="OK"/>
</notification>
<notification name="GlobalOnlineStatusToggle"> <notification name="GlobalOnlineStatusToggle">
En raison de la charge du serveur, le basculement massif de la visibilité du statut en ligne peut prendre un certain temps avant d'être effectif. Veuillez être patient. En raison de la charge du serveur, le basculement massif de la visibilité du statut en ligne peut prendre un certain temps avant d'être effectif. Veuillez être patient.
<usetemplate ignoretext="Informez-moi que le changement de visibilité du statut en ligne peut prendre un certain temps." name="okignore" yestext="OK"/> <usetemplate ignoretext="Informez-moi que le changement de visibilité du statut en ligne peut prendre un certain temps." name="okignore" yestext="OK"/>
@ -5655,6 +5674,10 @@ https://wiki.firestormviewer.org/antivirus_whitelisting
Remplacer la pose “[POSE_NAME]”? Remplacer la pose “[POSE_NAME]”?
<usetemplate name="okcancelbuttons" notext="Annuler" yestext="Ok"/> <usetemplate name="okcancelbuttons" notext="Annuler" yestext="Ok"/>
</notification> </notification>
<notification name="FSLargeOutfitsWarningInThisSession">
Un grand nombre de tenues ont été détectées : [AMOUNT]. Cela peut entraîner des blocages ou des déconnexions de la visionneuse. Envisagez de réduire le nombre de tenues pour améliorer les performances (en dessous de [MAX]). IL S'AGIT UNIQUEMENT D'UNE SUGGESTION. Si votre ordinateur fonctionne normalement, vous pouvez ignorer cette recommandation.
<usetemplate ignoretext="Avertissement nombre de tenues" name="okignore" yestext="OK" />
</notification>
<notification name="PrimfeedLoginRequestFailed"> <notification name="PrimfeedLoginRequestFailed">
Demande de connexion refusée par Primfeed. Demande de connexion refusée par Primfeed.
</notification> </notification>

View File

@ -47,6 +47,7 @@ Pour rechercher des résidents avec qui passer du temps, utilisez [secondlife://
<button name="add_friend_btn" tool_tip="Proposer à ce résident de devenir votre ami"/> <button name="add_friend_btn" tool_tip="Proposer à ce résident de devenir votre ami"/>
<dnd_button name="nearby_del_btn" tool_tip="Supprimer la personne sélectionnée des amis"/> <dnd_button name="nearby_del_btn" tool_tip="Supprimer la personne sélectionnée des amis"/>
</panel> </panel>
<slider label="Distance :" name="near_me_range" tool_tip="Distance des personnes à proximité"/>
--> -->
</panel> </panel>
<panel label="Amis" name="friends_panel"> <panel label="Amis" name="friends_panel">

View File

@ -109,10 +109,10 @@
<combo_item name="High" label="Haut"/> <combo_item name="High" label="Haut"/>
</combo_box> </combo_box>
<check_box name="FSBuildPrefs_EmbedItem" label="Intégrer un objet dans le nouveau prim"/> <check_box name="FSBuildPrefs_EmbedItem" label="Intégrer un objet dans le nouveau prim"/>
<fs_embedded_item_drop_target name="embed_item" tool_tip="Glissez un objet ici.">Glissez un objet ici.</fs_embedded_item_drop_target> <fs_embedded_item_drop_target name="embed_item" tool_tip="Déposez ici un objet provenant de votre inventaire.">Déposez un objet ici.</fs_embedded_item_drop_target>
<text name="build_item_add_disp_rect_txt">Actuellement : [ITEM]</text> <text name="build_item_add_disp_rect_txt">Actuellement : [ITEM]</text>
<check_box name="FSBuildPrefs_UseCustomScript" label="Use custom script for New Script button"/> <check_box name="FSBuildPrefs_UseCustomScript" label="Script personnalisé pour bouton Nouveau script"/>
<fs_embedded_item_drop_target name="custom_script" tool_tip="Drop a script from your inventory here.">Drop a script here.</fs_embedded_item_drop_target> <fs_embedded_item_drop_target name="custom_script" tool_tip="Déposez ici un script provenant de votre inventaire.">Déposez un script ici.</fs_embedded_item_drop_target>
<text name="custom_script_disp_rect_txt">Actuellement: [SCRIPT]</text> <text name="custom_script_disp_rect_txt">Actuellement: [SCRIPT]</text>
<button label="Permissions de création par défaut" name="fs_default_creation_permissions"/> <button label="Permissions de création par défaut" name="fs_default_creation_permissions"/>
<check_box label="Contours lumineux des prims sélectionnées" tool_tip="Quand décoché, les contours jaunes/bleus ne seront pas affichés, améliorant les performances quand un grand nombre de prims est sélectionné." name="FSBuildPrefsRenderHighlight_toggle"/> <check_box label="Contours lumineux des prims sélectionnées" tool_tip="Quand décoché, les contours jaunes/bleus ne seront pas affichés, améliorant les performances quand un grand nombre de prims est sélectionné." name="FSBuildPrefsRenderHighlight_toggle"/>
@ -145,7 +145,7 @@
<check_box label="Points d'attachement alphabétiques dans les menus &quot;Attacher à&quot; (redémarrage requis)" tool_tip="Si cette option est activée, la liste des points d'attachement dans les menus 'Attacher à' sera triée par ordre alphabétique." name="FSSortAttachmentSpotsAlphabetically"/> <check_box label="Points d'attachement alphabétiques dans les menus &quot;Attacher à&quot; (redémarrage requis)" tool_tip="Si cette option est activée, la liste des points d'attachement dans les menus 'Attacher à' sera triée par ordre alphabétique." name="FSSortAttachmentSpotsAlphabetically"/>
<check_box label="Nouveau module de texture Firestorm dans la fenêtre d'outils (nécessite un redémarrage)" tool_tip="Si cette option est activée, les outils d'édition de textures utiliseront le flux de travail amélioré de FS et permettront l'édition de textures Blinn-Phong et PBR." name="FSUseNewTexturePanel"/> <check_box label="Nouveau module de texture Firestorm dans la fenêtre d'outils (nécessite un redémarrage)" tool_tip="Si cette option est activée, les outils d'édition de textures utiliseront le flux de travail amélioré de FS et permettront l'édition de textures Blinn-Phong et PBR." name="FSUseNewTexturePanel"/>
</panel> </panel>
<panel label="Script Editor" name="BuildTabScriptEditor"> <panel label="Éditeur de script" name="BuildTabScriptEditor">
<text name="text_box_scripting_font" width="155">Police de l'éditeur de script :</text> <text name="text_box_scripting_font" width="155">Police de l'éditeur de script :</text>
<combo_box name="FSScriptingFontName" tool_tip="Nom de la police utilisée dans l'éditeur de script LSL" /> <combo_box name="FSScriptingFontName" tool_tip="Nom de la police utilisée dans l'éditeur de script LSL" />
<combo_box name="FSScriptingFontSize" tool_tip="Taille de la police utilisée dans l'éditeur de script LSL"> <combo_box name="FSScriptingFontSize" tool_tip="Taille de la police utilisée dans l'éditeur de script LSL">
@ -168,6 +168,7 @@
<text name="title">Dossiers de destination pour les chargements :</text> <text name="title">Dossiers de destination pour les chargements :</text>
<text name="title_sounds">Sons</text> <text name="title_sounds">Sons</text>
<text name="title_models">Modèles</text> <text name="title_models">Modèles</text>
<text name="title_pbr">Matériaux PBR</text>
<text name="upload_help">Pour changer le dossier de destination, cliquez droit dessus dans l'inventaire et sélectionnez <text name="upload_help">Pour changer le dossier de destination, cliquez droit dessus dans l'inventaire et sélectionnez
"Par défaut pour" "Par défaut pour"
</text> </text>

View File

@ -3809,7 +3809,7 @@ Veuillez réinstaller la visionneuse à partir de [DOWNLOAD_URL] et contacter [S
Taille Taille
</string> </string>
<string name="High"> <string name="High">
Haut Élevée
</string> </string>
<string name="High Heels"> <string name="High Heels">
Talons hauts Talons hauts
@ -4013,7 +4013,7 @@ Veuillez réinstaller la visionneuse à partir de [DOWNLOAD_URL] et contacter [S
Poignées d&apos;amour Poignées d&apos;amour
</string> </string>
<string name="Low"> <string name="Low">
Bas Basse
</string> </string>
<string name="Low Heels"> <string name="Low Heels">
Talons bas Talons bas
@ -6222,7 +6222,7 @@ Essayez avec le chemin d&apos;accès à l&apos;éditeur entre guillemets doubles
Aperçu Aperçu
</string> </string>
<string name="Normal"> <string name="Normal">
Normal Normale
</string> </string>
<string name="Pathfinding_Wiki_URL"> <string name="Pathfinding_Wiki_URL">
http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer
@ -7166,6 +7166,22 @@ Votre position actuelle : [AVATAR_POS]
Trop d'entrées. Le fichier CSV contient [COUNT] entrées et il y a [MAX] emplacements disponibles. Trop d'entrées. Le fichier CSV contient [COUNT] entrées et il y a [MAX] emplacements disponibles.
</string> </string>
<string name="ImportSuccessful"> <string name="ImportSuccessful">
Successfully processed [COUNT] entries. [COUNT] entrées traitées avec succès.
</string>
<!-- VHACD enum strings -->
<string name="Flood">
Inondation
</string>
<string name="Surface Only">
Surface seulement
</string>
<string name="Raycast">
Diffusion de rayons
</string>
<string name="Very High">
Très élevée
</string>
<string name="Maximum">
Maximale
</string> </string>
</strings> </strings>

View File

@ -146,7 +146,7 @@
<panel name="joints_parent_panel"> <panel name="joints_parent_panel">
<tab_container name="joints_tabs"> <tab_container name="joints_tabs">
<panel title="Muovi" name="positionRotation_panel"> <panel title="Muovi" name="positionRotation_panel">
<panel name="title"> <panel name="move_tab_panel">
<text name="av_position_updown_label"> <text name="av_position_updown_label">
Su / Giù: Su / Giù:
</text> </text>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<floater name="splash_screen_settings" title="Impostazioni Schermata Iniziale">
<text name="description">
Configura qui le opzioni di visualizzazione della schermata iniziale. Queste impostazioni avranno effetto dopo il prossimo riavvio del visualizzatore.
</text>
<text name="section_visibility">
Visibilità sezione:
</text>
<check_box label="Nascondi barra superiore" name="hide_top_bar" />
<check_box label="Nascondi sezione blog" name="hide_blogs" />
<check_box label="Nascondi destinazioni" name="hide_destinations" />
<text name="section_accessibility">
Opzioni di Accessibilità:
</text>
<check_box label="Abilita modalità scala di grigi" name="use_gray_mode" />
<check_box label="Abilita modalità contrasto elevato" name="use_high_contrast" />
<check_box label="Abilita la modalità &quot;TUTTO MAIUSCOLO&quot;" name="use_all_caps" />
<check_box label="Usa caratteri più grandi" name="use_larger_fonts"/>
<check_box label="Disabilita effetti di trasparenza" name="no_transparency" />
</floater>

View File

@ -407,7 +407,7 @@
Problemi di fisica: Problemi di fisica:
</text> </text>
<color_swatch label="Bordi triangoli difettosi" tool_tip="Colore dei bordi per triangoli degenerati (sottili) nella finestra di anteprima per il caricamento della mesh" name="mesh_preview_degenerate_edge_color" /> <color_swatch label="Bordi triangoli difettosi" tool_tip="Colore dei bordi per triangoli degenerati (sottili) nella finestra di anteprima per il caricamento della mesh" name="mesh_preview_degenerate_edge_color" />
<color_swatch label="Riempim. triangoli difet." tool_tip="Colore di riempimento per triangoli degenerati (sottili) nella finestra di anteprima per il caricamento della mesh" name="mesh_degenerate_fill_color" /> <color_swatch label="Riempim. triangoli difet." tool_tip="Colore di riempimento per triangoli degenerati (sottili) nella finestra di anteprima per il caricamento della mesh" name="mesh_preview_degenerate_fill_color" />
</panel> </panel>
</tab_container> </tab_container>
<panel name="weights_and_warning_panel"> <panel name="weights_and_warning_panel">

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