diff --git a/.hgtags b/.hgtags index 6eb0813d1b..8fa56c75b7 100644 --- a/.hgtags +++ b/.hgtags @@ -70,3 +70,4 @@ b723921b5c711bd24dbe77dc76ef488b544dac78 DRTVWR-34_2.5.0-beta3 b723921b5c711bd24dbe77dc76ef488b544dac78 2.5.0-release b723921b5c711bd24dbe77dc76ef488b544dac78 DRTVWR-31_2.5.0-release 92e58e51776a4f8c29069b1a62ff21454d2085f0 2.6.0-start +63a6aedfce785a6c760377bf685b2dae616797d2 2.5.1-start diff --git a/doc/contributions.txt b/doc/contributions.txt index e1fb234f05..e94acb566a 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -86,6 +86,7 @@ Aleric Inglewood VWR-24320 VWR-24321 VWR-24354 + VWR-24366 VWR-24519 SNOW-84 SNOW-477 @@ -155,6 +156,7 @@ Ann Congrejo CT-193 Ardy Lay VWR-19499 + VWR-24917 Argent Stonecutter VWR-68 Armin Weatherwax @@ -189,6 +191,7 @@ Blakar Ogre blino Nakamura VWR-17 Boroondas Gupte + OPEN-29 SNOW-278 SNOW-503 SNOW-510 @@ -391,10 +394,19 @@ Jonathan Yap STORM-812 STORM-829 STORM-844 + STORM-953 + STORM-954 + STORM-960 STORM-869 + STORM-974 + STORM-975 + STORM-977 + STORM-979 + STORM-980 + STORM-1040 VWR-17801 VWR-24347 - STORM-975 + STORM-990 Kage Pixel VWR-11 Ken March @@ -781,6 +793,7 @@ Twisted Laws STORM-467 STORM-844 STORM-643 + STORM-954 Vadim Bigbear VWR-2681 Vector Hastings @@ -821,6 +834,7 @@ WolfPup Lowenhar STORM-102 STORM-103 STORM-143 + STORM-236 STORM-255 STORM-256 STORM-288 diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 6d17a6f402..7ba43f4b13 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -72,9 +72,9 @@ if (VIEWER) add_subdirectory(${LIBS_OPEN_PREFIX}media_plugins) # llplugin testbed code (is this the right way to include it?) - if (NOT LINUX) + if (LL_TESTS AND NOT LINUX) add_subdirectory(${VIEWER_PREFIX}test_apps/llplugintest) - endif (NOT LINUX) + endif (LL_TESTS AND NOT LINUX) if (LINUX) add_subdirectory(${VIEWER_PREFIX}linux_crash_logger) diff --git a/indra/cmake/GoogleMock.cmake b/indra/cmake/GoogleMock.cmake index ca5a8034ba..06d6d847a0 100644 --- a/indra/cmake/GoogleMock.cmake +++ b/indra/cmake/GoogleMock.cmake @@ -8,9 +8,10 @@ set(GOOGLEMOCK_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) if (LINUX) + # VWR-24366: gmock is underlinked, it needs gtest. set(GOOGLEMOCK_LIBRARIES - gmock - gtest) + gmock -Wl,--no-as-needed + gtest -Wl,--as-needed) elseif(WINDOWS) set(GOOGLEMOCK_LIBRARIES gmock) diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index 05f0492234..cd0eada2d0 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -57,11 +57,6 @@ INCLUDE(GoogleMock) ${CMAKE_SOURCE_DIR}/test/test.h ) - # Use the default flags - if (LINUX) - SET(CMAKE_EXE_LINKER_FLAGS "") - endif (LINUX) - # start the source test executable definitions SET(${project}_TEST_OUTPUT "") FOREACH (source ${sources}) diff --git a/indra/cmake/run_build_test.py b/indra/cmake/run_build_test.py index 37aa75e364..320a9be8ab 100644 --- a/indra/cmake/run_build_test.py +++ b/indra/cmake/run_build_test.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python """\ @file run_build_test.py @author Nat Goodspeed diff --git a/indra/copy_win_scripts/start-client.py b/indra/copy_win_scripts/start-client.py index 5f7ff2f293..5699f5273f 100644 --- a/indra/copy_win_scripts/start-client.py +++ b/indra/copy_win_scripts/start-client.py @@ -1,4 +1,28 @@ #!/usr/bin/env python +"""\ +@file start-client.py + +$LicenseInfo:firstyear=2010&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2010-2011, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" import sys, getopt import os import llstart diff --git a/indra/develop.py b/indra/develop.py index 36c947327a..d9a66352f3 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -1,29 +1,30 @@ #!/usr/bin/env python -# -# @file develop.py -# @authors Bryan O'Sullivan, Mark Palange, Aaron Brashears -# @brief Fire and forget script to appropriately configure cmake for SL. -# -# $LicenseInfo:firstyear=2007&license=viewerlgpl$ -# Second Life Viewer Source Code -# Copyright (C) 2010, Linden Research, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; -# version 2.1 of the License only. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# -# Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -# $/LicenseInfo$ +"""\ +@file develop.py +@authors Bryan O'Sullivan, Mark Palange, Aaron Brashears +@brief Fire and forget script to appropriately configure cmake for SL. + +$LicenseInfo:firstyear=2007&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2007-2011, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" import errno diff --git a/indra/lib/python/indra/util/llperformance.py b/indra/lib/python/indra/util/llperformance.py index 7c52730b5e..57dd64de3f 100755 --- a/indra/lib/python/indra/util/llperformance.py +++ b/indra/lib/python/indra/util/llperformance.py @@ -1,4 +1,28 @@ -#!/usr/bin/python +#!/usr/bin/env python +"""\ +@file llperformance.py + +$LicenseInfo:firstyear=2010&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2010-2011, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" # ------------------------------------------------ # Sim metrics utility functions. diff --git a/indra/lib/python/indra/util/llversion.py b/indra/lib/python/indra/util/llversion.py index 2718a85f41..ba6f567b60 100644 --- a/indra/lib/python/indra/util/llversion.py +++ b/indra/lib/python/indra/util/llversion.py @@ -1,7 +1,9 @@ -"""@file llversion.py -@brief Utility for parsing llcommon/llversion${server}.h - for the version string and channel string - Utility that parses hg or svn info for branch and revision +#!/usr/bin/env python +"""\ +@file llversion.py +@brief Parses llcommon/llversionserver.h and llcommon/llversionviewer.h + for the version string and channel string. + Parses hg info for branch and revision. $LicenseInfo:firstyear=2006&license=mit$ @@ -27,7 +29,7 @@ THE SOFTWARE. $/LicenseInfo$ """ -import re, sys, os, commands +import re, sys, os, subprocess # Methods for gathering version information from # llversionviewer.h and llversionserver.h @@ -73,29 +75,13 @@ def get_viewer_channel(): def get_server_channel(): return get_channel('server') -# Methods for gathering subversion information -def get_svn_status_matching(regular_expression): - # Get the subversion info from the working source tree - status, output = commands.getstatusoutput('svn info %s' % get_src_root()) - m = regular_expression.search(output) - if not m: - print >> sys.stderr, "Failed to parse svn info output, result follows:" - print >> sys.stderr, output - raise Exception, "No matching svn status in "+src_root - return m.group(1) - -def get_svn_branch(): - branch_re = re.compile('URL: (\S+)') - return get_svn_status_matching(branch_re) - -def get_svn_revision(): - last_rev_re = re.compile('Last Changed Rev: (\d+)') - return get_svn_status_matching(last_rev_re) - +# Methods for gathering hg information def get_hg_repo(): - status, output = commands.getstatusoutput('hg showconfig paths.default') + child = subprocess.Popen(["hg","showconfig","paths.default"], stdout=subprocess.PIPE) + output, error = child.communicate() + status = child.returncode if status: - print >> sys.stderr, output + print >> sys.stderr, error sys.exit(1) if not output: print >> sys.stderr, 'ERROR: cannot find repo we cloned from' @@ -103,24 +89,19 @@ def get_hg_repo(): return output def get_hg_changeset(): - # The right thing to do: - # status, output = commands.getstatusoutput('hg id -i') - # if status: - # print >> sys.stderr, output - # sys.exit(1) - - # The temporary hack: - status, output = commands.getstatusoutput('hg parents --template "{rev}"') + # The right thing to do would be to use the *global* revision id: + # "hg id -i" + # For the moment though, we use the parent revision: + child = subprocess.Popen(["hg","parents","--template","{rev}"], stdout=subprocess.PIPE) + output, error = child.communicate() + status = child.returncode if status: - print >> sys.stderr, output + print >> sys.stderr, error sys.exit(1) lines = output.splitlines() if len(lines) > 1: print >> sys.stderr, 'ERROR: working directory has %d parents' % len(lines) return lines[0] -def using_svn(): - return os.path.isdir(os.path.join(get_src_root(), '.svn')) - def using_hg(): return os.path.isdir(os.path.join(get_src_root(), '.hg')) diff --git a/indra/lib/python/indra/util/simperf_proc_interface.py b/indra/lib/python/indra/util/simperf_proc_interface.py index da6304a274..de061f68cc 100755 --- a/indra/lib/python/indra/util/simperf_proc_interface.py +++ b/indra/lib/python/indra/util/simperf_proc_interface.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python """\ @file simperf_proc_interface.py @brief Utility to extract log messages from *..llsd files containing performance statistics. diff --git a/indra/lib/python/indra/util/test_win32_manifest.py b/indra/lib/python/indra/util/test_win32_manifest.py index da8ee6c545..0532cb0065 100644 --- a/indra/lib/python/indra/util/test_win32_manifest.py +++ b/indra/lib/python/indra/util/test_win32_manifest.py @@ -1,28 +1,29 @@ #!/usr/bin/env python -# @file test_win32_manifest.py -# @brief Test an assembly binding version and uniqueness in a windows dll or exe. -# -# $LicenseInfo:firstyear=2009&license=viewerlgpl$ -# Second Life Viewer Source Code -# Copyright (C) 2010, Linden Research, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; -# version 2.1 of the License only. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# -# Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -# $/LicenseInfo$ +"""\ +@file test_win32_manifest.py +@brief Test an assembly binding version and uniqueness in a windows dll or exe. +$LicenseInfo:firstyear=2009&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2009-2011, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" import sys, os import tempfile from xml.dom.minidom import parse diff --git a/indra/llcommon/llavatarname.cpp b/indra/llcommon/llavatarname.cpp index ad1845d387..ba3dd6d6b4 100644 --- a/indra/llcommon/llavatarname.cpp +++ b/indra/llcommon/llavatarname.cpp @@ -90,14 +90,16 @@ void LLAvatarName::fromLLSD(const LLSD& sd) std::string LLAvatarName::getCompleteName() const { std::string name; - if (!mUsername.empty()) + if (mUsername.empty() || mIsDisplayNameDefault) + // If the display name feature is off + // OR this particular display name is defaulted (i.e. based on user name), + // then display only the easier to read instance of the person's name. { - name = mDisplayName + " (" + mUsername + ")"; + name = mDisplayName; } else { - // ...display names are off, legacy name is in mDisplayName - name = mDisplayName; + name = mDisplayName + " (" + mUsername + ")"; } return name; } diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h index 7703132d90..15e3ffe1da 100644 --- a/indra/llcommon/llversionviewer.h +++ b/indra/llcommon/llversionviewer.h @@ -28,8 +28,8 @@ #define LL_LLVERSIONVIEWER_H const S32 LL_VERSION_MAJOR = 2; -const S32 LL_VERSION_MINOR = 7; -const S32 LL_VERSION_PATCH = 0; +const S32 LL_VERSION_MINOR = 5; +const S32 LL_VERSION_PATCH = 2; const S32 LL_VERSION_BUILD = 0; const char * const LL_CHANNEL = "Second Life Developer"; diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp index 488bd45d8f..0a4cd51ea0 100644 --- a/indra/llinventory/llparcel.cpp +++ b/indra/llinventory/llparcel.cpp @@ -72,6 +72,7 @@ static const std::string PARCEL_CATEGORY_STRING[LLParcel::C_COUNT] = "shopping", "stage", "other", + "rental" }; static const std::string PARCEL_CATEGORY_UI_STRING[LLParcel::C_COUNT + 1] = { @@ -89,6 +90,7 @@ static const std::string PARCEL_CATEGORY_UI_STRING[LLParcel::C_COUNT + 1] = "Shopping", "Stage", "Other", + "Rental", "Any", // valid string for parcel searches }; @@ -188,8 +190,6 @@ void LLParcel::init(const LLUUID &owner_id, mMediaID.setNull(); mMediaAutoScale = 0; mMediaLoop = TRUE; - mObscureMedia = 1; - mObscureMusic = 1; mMediaWidth = 0; mMediaHeight = 0; setMediaCurrentURL(LLStringUtil::null); @@ -685,8 +685,8 @@ void LLParcel::packMessage(LLSD& msg) msg["auto_scale"] = getMediaAutoScale(); msg["media_loop"] = getMediaLoop(); msg["media_current_url"] = getMediaCurrentURL(); - msg["obscure_media"] = getObscureMedia(); - msg["obscure_music"] = getObscureMusic(); + msg["obscure_media"] = false; // OBSOLETE - no longer used + msg["obscure_music"] = false; // OBSOLETE - no longer used msg["media_id"] = getMediaID(); msg["media_allow_navigate"] = getMediaAllowNavigate(); msg["media_prevent_camera_zoom"] = getMediaPreventCameraZoom(); @@ -750,16 +750,13 @@ void LLParcel::unpackMessage(LLMessageSystem* msg) msg->getS32("MediaData", "MediaWidth", mMediaWidth); msg->getS32("MediaData", "MediaHeight", mMediaHeight); msg->getU8 ( "MediaData", "MediaLoop", mMediaLoop ); - msg->getU8 ( "MediaData", "ObscureMedia", mObscureMedia ); - msg->getU8 ( "MediaData", "ObscureMusic", mObscureMusic ); + // the ObscureMedia and ObscureMusic flags previously set here are no longer used } else { setMediaType(std::string("video/vnd.secondlife.qt.legacy")); setMediaDesc(std::string("No Description available without Server Upgrade")); mMediaLoop = true; - mObscureMedia = true; - mObscureMusic = true; } if(msg->getNumberOfBlocks("MediaLinkSharing") > 0) @@ -1225,8 +1222,6 @@ void LLParcel::clearParcel() setMediaDesc(LLStringUtil::null); setMediaAutoScale(0); setMediaLoop(TRUE); - mObscureMedia = 1; - mObscureMusic = 1; mMediaWidth = 0; mMediaHeight = 0; setMediaCurrentURL(LLStringUtil::null); diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h index ae301af9f5..71b65d99ce 100644 --- a/indra/llinventory/llparcel.h +++ b/indra/llinventory/llparcel.h @@ -165,6 +165,7 @@ public: C_SHOPPING, C_STAGE, C_OTHER, + C_RENTAL, C_COUNT, C_ANY = -1 // only useful in queries }; @@ -238,8 +239,6 @@ public: void setMediaID(const LLUUID& id) { mMediaID = id; } void setMediaAutoScale ( U8 flagIn ) { mMediaAutoScale = flagIn; } void setMediaLoop (U8 loop) { mMediaLoop = loop; } - void setObscureMedia( U8 flagIn ) { mObscureMedia = flagIn; } - void setObscureMusic( U8 flagIn ) { mObscureMusic = flagIn; } void setMediaWidth(S32 width); void setMediaHeight(S32 height); void setMediaCurrentURL(const std::string& url); @@ -346,8 +345,6 @@ public: U8 getMediaAutoScale() const { return mMediaAutoScale; } U8 getMediaLoop() const { return mMediaLoop; } const std::string& getMediaCurrentURL() const { return mMediaCurrentURL; } - U8 getObscureMedia() const { return mObscureMedia; } - U8 getObscureMusic() const { return mObscureMusic; } U8 getMediaURLFilterEnable() const { return mMediaURLFilterEnable; } LLSD getMediaURLFilterList() const { return mMediaURLFilterList; } U8 getMediaAllowNavigate() const { return mMediaAllowNavigate; } @@ -636,8 +633,6 @@ protected: U8 mMediaAutoScale; U8 mMediaLoop; std::string mMediaCurrentURL; - U8 mObscureMedia; - U8 mObscureMusic; LLUUID mMediaID; U8 mMediaURLFilterEnable; LLSD mMediaURLFilterList; diff --git a/indra/llmessage/tests/llhost_test.cpp b/indra/llmessage/tests/llhost_test.cpp index b20bceae1d..705473b0c0 100644 --- a/indra/llmessage/tests/llhost_test.cpp +++ b/indra/llmessage/tests/llhost_test.cpp @@ -152,7 +152,7 @@ namespace tut void host_object::test<9>() { // skip("setHostByName(\"google.com\"); getHostName() -> (e.g.) \"yx-in-f100.1e100.net\""); - std::string hostStr = "linux.org"; + std::string hostStr = "lindenlab.com"; LLHost host; host.setHostByName(hostStr); diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index 7eb198bb34..580ee7f8b4 100644 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python """\ @file test_llsdmessage_peer.py @author Nat Goodspeed diff --git a/indra/llmessage/tests/testrunner.py b/indra/llmessage/tests/testrunner.py index 4d58ef7130..b70ce91ee7 100644 --- a/indra/llmessage/tests/testrunner.py +++ b/indra/llmessage/tests/testrunner.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python """\ @file testrunner.py @author Nat Goodspeed diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 02160b09c4..1beb74eca6 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -47,6 +47,7 @@ U32 LLVertexBuffer::sSetCount = 0; S32 LLVertexBuffer::sCount = 0; S32 LLVertexBuffer::sGLCount = 0; S32 LLVertexBuffer::sMappedCount = 0; +BOOL LLVertexBuffer::sDisableVBOMapping = FALSE ; BOOL LLVertexBuffer::sEnableVBOs = TRUE; U32 LLVertexBuffer::sGLRenderBuffer = 0; U32 LLVertexBuffer::sGLRenderIndices = 0; @@ -251,6 +252,7 @@ void LLVertexBuffer::drawRange(U32 mode, U32 start, U32 end, U32 count, U32 indi void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const { llassert(mRequestedNumIndices >= 0); + if (indices_offset >= (U32) mRequestedNumIndices || indices_offset + count > (U32) mRequestedNumIndices) { @@ -282,6 +284,7 @@ void LLVertexBuffer::draw(U32 mode, U32 count, U32 indices_offset) const void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const { llassert(mRequestedNumVerts >= 0); + if (first >= (U32) mRequestedNumVerts || first + count > (U32) mRequestedNumVerts) { @@ -305,9 +308,21 @@ void LLVertexBuffer::drawArrays(U32 mode, U32 first, U32 count) const } //static -void LLVertexBuffer::initClass(bool use_vbo) +void LLVertexBuffer::initClass(bool use_vbo, bool no_vbo_mapping) { - sEnableVBOs = use_vbo; + sEnableVBOs = use_vbo && gGLManager.mHasVertexBufferObject ; + if(sEnableVBOs) + { + //llassert_always(glBindBufferARB) ; //double check the extention for VBO is loaded. + + llinfos << "VBO is enabled." << llendl ; + } + else + { + llinfos << "VBO is disabled." << llendl ; + } + + sDisableVBOMapping = sEnableVBOs && no_vbo_mapping ; LLGLNamePool::registerPool(&sDynamicVBOPool); LLGLNamePool::registerPool(&sDynamicIBOPool); LLGLNamePool::registerPool(&sStreamVBOPool); @@ -364,7 +379,9 @@ LLVertexBuffer::LLVertexBuffer(U32 typemask, S32 usage) : mGLBuffer(0), mGLIndices(0), mMappedData(NULL), - mMappedIndexData(NULL), mLocked(FALSE), + mMappedIndexData(NULL), + mVertexLocked(FALSE), + mIndexLocked(FALSE), mFinal(FALSE), mFilthy(FALSE), mEmpty(TRUE), @@ -422,6 +439,8 @@ LLVertexBuffer::~LLVertexBuffer() destroyGLBuffer(); destroyGLIndices(); sCount--; + + llassert_always(!mMappedData && !mMappedIndexData) ; }; //---------------------------------------------------------------------------- @@ -567,6 +586,8 @@ void LLVertexBuffer::destroyGLBuffer() { if (useVBOs()) { + freeClientBuffer() ; + if (mMappedData || mMappedIndexData) { llerrs << "Vertex buffer destroyed while mapped!" << llendl; @@ -594,11 +615,13 @@ void LLVertexBuffer::destroyGLIndices() { if (useVBOs()) { + freeClientBuffer() ; + if (mMappedData || mMappedIndexData) { llerrs << "Vertex buffer destroyed while mapped." << llendl; } - releaseIndices(); + releaseIndices(); } else { @@ -799,6 +822,7 @@ void LLVertexBuffer::resizeBuffer(S32 newnverts, S32 newnindices) if (mResized && useVBOs()) { + freeClientBuffer() ; setBuffer(0); } } @@ -822,36 +846,69 @@ BOOL LLVertexBuffer::useVBOs() const } //---------------------------------------------------------------------------- +void LLVertexBuffer::freeClientBuffer() +{ + if(useVBOs() && sDisableVBOMapping && (mMappedData || mMappedIndexData)) + { + delete[] mMappedData ; + delete[] mMappedIndexData ; + mMappedData = NULL ; + mMappedIndexData = NULL ; + } +} + +void LLVertexBuffer::allocateClientVertexBuffer() +{ + if(!mMappedData) + { + U32 size = getSize() ; + mMappedData = new U8[size]; + memset(mMappedData, 0, size); + } +} + +void LLVertexBuffer::allocateClientIndexBuffer() +{ + if(!mMappedIndexData) + { + U32 size = getIndicesSize(); + mMappedIndexData = new U8[size]; + memset(mMappedIndexData, 0, size); + } +} // Map for data access -U8* LLVertexBuffer::mapBuffer(S32 access) +U8* LLVertexBuffer::mapVertexBuffer(S32 type, S32 access) { LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER); if (mFinal) { - llerrs << "LLVertexBuffer::mapBuffer() called on a finalized buffer." << llendl; + llerrs << "LLVertexBuffer::mapVeretxBuffer() called on a finalized buffer." << llendl; } if (!useVBOs() && !mMappedData && !mMappedIndexData) { - llerrs << "LLVertexBuffer::mapBuffer() called on unallocated buffer." << llendl; + llerrs << "LLVertexBuffer::mapVertexBuffer() called on unallocated buffer." << llendl; } - if (!mLocked && useVBOs()) + if (!mVertexLocked && useVBOs()) { { LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_VERTICES); - setBuffer(0); - mLocked = TRUE; + setBuffer(0, type); + mVertexLocked = TRUE; stop_glerror(); - mMappedData = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); - stop_glerror(); - } - { - LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES); - mMappedIndexData = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); - stop_glerror(); - } + if(sDisableVBOMapping) + { + allocateClientVertexBuffer() ; + } + else + { + mMappedData = (U8*) glMapBufferARB(GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); + } + stop_glerror(); + } + if (!mMappedData) { log_glerror(); @@ -862,80 +919,166 @@ U8* LLVertexBuffer::mapBuffer(S32 access) llinfos << "Available physical mwmory(KB): " << avail_phy_mem << llendl ; llinfos << "Available virtual memory(KB): " << avail_vir_mem << llendl; - //-------------------- - //print out more debug info before crash - llinfos << "vertex buffer size: (num verts : num indices) = " << getNumVerts() << " : " << getNumIndices() << llendl ; - GLint size ; - glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &size) ; - llinfos << "GL_ARRAY_BUFFER_ARB size is " << size << llendl ; - //-------------------- - - GLint buff; - glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &buff); - if ((GLuint)buff != mGLBuffer) + if(!sDisableVBOMapping) { - llerrs << "Invalid GL vertex buffer bound: " << buff << llendl; + //-------------------- + //print out more debug info before crash + llinfos << "vertex buffer size: (num verts : num indices) = " << getNumVerts() << " : " << getNumIndices() << llendl ; + GLint size ; + glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &size) ; + llinfos << "GL_ARRAY_BUFFER_ARB size is " << size << llendl ; + //-------------------- + + GLint buff; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING_ARB, &buff); + if ((GLuint)buff != mGLBuffer) + { + llerrs << "Invalid GL vertex buffer bound: " << buff << llendl; + } + + + llerrs << "glMapBuffer returned NULL (no vertex data)" << llendl; } - - - llerrs << "glMapBuffer returned NULL (no vertex data)" << llendl; - } - - if (!mMappedIndexData) - { - log_glerror(); - - GLint buff; - glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff); - if ((GLuint)buff != mGLIndices) + else { - llerrs << "Invalid GL index buffer bound: " << buff << llendl; + llerrs << "memory allocation for vertex data failed." << llendl ; } - - llerrs << "glMapBuffer returned NULL (no index data)" << llendl; } - sMappedCount++; } return mMappedData; } -void LLVertexBuffer::unmapBuffer() +U8* LLVertexBuffer::mapIndexBuffer(S32 access) +{ + LLMemType mt2(LLMemType::MTYPE_VERTEX_MAP_BUFFER); + if (mFinal) + { + llerrs << "LLVertexBuffer::mapIndexBuffer() called on a finalized buffer." << llendl; + } + if (!useVBOs() && !mMappedData && !mMappedIndexData) + { + llerrs << "LLVertexBuffer::mapIndexBuffer() called on unallocated buffer." << llendl; + } + + if (!mIndexLocked && useVBOs()) + { + { + LLMemType mt_v(LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES); + + setBuffer(0, TYPE_INDEX); + mIndexLocked = TRUE; + stop_glerror(); + + if(sDisableVBOMapping) + { + allocateClientIndexBuffer() ; + } + else + { + mMappedIndexData = (U8*) glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB); + } + stop_glerror(); + } + + if (!mMappedIndexData) + { + log_glerror(); + + if(!sDisableVBOMapping) + { + GLint buff; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB, &buff); + if ((GLuint)buff != mGLIndices) + { + llerrs << "Invalid GL index buffer bound: " << buff << llendl; + } + + llerrs << "glMapBuffer returned NULL (no index data)" << llendl; + } + else + { + llerrs << "memory allocation for Index data failed. " << llendl ; + } + } + + sMappedCount++; + } + + return mMappedIndexData ; +} + +void LLVertexBuffer::unmapBuffer(S32 type) { LLMemType mt2(LLMemType::MTYPE_VERTEX_UNMAP_BUFFER); - if (mMappedData || mMappedIndexData) + if (!useVBOs()) { - if (useVBOs() && mLocked) + return ; //nothing to unmap + } + + bool updated_all = false ; + if (mMappedData && mVertexLocked && type != TYPE_INDEX) + { + updated_all = (mIndexLocked && type < 0) ; //both vertex and index buffers done updating + + if(sDisableVBOMapping) + { + stop_glerror(); + glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, getSize(), mMappedData); + stop_glerror(); + } + else { stop_glerror(); glUnmapBufferARB(GL_ARRAY_BUFFER_ARB); stop_glerror(); + + mMappedData = NULL; + } + + mVertexLocked = FALSE ; + sMappedCount--; + } + + if(mMappedIndexData && mIndexLocked && (type < 0 || type == TYPE_INDEX)) + { + if(sDisableVBOMapping) + { + stop_glerror(); + glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, getIndicesSize(), mMappedIndexData); + stop_glerror(); + } + else + { + stop_glerror(); glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB); stop_glerror(); - /*if (!sMapped) - { - llerrs << "Redundantly unmapped VBO!" << llendl; - } - sMapped = FALSE;*/ - sMappedCount--; + mMappedIndexData = NULL ; + } - if (mUsage == GL_STATIC_DRAW_ARB) - { //static draw buffers can only be mapped a single time - //throw out client data (we won't be using it again) - mEmpty = TRUE; - mFinal = TRUE; - } - else - { - mEmpty = FALSE; - } + mIndexLocked = FALSE ; + sMappedCount--; + } - mMappedIndexData = NULL; - mMappedData = NULL; - - mLocked = FALSE; + if(updated_all) + { + if(mUsage == GL_STATIC_DRAW_ARB) + { + //static draw buffers can only be mapped a single time + //throw out client data (we won't be using it again) + mEmpty = TRUE; + mFinal = TRUE; + + if(sDisableVBOMapping) + { + freeClientBuffer() ; + } + } + else + { + mEmpty = FALSE; } } } @@ -949,15 +1092,16 @@ template struct VertexBufferStrider strider_t& strider, S32 index) { - if (vbo.mapBuffer() == NULL) - { - llwarns << "mapBuffer failed!" << llendl; - return FALSE; - } - if (type == LLVertexBuffer::TYPE_INDEX) { S32 stride = sizeof(T); + + if (vbo.mapIndexBuffer() == NULL) + { + llwarns << "mapIndexBuffer failed!" << llendl; + return FALSE; + } + strider = (T*)(vbo.getMappedIndices() + index*stride); strider.setStride(0); return TRUE; @@ -965,6 +1109,13 @@ template struct VertexBufferStrider else if (vbo.hasDataType(type)) { S32 stride = vbo.getStride(); + + if (vbo.mapVertexBuffer(type) == NULL) + { + llwarns << "mapVertexBuffer failed!" << llendl; + return FALSE; + } + strider = (T*)(vbo.getMappedData() + vbo.getOffset(type) + index*stride); strider.setStride(stride); return TRUE; @@ -1045,7 +1196,7 @@ void LLVertexBuffer::setStride(S32 type, S32 new_stride) //---------------------------------------------------------------------------- // Set for rendering -void LLVertexBuffer::setBuffer(U32 data_mask) +void LLVertexBuffer::setBuffer(U32 data_mask, S32 type) { LLMemType mt2(LLMemType::MTYPE_VERTEX_SET_BUFFER); //set up pointers if the data mask is different ... @@ -1186,7 +1337,7 @@ void LLVertexBuffer::setBuffer(U32 data_mask) { ll_fail("LLVertexBuffer::mapBuffer failed"); } - unmapBuffer(); + unmapBuffer(type); } else { diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index 94fa790957..c51ce7ac4e 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -80,7 +80,7 @@ public: static BOOL sUseStreamDraw; - static void initClass(bool use_vbo); + static void initClass(bool use_vbo, bool no_vbo_mapping); static void cleanupClass(); static void setupClientArrays(U32 data_mask); static void clientCopy(F64 max_time = 0.005); //copy data from client to GL @@ -139,19 +139,24 @@ protected: void updateNumVerts(S32 nverts); void updateNumIndices(S32 nindices); virtual BOOL useVBOs() const; - void unmapBuffer(); - + void unmapBuffer(S32 type); + void freeClientBuffer() ; + void allocateClientVertexBuffer() ; + void allocateClientIndexBuffer() ; + public: LLVertexBuffer(U32 typemask, S32 usage); // map for data access - U8* mapBuffer(S32 access = -1); + U8* mapVertexBuffer(S32 type = -1, S32 access = -1); + U8* mapIndexBuffer(S32 access = -1); + // set for rendering - virtual void setBuffer(U32 data_mask); // calls setupVertexBuffer() if data_mask is not 0 + virtual void setBuffer(U32 data_mask, S32 type = -1); // calls setupVertexBuffer() if data_mask is not 0 // allocate buffer void allocateBuffer(S32 nverts, S32 nindices, bool create); virtual void resizeBuffer(S32 newnverts, S32 newnindices); - + // Only call each getVertexPointer, etc, once before calling unmapBuffer() // call unmapBuffer() after calls to getXXXStrider() before any cals to setBuffer() // example: @@ -170,7 +175,7 @@ public: bool getClothWeightStrider(LLStrider& strider, S32 index=0); BOOL isEmpty() const { return mEmpty; } - BOOL isLocked() const { return mLocked; } + BOOL isLocked() const { return mVertexLocked || mIndexLocked; } S32 getNumVerts() const { return mNumVerts; } S32 getNumIndices() const { return mNumIndices; } S32 getRequestedVerts() const { return mRequestedNumVerts; } @@ -209,13 +214,14 @@ protected: U32 mGLIndices; // GL IBO handle U8* mMappedData; // pointer to currently mapped data (NULL if unmapped) U8* mMappedIndexData; // pointer to currently mapped indices (NULL if unmapped) - BOOL mLocked; // if TRUE, buffer is being or has been written to in client memory + BOOL mVertexLocked; // if TRUE, vertex buffer is being or has been written to in client memory + BOOL mIndexLocked; // if TRUE, index buffer is being or has been written to in client memory BOOL mFinal; // if TRUE, buffer can not be mapped again BOOL mFilthy; // if TRUE, entire buffer must be copied (used to prevent redundant dirty flags) - BOOL mEmpty; // if TRUE, client buffer is empty (or NULL). Old values have been discarded. - S32 mOffsets[TYPE_MAX]; + BOOL mEmpty; // if TRUE, client buffer is empty (or NULL). Old values have been discarded. BOOL mResized; // if TRUE, client buffer has been resized and GL buffer has not BOOL mDynamicSize; // if TRUE, buffer has been resized at least once (and should be padded) + S32 mOffsets[TYPE_MAX]; class DirtyRegion { @@ -240,13 +246,14 @@ public: static std::vector sDeleteList; typedef std::list buffer_list_t; + static BOOL sDisableVBOMapping; //disable glMapBufferARB static BOOL sEnableVBOs; + static BOOL sVBOActive; + static BOOL sIBOActive; static S32 sTypeOffsets[TYPE_MAX]; static U32 sGLMode[LLRender::NUM_MODES]; static U32 sGLRenderBuffer; - static U32 sGLRenderIndices; - static BOOL sVBOActive; - static BOOL sIBOActive; + static U32 sGLRenderIndices; static U32 sLastMask; static U32 sAllocatedBytes; static U32 sBindCount; diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 8b6a73af56..6f9893b07a 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -316,7 +316,7 @@ void LLComboBox::setValue(const LLSD& value) LLScrollListItem* item = mList->getFirstSelected(); if (item) { - setLabel(getSelectedItemLabel()); + updateLabel(); } mLastSelectedIndex = mList->getFirstSelectedIndex(); } @@ -384,6 +384,23 @@ void LLComboBox::setLabel(const LLStringExplicit& name) } } +void LLComboBox::updateLabel() +{ + // Update the combo editor with the selected + // item label. + if (mTextEntry) + { + mTextEntry->setText(getSelectedItemLabel()); + mTextEntry->setTentative(FALSE); + } + + // If combo box doesn't allow text entry update + // the combo button label. + if (!mAllowTextEntry) + { + mButton->setLabel(getSelectedItemLabel()); + } +} BOOL LLComboBox::remove(const std::string& name) { @@ -701,13 +718,13 @@ void LLComboBox::onItemSelected(const LLSD& data) mLastSelectedIndex = getCurrentIndex(); if (mLastSelectedIndex != -1) { - setLabel(getSelectedItemLabel()); + updateLabel(); if (mAllowTextEntry) - { - gFocusMgr.setKeyboardFocus(mTextEntry); - mTextEntry->selectAll(); - } + { + gFocusMgr.setKeyboardFocus(mTextEntry); + mTextEntry->selectAll(); + } } // hiding the list reasserts the old value stored in the text editor/dropdown button hideList(); diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index 74d64269bd..e9ef9d07e4 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -148,6 +148,9 @@ public: // This is probably a UI abuse. void setLabel(const LLStringExplicit& name); + // Updates the combobox label to match the selected list item. + void updateLabel(); + BOOL remove(const std::string& name); // remove item "name", return TRUE if found and removed BOOL setCurrentByIndex( S32 index ); diff --git a/indra/llui/lldockcontrol.cpp b/indra/llui/lldockcontrol.cpp index e18309ac33..3f81c2b28f 100644 --- a/indra/llui/lldockcontrol.cpp +++ b/indra/llui/lldockcontrol.cpp @@ -1,359 +1,361 @@ -/** - * @file lldockcontrol.cpp - * @brief Creates a panel of a specific kind for a toast - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "lldockcontrol.h" -#include "lldockablefloater.h" - -LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater, - const LLUIImagePtr& dockTongue, DocAt dockAt, get_allowed_rect_callback_t get_allowed_rect_callback) : - mDockWidget(dockWidget), - mDockableFloater(dockableFloater), - mDockTongue(dockTongue), - mDockTongueX(0), - mDockTongueY(0) -{ - mDockAt = dockAt; - - if (dockableFloater->isDocked()) - { - on(); - } - else - { - off(); - } - - if (!(get_allowed_rect_callback)) - { - mGetAllowedRectCallback = boost::bind(&LLDockControl::getAllowedRect, this, _1); - } - else - { - mGetAllowedRectCallback = get_allowed_rect_callback; - } - - if (dockWidget != NULL) - { - repositionDockable(); - } - - if (mDockWidget != NULL) - { - mDockWidgetVisible = isDockVisible(); - } - else - { - mDockWidgetVisible = false; - } -} - -LLDockControl::~LLDockControl() -{ -} - -void LLDockControl::setDock(LLView* dockWidget) -{ - mDockWidget = dockWidget; - if (mDockWidget != NULL) - { - repositionDockable(); - mDockWidgetVisible = isDockVisible(); - } - else - { - mDockWidgetVisible = false; - } -} - -void LLDockControl::getAllowedRect(LLRect& rect) -{ - rect = mDockableFloater->getRootView()->getRect(); -} - -void LLDockControl::repositionDockable() -{ - LLRect dockRect = mDockWidget->calcScreenRect(); - LLRect rootRect; - mGetAllowedRectCallback(rootRect); - - // recalculate dockable position if dock position changed, dock visibility changed, - // root view rect changed or recalculation is forced - if (mPrevDockRect != dockRect || mDockWidgetVisible != isDockVisible() - || mRootRect != rootRect || mRecalculateDocablePosition) - { - // undock dockable and off() if dock not visible - if (!isDockVisible()) - { - mDockableFloater->setDocked(false); - // force off() since dockable may not have dockControll at this time - off(); - LLDockableFloater* dockable_floater = - dynamic_cast (mDockableFloater); - if(dockable_floater != NULL) - { - dockable_floater->onDockHidden(); - } - } - else - { - if(mEnabled) - { - moveDockable(); - } - LLDockableFloater* dockable_floater = - dynamic_cast (mDockableFloater); - if(dockable_floater != NULL) - { - dockable_floater->onDockShown(); - } - } - - mPrevDockRect = dockRect; - mRootRect = rootRect; - mRecalculateDocablePosition = false; - mDockWidgetVisible = isDockVisible(); - } -} - -bool LLDockControl::isDockVisible() -{ - bool res = true; - - if (mDockWidget != NULL) - { - //we should check all hierarchy - res = mDockWidget->isInVisibleChain(); - if (res) - { - LLRect dockRect = mDockWidget->calcScreenRect(); - - switch (mDockAt) - { - case LEFT: // to keep compiler happy - break; - case BOTTOM: - case TOP: - { - // check is dock inside parent rect - LLRect dockParentRect = - mDockWidget->getParent()->calcScreenRect(); - if (dockRect.mRight <= dockParentRect.mLeft - || dockRect.mLeft >= dockParentRect.mRight) - { - res = false; - } - break; - } - default: - break; - } - } - } - - return res; -} - -void LLDockControl::moveDockable() -{ - // calculate new dockable position - LLRect dockRect = mDockWidget->calcScreenRect(); - LLRect rootRect; - mGetAllowedRectCallback(rootRect); - - bool use_tongue = false; - LLDockableFloater* dockable_floater = - dynamic_cast (mDockableFloater); - if (dockable_floater != NULL) - { - use_tongue = dockable_floater->getUseTongue(); - } - - LLRect dockableRect = mDockableFloater->calcScreenRect(); - S32 x = 0; - S32 y = 0; - LLRect dockParentRect; - switch (mDockAt) - { - case LEFT: - x = dockRect.mLeft; - y = dockRect.mTop + mDockTongue->getHeight() + dockableRect.getHeight(); - // check is dockable inside root view rect - if (x < rootRect.mLeft) - { - x = rootRect.mLeft; - } - if (x + dockableRect.getWidth() > rootRect.mRight) - { - x = rootRect.mRight - dockableRect.getWidth(); - } - - mDockTongueX = x + dockableRect.getWidth()/2 - mDockTongue->getWidth() / 2; - - mDockTongueY = dockRect.mTop; - break; - - case TOP: - x = dockRect.getCenterX() - dockableRect.getWidth() / 2; - y = dockRect.mTop + dockableRect.getHeight(); - // unique docking used with dock tongue, so add tongue height to the Y coordinate - if (use_tongue) - { - y += mDockTongue->getHeight(); - - if ( y > rootRect.mTop) - { - y = rootRect.mTop; - } - } - - // check is dockable inside root view rect - if (x < rootRect.mLeft) - { - x = rootRect.mLeft; - } - if (x + dockableRect.getWidth() > rootRect.mRight) - { - x = rootRect.mRight - dockableRect.getWidth(); - } - - - // calculate dock tongue position - dockParentRect = mDockWidget->getParent()->calcScreenRect(); - if (dockRect.getCenterX() < dockParentRect.mLeft) - { - mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2; - } - else if (dockRect.getCenterX() > dockParentRect.mRight) - { - mDockTongueX = dockParentRect.mRight - mDockTongue->getWidth() / 2;; - } - else - { - mDockTongueX = dockRect.getCenterX() - mDockTongue->getWidth() / 2; - } - mDockTongueY = dockRect.mTop; - - break; - case BOTTOM: - x = dockRect.getCenterX() - dockableRect.getWidth() / 2; - y = dockRect.mBottom; - // unique docking used with dock tongue, so add tongue height to the Y coordinate - if (use_tongue) - { - y -= mDockTongue->getHeight(); - } - - // check is dockable inside root view rect - if (x < rootRect.mLeft) - { - x = rootRect.mLeft; - } - if (x + dockableRect.getWidth() > rootRect.mRight) - { - x = rootRect.mRight - dockableRect.getWidth(); - } - - // calculate dock tongue position - dockParentRect = mDockWidget->getParent()->calcScreenRect(); - if (dockRect.getCenterX() < dockParentRect.mLeft) - { - mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2; - } - else if (dockRect.getCenterX() > dockParentRect.mRight) - { - mDockTongueX = dockParentRect.mRight - mDockTongue->getWidth() / 2;; - } - else - { - mDockTongueX = dockRect.getCenterX() - mDockTongue->getWidth() / 2; - } - mDockTongueY = dockRect.mBottom - mDockTongue->getHeight(); - - break; - } - - S32 max_available_height = rootRect.getHeight() - (rootRect.mBottom - mDockTongueY) - mDockTongue->getHeight(); - - // A floater should be shrunk so it doesn't cover a part of its docking tongue and - // there is a space between a dockable floater and a control to which it is docked. - if (use_tongue && dockableRect.getHeight() >= max_available_height) - { - dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(), max_available_height); - mDockableFloater->reshape(dockableRect.getWidth(), dockableRect.getHeight()); - } - else - { - // move dockable - dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(), - dockableRect.getHeight()); - } - LLRect localDocableParentRect; - mDockableFloater->getParent()->screenRectToLocal(dockableRect, - &localDocableParentRect); - mDockableFloater->setRect(localDocableParentRect); - - mDockableFloater->screenPointToLocal(mDockTongueX, mDockTongueY, - &mDockTongueX, &mDockTongueY); - -} - -void LLDockControl::on() -{ - if (isDockVisible()) - { - mEnabled = true; - mRecalculateDocablePosition = true; - } -} - -void LLDockControl::off() -{ - mEnabled = false; -} - -void LLDockControl::forceRecalculatePosition() -{ - mRecalculateDocablePosition = true; -} - -void LLDockControl::drawToungue() -{ - bool use_tongue = false; - LLDockableFloater* dockable_floater = - dynamic_cast (mDockableFloater); - if (dockable_floater != NULL) - { - use_tongue = dockable_floater->getUseTongue(); - } - - if (mEnabled && use_tongue) - { - mDockTongue->draw(mDockTongueX, mDockTongueY); - } -} - +/** + * @file lldockcontrol.cpp + * @brief Creates a panel of a specific kind for a toast + * + * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "lldockcontrol.h" +#include "lldockablefloater.h" + +LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater, + const LLUIImagePtr& dockTongue, DocAt dockAt, get_allowed_rect_callback_t get_allowed_rect_callback) : + mDockWidget(dockWidget), + mDockableFloater(dockableFloater), + mDockTongue(dockTongue), + mDockTongueX(0), + mDockTongueY(0) +{ + mDockAt = dockAt; + + if (dockableFloater->isDocked()) + { + on(); + } + else + { + off(); + } + + if (!(get_allowed_rect_callback)) + { + mGetAllowedRectCallback = boost::bind(&LLDockControl::getAllowedRect, this, _1); + } + else + { + mGetAllowedRectCallback = get_allowed_rect_callback; + } + + if (dockWidget != NULL) + { + repositionDockable(); + } + + if (mDockWidget != NULL) + { + mDockWidgetVisible = isDockVisible(); + } + else + { + mDockWidgetVisible = false; + } +} + +LLDockControl::~LLDockControl() +{ +} + +void LLDockControl::setDock(LLView* dockWidget) +{ + mDockWidget = dockWidget; + if (mDockWidget != NULL) + { + repositionDockable(); + mDockWidgetVisible = isDockVisible(); + } + else + { + mDockWidgetVisible = false; + } +} + +void LLDockControl::getAllowedRect(LLRect& rect) +{ + rect = mDockableFloater->getRootView()->getRect(); +} + +void LLDockControl::repositionDockable() +{ + LLRect dockRect = mDockWidget->calcScreenRect(); + LLRect rootRect; + mGetAllowedRectCallback(rootRect); + + // recalculate dockable position if dock position changed, dock visibility changed, + // root view rect changed or recalculation is forced + if (mPrevDockRect != dockRect || mDockWidgetVisible != isDockVisible() + || mRootRect != rootRect || mRecalculateDocablePosition) + { + // undock dockable and off() if dock not visible + if (!isDockVisible()) + { + mDockableFloater->setDocked(false); + // force off() since dockable may not have dockControll at this time + off(); + LLDockableFloater* dockable_floater = + dynamic_cast (mDockableFloater); + if(dockable_floater != NULL) + { + dockable_floater->onDockHidden(); + } + } + else + { + if(mEnabled) + { + moveDockable(); + } + LLDockableFloater* dockable_floater = + dynamic_cast (mDockableFloater); + if(dockable_floater != NULL) + { + dockable_floater->onDockShown(); + } + } + + mPrevDockRect = dockRect; + mRootRect = rootRect; + mRecalculateDocablePosition = false; + mDockWidgetVisible = isDockVisible(); + } +} + +bool LLDockControl::isDockVisible() +{ + bool res = true; + + if (mDockWidget != NULL) + { + //we should check all hierarchy + res = mDockWidget->isInVisibleChain(); + if (res) + { + LLRect dockRect = mDockWidget->calcScreenRect(); + + switch (mDockAt) + { + case LEFT: // to keep compiler happy + break; + case BOTTOM: + case TOP: + { + // check is dock inside parent rect + // assume that parent for all dockable flaoters + // is the root view + LLRect dockParentRect = + mDockWidget->getRootView()->calcScreenRect(); + if (dockRect.mRight <= dockParentRect.mLeft + || dockRect.mLeft >= dockParentRect.mRight) + { + res = false; + } + break; + } + default: + break; + } + } + } + + return res; +} + +void LLDockControl::moveDockable() +{ + // calculate new dockable position + LLRect dockRect = mDockWidget->calcScreenRect(); + LLRect rootRect; + mGetAllowedRectCallback(rootRect); + + bool use_tongue = false; + LLDockableFloater* dockable_floater = + dynamic_cast (mDockableFloater); + if (dockable_floater != NULL) + { + use_tongue = dockable_floater->getUseTongue(); + } + + LLRect dockableRect = mDockableFloater->calcScreenRect(); + S32 x = 0; + S32 y = 0; + LLRect dockParentRect; + switch (mDockAt) + { + case LEFT: + x = dockRect.mLeft; + y = dockRect.mTop + mDockTongue->getHeight() + dockableRect.getHeight(); + // check is dockable inside root view rect + if (x < rootRect.mLeft) + { + x = rootRect.mLeft; + } + if (x + dockableRect.getWidth() > rootRect.mRight) + { + x = rootRect.mRight - dockableRect.getWidth(); + } + + mDockTongueX = x + dockableRect.getWidth()/2 - mDockTongue->getWidth() / 2; + + mDockTongueY = dockRect.mTop; + break; + + case TOP: + x = dockRect.getCenterX() - dockableRect.getWidth() / 2; + y = dockRect.mTop + dockableRect.getHeight(); + // unique docking used with dock tongue, so add tongue height to the Y coordinate + if (use_tongue) + { + y += mDockTongue->getHeight(); + + if ( y > rootRect.mTop) + { + y = rootRect.mTop; + } + } + + // check is dockable inside root view rect + if (x < rootRect.mLeft) + { + x = rootRect.mLeft; + } + if (x + dockableRect.getWidth() > rootRect.mRight) + { + x = rootRect.mRight - dockableRect.getWidth(); + } + + + // calculate dock tongue position + dockParentRect = mDockWidget->getParent()->calcScreenRect(); + if (dockRect.getCenterX() < dockParentRect.mLeft) + { + mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2; + } + else if (dockRect.getCenterX() > dockParentRect.mRight) + { + mDockTongueX = dockParentRect.mRight - mDockTongue->getWidth() / 2;; + } + else + { + mDockTongueX = dockRect.getCenterX() - mDockTongue->getWidth() / 2; + } + mDockTongueY = dockRect.mTop; + + break; + case BOTTOM: + x = dockRect.getCenterX() - dockableRect.getWidth() / 2; + y = dockRect.mBottom; + // unique docking used with dock tongue, so add tongue height to the Y coordinate + if (use_tongue) + { + y -= mDockTongue->getHeight(); + } + + // check is dockable inside root view rect + if (x < rootRect.mLeft) + { + x = rootRect.mLeft; + } + if (x + dockableRect.getWidth() > rootRect.mRight) + { + x = rootRect.mRight - dockableRect.getWidth(); + } + + // calculate dock tongue position + dockParentRect = mDockWidget->getParent()->calcScreenRect(); + if (dockRect.getCenterX() < dockParentRect.mLeft) + { + mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2; + } + else if (dockRect.getCenterX() > dockParentRect.mRight) + { + mDockTongueX = dockParentRect.mRight - mDockTongue->getWidth() / 2;; + } + else + { + mDockTongueX = dockRect.getCenterX() - mDockTongue->getWidth() / 2; + } + mDockTongueY = dockRect.mBottom - mDockTongue->getHeight(); + + break; + } + + S32 max_available_height = rootRect.getHeight() - (rootRect.mBottom - mDockTongueY) - mDockTongue->getHeight(); + + // A floater should be shrunk so it doesn't cover a part of its docking tongue and + // there is a space between a dockable floater and a control to which it is docked. + if (use_tongue && dockableRect.getHeight() >= max_available_height) + { + dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(), max_available_height); + mDockableFloater->reshape(dockableRect.getWidth(), dockableRect.getHeight()); + } + else + { + // move dockable + dockableRect.setLeftTopAndSize(x, y, dockableRect.getWidth(), + dockableRect.getHeight()); + } + LLRect localDocableParentRect; + mDockableFloater->getParent()->screenRectToLocal(dockableRect, + &localDocableParentRect); + mDockableFloater->setRect(localDocableParentRect); + + mDockableFloater->screenPointToLocal(mDockTongueX, mDockTongueY, + &mDockTongueX, &mDockTongueY); + +} + +void LLDockControl::on() +{ + if (isDockVisible()) + { + mEnabled = true; + mRecalculateDocablePosition = true; + } +} + +void LLDockControl::off() +{ + mEnabled = false; +} + +void LLDockControl::forceRecalculatePosition() +{ + mRecalculateDocablePosition = true; +} + +void LLDockControl::drawToungue() +{ + bool use_tongue = false; + LLDockableFloater* dockable_floater = + dynamic_cast (mDockableFloater); + if (dockable_floater != NULL) + { + use_tongue = dockable_floater->getUseTongue(); + } + + if (mEnabled && use_tongue) + { + mDockTongue->draw(mDockTongueX, mDockTongueY); + } +} + diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 19ac4c58a8..9b6830a816 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -563,7 +563,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) } // update resize bars with new limits - LLResizeBar* last_resize_bar = NULL; + LLLayoutPanel* last_resizeable_panel = NULL; for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { LLPanel* panelp = (*panel_it); @@ -585,17 +585,17 @@ void LLLayoutStack::updateLayout(BOOL force_resize) BOOL resize_bar_enabled = panelp->getVisible() && (*panel_it)->mUserResize; (*panel_it)->mResizeBar->setVisible(resize_bar_enabled); - if (resize_bar_enabled) + if ((*panel_it)->mUserResize || (*panel_it)->mAutoResize) { - last_resize_bar = (*panel_it)->mResizeBar; + last_resizeable_panel = (*panel_it); } } // hide last resize bar as there is nothing past it // resize bars need to be in between two resizable panels - if (last_resize_bar) + if (last_resizeable_panel) { - last_resize_bar->setVisible(FALSE); + last_resizeable_panel->mResizeBar->setVisible(FALSE); } // not enough room to fit existing contents diff --git a/indra/llvfs/lldir_linux.h b/indra/llvfs/lldir_linux.h index 451e81ae93..a34de1241d 100644 --- a/indra/llvfs/lldir_linux.h +++ b/indra/llvfs/lldir_linux.h @@ -1,6 +1,6 @@ /** * @file lldir_linux.h - * @brief Definition of directory utilities class for linux + * @brief Definition of directory utilities class for linux * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code @@ -24,6 +24,10 @@ * $/LicenseInfo$ */ +#if !LL_LINUX +#error This header must not be included when compiling for any target other than Linux. Consider including lldir.h instead. +#endif // !LL_LINUX + #ifndef LL_LLDIR_LINUX_H #define LL_LLDIR_LINUX_H @@ -40,7 +44,7 @@ public: /*virtual*/ void initAppDirs(const std::string &app_name, const std::string& app_read_only_data_dir); -public: + virtual std::string getCurPath(); virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); @@ -53,7 +57,7 @@ private: DIR *mDirp; int mCurrentDirIndex; int mCurrentDirCount; - std::string mCurrentDir; + std::string mCurrentDir; }; #endif // LL_LLDIR_LINUX_H diff --git a/indra/llvfs/lldir_mac.h b/indra/llvfs/lldir_mac.h index 4eac3c3ae6..b456d3afca 100644 --- a/indra/llvfs/lldir_mac.h +++ b/indra/llvfs/lldir_mac.h @@ -24,6 +24,10 @@ * $/LicenseInfo$ */ +#if !LL_DARWIN +#error This header must not be included when compiling for any target other than Mac OS. Consider including lldir.h instead. +#endif // !LL_DARWIN + #ifndef LL_LLDIR_MAC_H #define LL_LLDIR_MAC_H @@ -39,7 +43,7 @@ public: /*virtual*/ void initAppDirs(const std::string &app_name, const std::string& app_read_only_data_dir); -public: + virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask); virtual std::string getCurPath(); virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); diff --git a/indra/llvfs/lldir_solaris.h b/indra/llvfs/lldir_solaris.h index 4a1794f539..70fac6f818 100644 --- a/indra/llvfs/lldir_solaris.h +++ b/indra/llvfs/lldir_solaris.h @@ -24,6 +24,10 @@ * $/LicenseInfo$ */ +#if !LL_SOLARIS +#error This header must not be included when compiling for any target other than Solaris. Consider including lldir.h instead. +#endif // !LL_SOLARIS + #ifndef LL_LLDIR_SOLARIS_H #define LL_LLDIR_SOLARIS_H @@ -40,7 +44,7 @@ public: /*virtual*/ void initAppDirs(const std::string &app_name, const std::string& app_read_only_data_dir); -public: + virtual std::string getCurPath(); virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask); virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); @@ -50,7 +54,7 @@ private: DIR *mDirp; int mCurrentDirIndex; int mCurrentDirCount; - std::string mCurrentDir; + std::string mCurrentDir; }; #endif // LL_LLDIR_SOLARIS_H diff --git a/indra/llvfs/lldir_win32.h b/indra/llvfs/lldir_win32.h index 4c932c932c..b170ebbcd7 100644 --- a/indra/llvfs/lldir_win32.h +++ b/indra/llvfs/lldir_win32.h @@ -24,6 +24,10 @@ * $/LicenseInfo$ */ +#if !LL_WINDOWS +#error This header must not be included when compiling for any target other than Windows. Consider including lldir.h instead. +#endif // !LL_WINDOWS + #ifndef LL_LLDIR_WIN32_H #define LL_LLDIR_WIN32_H @@ -47,8 +51,8 @@ public: /*virtual*/ std::string getLLPluginFilename(std::string base_name); private: - BOOL LLDir_Win32::getNextFileInDir(const llutf16string &dirname, const std::string &mask, std::string &fname); - + BOOL getNextFileInDir(const llutf16string &dirname, const std::string &mask, std::string &fname); + void* mDirSearch_h; llutf16string mCurrentDir; }; diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 80edf5d3c9..ea8b1c681d 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1,12524 +1,12535 @@ - - - - CrashHostUrl - - Comment - A URL pointing to a crash report handler; overrides cluster negotiation to locate crash handler. - Persist - 1 - Type - String - Value - - - AFKTimeout - - Comment - Time before automatically setting AFK (away from keyboard) mode (seconds, 0=never). - Valid values are: 0, 120, 300, 600, 1800 - Persist - 1 - Type - S32 - Value - 300 - - AdminMenu - - Comment - Enable the debug admin menu from the main menu. Note: This will just allow the menu to be shown; this does not grant admin privileges. - Persist - 0 - Type - Boolean - Value - 0 - - ActiveFloaterTransparency - - Comment - Transparency of active floaters (floaters that have focus) - Persist - 1 - Type - F32 - Value - 0.95 - - AdvanceSnapshot - - Comment - Display advanced parameter settings in snaphot interface - Persist - 1 - Type - Boolean - Value - 0 - - AgentPause - - Comment - Ask the simulator to stop updating the agent while enabled - Persist - 0 - Type - Boolean - Value - 0 - - AlertedUnsupportedHardware - - Comment - Set if there's unsupported hardware and we've already done a notification. - Persist - 1 - Type - Boolean - Value - 0 - - AllowMultipleViewers - - Comment - Allow multiple viewers. - Persist - 1 - Type - Boolean - Value - 0 - - AllowTapTapHoldRun - - Comment - Tapping a direction key twice and holding it down makes avatar run - Persist - 1 - Type - Boolean - Value - 1 - - AnimateTextures - - Comment - Enable texture animation (debug) - Persist - 1 - Type - Boolean - Value - 1 - - AnimationDebug - - Comment - Show active animations in a bubble above avatars head - Persist - 1 - Type - Boolean - Value - 0 - - AppearanceCameraMovement - - Comment - When entering appearance editing mode, camera zooms in on currently selected portion of avatar - Persist - 1 - Type - Boolean - Value - 1 - - ApplyColorImmediately - - Comment - Preview selections in color picker immediately - Persist - 1 - Type - Boolean - Value - 1 - - ApplyTextureImmediately - - Comment - Preview selections in texture picker immediately - Persist - 1 - Type - Boolean - Value - 1 - - ArrowKeysAlwaysMove - - Comment - While cursor is in chat entry box, arrow keys still control your avatar - Persist - 1 - Type - Boolean - Value - 0 - - AskedAboutCrashReports - - Comment - Turns off dialog asking if you want to enable crash reporting - Persist - 1 - Type - Boolean - Value - 0 - - AuctionShowFence - - Comment - When auctioning land, include parcel boundary marker in snapshot - Persist - 1 - Type - Boolean - Value - 1 - - AudioLevelAmbient - - Comment - Audio level of environment sounds - Persist - 1 - Type - F32 - Value - 0.5 - - AudioLevelDoppler - - Comment - Scale of doppler effect on moving audio sources (1.0 = normal, <1.0 = diminished doppler effect, >1.0 = enhanced doppler effect) - Persist - 1 - Type - F32 - Value - 1.0 - - AudioLevelMaster - - Comment - Master audio level, or overall volume - Persist - 1 - Type - F32 - Value - 1.0 - - AudioLevelMedia - - Comment - Audio level of Quicktime movies - Persist - 1 - Type - F32 - Value - 0.5 - - AudioLevelMic - - Comment - Audio level of microphone input - Persist - 1 - Type - F32 - Value - 1.0 - - AudioLevelMusic - - Comment - Audio level of streaming music - Persist - 1 - Type - F32 - Value - 0.5 - - AudioLevelRolloff - - Comment - Controls the distance-based dropoff of audio volume (fraction or multiple of default audio rolloff) - Persist - 1 - Type - F32 - Value - 1.0 - - AudioLevelSFX - - Comment - Audio level of in-world sound effects - Persist - 1 - Type - F32 - Value - 0.5 - - AudioLevelUI - - Comment - Audio level of UI sound effects - Persist - 1 - Type - F32 - Value - 0.5 - - AudioLevelVoice - - Comment - Audio level of voice chat - Persist - 1 - Type - F32 - Value - 0.5 - - AudioLevelWind - - Comment - Audio level of wind noise when standing still - Persist - 1 - Type - F32 - Value - 0.5 - - AudioStreamingMedia - - Comment - Enable streaming - Persist - 1 - Type - Boolean - Value - 1 - - AudioStreamingMusic - - Comment - Enable streaming audio - Persist - 1 - Type - Boolean - Value - 1 - - AuditTexture - - Comment - Enable texture auditting. - Persist - 1 - Type - Boolean - Value - 0 - - AutoAcceptNewInventory - - Comment - Automatically accept new notecards/textures/landmarks - Persist - 1 - Type - Boolean - Value - 0 - - AutoLeveling - - Comment - Keep Flycam level. - Persist - 1 - Type - Boolean - Value - 1 - - AutoLoadWebProfiles - - Comment - Automatically load ALL profile webpages without asking first. - Persist - 1 - Type - Boolean - Value - 0 - - AutoLogin - - Comment - Login automatically using last username/password combination - Persist - 0 - Type - Boolean - Value - 0 - - AutoMimeDiscovery - - Comment - Enable viewer mime type discovery of media URLs - Persist - 1 - Type - Boolean - Value - 0 - - AutoPilotLocksCamera - - Comment - Keep camera position locked when avatar walks to selected position - Persist - 1 - Type - Boolean - Value - 0 - - AutoSnapshot - - Comment - Update snapshot when camera stops moving, or any parameter changes - Persist - 1 - Type - Boolean - Value - 0 - - AutomaticFly - - Comment - Fly by holding jump key or using "Fly" command (FALSE = fly by using "Fly" command only) - Persist - 1 - Type - Boolean - Value - 1 - - AvalinePhoneSeparator - - Comment - Separator of phone parts to have Avaline numbers human readable in Voice Control Panel - Persist - 1 - Type - String - Value - - - - AvatarAxisDeadZone0 - - Comment - Avatar axis 0 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - AvatarAxisDeadZone1 - - Comment - Avatar axis 1 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - AvatarAxisDeadZone2 - - Comment - Avatar axis 2 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - AvatarAxisDeadZone3 - - Comment - Avatar axis 3 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - AvatarAxisDeadZone4 - - Comment - Avatar axis 4 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - AvatarAxisDeadZone5 - - Comment - Avatar axis 5 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - AvatarAxisScale0 - - Comment - Avatar axis 0 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - AvatarAxisScale1 - - Comment - Avatar axis 1 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - AvatarAxisScale2 - - Comment - Avatar axis 2 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - AvatarAxisScale3 - - Comment - Avatar axis 3 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - AvatarAxisScale4 - - Comment - Avatar axis 4 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - AvatarAxisScale5 - - Comment - Avatar axis 5 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - AvatarBacklight - - Comment - Add rim lighting to avatar rendering to approximate shininess of skin - Persist - 1 - Type - Boolean - Value - 1 - - AvatarFeathering - - Comment - Avatar feathering (less is softer) - Persist - 1 - Type - F32 - Value - 16.0 - - AvatarPickerSortOrder - - Comment - Specifies sort key for textures in avatar picker (+0 = name, +1 = date, +2 = folders always by name, +4 = system folders to top) - Persist - 1 - Type - U32 - Value - 2 - - AvatarPickerURL - - Comment - Avatar picker contents - Persist - 1 - Type - String - Value - - - AvatarBakedTextureUploadTimeout - - Comment - Specifes the maximum time in seconds to wait before sending your baked textures for avatar appearance. Set to 0 to disable and wait until all baked textures are at highest resolution. - Persist - 1 - Type - U32 - Value - 60 - - AvatarBakedLocalTextureUpdateTimeout - - Comment - Specifes the maximum time in seconds to wait before updating your appearance during appearance mode. - Persist - 1 - Type - U32 - Value - 10 - - - AvatarSex - - Comment - - Persist - 0 - Type - U32 - Value - 0 - - BackgroundYieldTime - - Comment - Amount of time to yield every frame to other applications when SL is not the foreground window (milliseconds) - Persist - 1 - Type - S32 - Value - 40 - - BottomPanelNew - - Comment - Enable the new bottom panel - Persist - 1 - Type - Boolean - Value - 0 - - BrowserHomePage - - Comment - [NOT USED] - Persist - 1 - Type - String - Value - http://www.secondlife.com - - BrowserIgnoreSSLCertErrors - - Comment - FOR TESTING ONLY: Tell the built-in web browser to ignore SSL cert errors. - Persist - 1 - Type - Boolean - Value - 0 - - BlockAvatarAppearanceMessages - - Comment - Ignores appearance messages (for simulating Ruth) - Persist - 1 - Type - Boolean - Value - 0 - - BlockSomeAvatarAppearanceVisualParams - - Comment - Drop around 50% of VisualParam occurances in appearance messages (for simulating Ruth) - Persist - 1 - Type - Boolean - Value - 0 - - BrowserProxyAddress - - Comment - Address for the Web Proxy] - Persist - 1 - Type - String - Value - - - BrowserProxyEnabled - - Comment - Use Web Proxy - Persist - 1 - Type - Boolean - Value - 0 - - BrowserProxyExclusions - - Comment - [NOT USED] - Persist - 1 - Type - String - Value - - - BrowserProxyPort - - Comment - Port for Web Proxy - Persist - 1 - Type - S32 - Value - 3128 - - BrowserProxySocks45 - - Comment - [NOT USED] - Persist - 1 - Type - S32 - Value - 5 - - BuildAxisDeadZone0 - - Comment - Build axis 0 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - BuildAxisDeadZone1 - - Comment - Build axis 1 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - BuildAxisDeadZone2 - - Comment - Build axis 2 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - BuildAxisDeadZone3 - - Comment - Build axis 3 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - BuildAxisDeadZone4 - - Comment - Build axis 4 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - BuildAxisDeadZone5 - - Comment - Build axis 5 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - BuildAxisScale0 - - Comment - Build axis 0 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - BuildAxisScale1 - - Comment - Build axis 1 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - BuildAxisScale2 - - Comment - Build axis 2 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - BuildAxisScale3 - - Comment - Build axis 3 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - BuildAxisScale4 - - Comment - Build axis 4 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - BuildAxisScale5 - - Comment - Build axis 5 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - BuildFeathering - - Comment - Build feathering (less is softer) - Persist - 1 - Type - F32 - Value - 16.0 - - BulkChangeIncludeAnimations - - Comment - Bulk permission changes affect animations - Persist - 1 - Type - Boolean - Value - 1 - - BulkChangeIncludeBodyParts - - Comment - Bulk permission changes affect body parts - Persist - 1 - Type - Boolean - Value - 1 - - BulkChangeIncludeClothing - - Comment - Bulk permission changes affect clothing - Persist - 1 - Type - Boolean - Value - 1 - - BulkChangeIncludeGestures - - Comment - Bulk permission changes affect gestures - Persist - 1 - Type - Boolean - Value - 1 - - BulkChangeIncludeNotecards - - Comment - Bulk permission changes affect notecards - Persist - 1 - Type - Boolean - Value - 1 - - BulkChangeIncludeObjects - - Comment - Bulk permission changes affect objects - Persist - 1 - Type - Boolean - Value - 1 - - BulkChangeIncludeScripts - - Comment - Bulk permission changes affect scripts - Persist - 1 - Type - Boolean - Value - 1 - - BulkChangeIncludeSounds - - Comment - Bulk permission changes affect sounds - Persist - 1 - Type - Boolean - Value - 1 - - BulkChangeIncludeTextures - - Comment - Bulk permission changes affect textures - Persist - 1 - Type - Boolean - Value - 1 - - BulkChangeEveryoneCopy - - Comment - Bulk changed objects can be copied by everyone - Persist - 1 - Type - Boolean - Value - 0 - - BulkChangeNextOwnerCopy - - Comment - Bulk changed objects can be copied by next owner - Persist - 1 - Type - Boolean - Value - 0 - - BulkChangeNextOwnerModify - - Comment - Bulk changed objects can be modified by next owner - Persist - 1 - Type - Boolean - Value - 0 - - BulkChangeNextOwnerTransfer - - Comment - Bulk changed objects can be resold or given away by next owner - Persist - 1 - Type - Boolean - Value - 0 - - BulkChangeShareWithGroup - - Comment - Bulk changed objects are shared with the currently active group - Persist - 1 - Type - Boolean - Value - 0 - - ButtonFlashCount - - Comment - Number of flashes after which flashing buttons stay lit up - Persist - 1 - Type - S32 - Value - 8 - - ButtonFlashRate - - Comment - Frequency at which buttons flash (hz) - Persist - 1 - Type - F32 - Value - 1.25 - - ButtonHPad - - Comment - Default horizontal spacing between buttons (pixels) - Persist - 1 - Type - S32 - Value - 4 - - ButtonHeight - - Comment - Default height for normal buttons (pixels) - Persist - 1 - Type - S32 - Value - 23 - - ButtonHeightSmall - - Comment - Default height for small buttons (pixels) - Persist - 1 - Type - S32 - Value - 23 - - CacheLocation - - Comment - Controls the location of the local disk cache - Persist - 1 - Type - String - Value - - - CacheLocationTopFolder - - Comment - Controls the top folder location of the the local disk cache - Persist - 1 - Type - String - Value - - - CacheNumberOfRegionsForObjects - - Comment - Controls number of regions to be cached for objects. - Persist - 1 - Type - U32 - Value - 128 - - CacheSize - - Comment - Controls amount of hard drive space reserved for local file caching in MB - Persist - 1 - Type - U32 - Value - 512 - - CacheValidateCounter - - Comment - Used to distribute cache validation - Persist - 1 - Type - U32 - Value - 0 - - CameraMouseWheelZoom - - Comment - Camera zooms in and out with mousewheel - Persist - 1 - Type - S32 - Value - 5 - - CameraAngle - - Comment - Camera field of view angle (Radians) - Persist - 1 - Type - F32 - Value - 1.047197551 - - CameraOffset - - Comment - Render with camera offset from view frustum (rendering debug) - Persist - 1 - Type - Boolean - Value - 0 - - CameraOffsetBuild - - Comment - Default camera position relative to focus point when entering build mode - Persist - 1 - Type - Vector3 - Value - - -6.0 - 0.0 - 6.0 - - - CameraOffsetRearView - - Comment - Initial camera offset from avatar in Rear View - Persist - 1 - Type - Vector3 - Value - - -3.0 - 0.0 - 0.75 - - - CameraOffsetFrontView - - Comment - Initial camera offset from avatar in Front View - Persist - 1 - Type - Vector3 - Value - - 2.2 - 0.0 - 0.0 - - - CameraOffsetGroupView - - Comment - Initial camera offset from avatar in Group View - Persist - 1 - Type - Vector3 - Value - - -1.0 - 0.7 - 0.5 - - - CameraOffsetScale - - Comment - Scales the default offset - Persist - 1 - Type - F32 - Value - 1.0 - - CameraPosOnLogout - - Comment - Camera position when last logged out (global coordinates) - Persist - 1 - Type - Vector3D - Value - - 0.0 - 0.0 - 0.0 - - - CameraPositionSmoothing - - Comment - Smooths camera position over time - Persist - 1 - Type - F32 - Value - 1.0 - - CameraPreset - - Comment - Preset camera position - view (0 - rear, 1 - front, 2 - group) - Persist - 1 - Type - U32 - Value - 0 - - CertStore - - Comment - Specifies the Certificate Store for certificate trust verification - Persist - 1 - Type - String - Value - default - - ChatBarStealsFocus - - Comment - Whenever keyboard focus is removed from the UI, and the chat bar is visible, the chat bar takes focus - Persist - 1 - Type - Boolean - Value - 1 - - LetterKeysFocusChatBar - - Comment - When printable characters keys (possibly with Shift held) are pressed, the chatbar takes focus - Persist - 1 - Type - S32 - Value - 0 - - ChatBubbleOpacity - - Comment - Opacity of chat bubble background (0.0 = completely transparent, 1.0 = completely opaque) - Persist - 1 - Type - F32 - Value - 0.5 - - ChatFontSize - - Comment - Size of chat text in chat console (0 = small, 1 = big) - Persist - 1 - Type - S32 - Value - 1 - - ChatFullWidth - - Comment - Chat console takes up full width of SL window - Persist - 1 - Type - Boolean - Value - 1 - - ChatHistoryTornOff - - Comment - Show chat history window separately from Communicate window. - Persist - 1 - Type - Boolean - Value - 0 - - ChatOnlineNotification - - Comment - Provide notifications for when friend log on and off of SL - Persist - 1 - Type - Boolean - Value - 1 - - ChatPersistTime - - Comment - Time for which chat stays visible in console (seconds) - Persist - 1 - Type - F32 - Value - 20.0 - - ChatShowTimestamps - - Comment - Show timestamps in chat - Persist - 1 - Type - Boolean - Value - 1 - - ChatVisible - - Comment - Chat bar is visible - Persist - 1 - Type - Boolean - Value - 1 - - ChatWindow - - Comment - Show chat in multiple windows(by default) or in one multi-tabbed window(requires restart) - Persist - 1 - Type - S32 - Value - 0 - - CheesyBeacon - - Comment - Enable cheesy beacon effects - Persist - 1 - Type - Boolean - Value - 0 - - ClientSettingsFile - - Comment - Client settings file name (per install). - Persist - 0 - Type - String - Value - - - CloseChatOnReturn - - Comment - Close chat after hitting return - Persist - 1 - Type - Boolean - Value - 0 - - CloseSnapshotOnKeep - - Comment - Close snapshot window after saving snapshot - Persist - 1 - Type - Boolean - Value - 1 - - CmdLineDisableVoice - - Comment - Disable Voice. - Persist - 0 - Type - Boolean - Value - 0 - - CmdLineGridChoice - - Comment - The user's grid choice or ip address. - Persist - 0 - Type - String - Value - - - CmdLineHelperURI - - Comment - Command line specified helper web CGI prefix to use. - Persist - 0 - Type - String - Value - - - CmdLineLoginURI - - Comment - Command line specified login server and CGI prefix to use. - Persist - 0 - Type - LLSD - Value - - - - - CompressSnapshotsToDisk - - Comment - Compress snapshots saved to disk (Using JPEG 2000) - Persist - 1 - Type - Boolean - Value - 0 - - ConnectAsGod - - Comment - Log in a god if you have god access. - Persist - 1 - Type - Boolean - Value - 0 - - ConnectionPort - - Comment - Custom connection port number - Persist - 1 - Type - U32 - Value - 13000 - - ConnectionPortEnabled - - Comment - Use the custom connection port? - Persist - 1 - Type - Boolean - Value - 0 - - ConsoleBackgroundOpacity - - Comment - Opacity of chat console (0.0 = completely transparent, 1.0 = completely opaque) - Persist - 1 - Type - F32 - Value - 0.700 - - ConsoleBufferSize - - Comment - Size of chat console history (lines of chat) - Persist - 1 - Type - S32 - Value - 40 - - ConsoleMaxLines - - Comment - Max number of lines of chat text visible in console. - Persist - 1 - Type - S32 - Value - 40 - - ContactsTornOff - - Comment - Show contacts window separately from Communicate window. - Persist - 1 - Type - Boolean - Value - 0 - - CookiesEnabled - - Comment - Accept cookies from Web sites? - Persist - 1 - Type - Boolean - Value - 1 - - BrowserJavascriptEnabled - - Comment - Enable Javascript in the built-in Web browser? - Persist - 1 - Type - Boolean - Value - 1 - - BrowserPluginsEnabled - - Comment - Enable Web plugins in the built-in Web browser? - Persist - 1 - Type - Boolean - Value - 1 - - ChatBarCustomWidth - - Comment - Stores customized width of chat bar. - Persist - 1 - Type - S32 - Value - 0 - - CreateToolCopyCenters - - Comment - - Persist - 0 - Type - Boolean - Value - 1 - - CreateToolCopyRotates - - Comment - - Persist - 0 - Type - Boolean - Value - 0 - - CreateToolCopySelection - - Comment - - Persist - 0 - Type - Boolean - Value - 0 - - CreateToolKeepSelected - - Comment - After using create tool, keep the create tool active - Persist - 1 - Type - Boolean - Value - 0 - - Cursor3D - - Comment - Treat Joystick values as absolute positions (not deltas). - Persist - 1 - Type - Boolean - Value - 1 - - CurrentGrid - - Comment - Currently Selected Grid - Persist - 1 - Type - String - Value - - - CustomServer - - Comment - Specifies IP address or hostname of grid to which you connect - Persist - 1 - Type - String - Value - - - DebugAvatarRezTime - - Comment - Display times for avatars to resolve. - Persist - 1 - Type - Boolean - Value - 0 - - DebugAvatarLocalTexLoadedTime - - Comment - Display time for loading avatar local textures. - Persist - 1 - Type - Boolean - Value - 0 - - DebugBeaconLineWidth - - Comment - Size of lines for Debug Beacons - Persist - 1 - Type - S32 - Value - 1 - - DebugInventoryFilters - - Comment - Turn on debugging display for inventory filtering - Persist - 1 - Type - Boolean - Value - 0 - - DebugPermissions - - Comment - Log permissions for selected inventory items - Persist - 1 - Type - Boolean - Value - 0 - - DebugPluginDisableTimeout - - Comment - Disable the code which watches for plugins that are crashed or hung - Persist - 1 - Type - Boolean - Value - 0 - - DebugShowColor - - Comment - Show color under cursor - Persist - 1 - Type - Boolean - Value - 0 - - DebugShowMemory - - Comment - Show Total Allocated Memory - Persist - 1 - Type - Boolean - Value - 0 - - DebugShowRenderInfo - - Comment - Show depth buffer contents - Persist - 1 - Type - Boolean - Value - 0 - - DebugShowRenderMatrices - - Comment - Display values of current view and projection matrices. - Persist - 1 - Type - Boolean - Value - 0 - - DebugShowTextureInfo - - Comment - Show inertested texture info - Persist - 1 - Type - Boolean - Value - 0 - - DebugShowTime - - Comment - Show time info - Persist - 1 - Type - Boolean - Value - 0 - - DebugShowXUINames - - Comment - Show tooltips with XUI path to widget - Persist - 0 - Type - Boolean - Value - 0 - - DebugStatModeFPS - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeBandwidth - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModePacketLoss - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatMode - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeKTrisDrawnFr - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeKTrisDrawnSec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeTotalObjs - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeNewObjs - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeTextureCount - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeRawCount - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeGLMem - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeFormattedMem - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeRawMem - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeBoundMem - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModePacketsIn - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModePacketsOut - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeObjects - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeTexture - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeAsset - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeLayers - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeActualIn - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeActualOut - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeVFSPendingOps - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeTimeDialation - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimFPS - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModePhysicsFPS - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModePinnedObjects - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeLowLODObjects - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeMemoryAllocated - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeAgentUpdatesSec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeMainAgents - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeChildAgents - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimObjects - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimActiveObjects - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimActiveScripts - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimScriptEvents - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimInPPS - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimOutPPS - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimPendingDownloads - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - SimPendingUploads - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimTotalUnackedBytes - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimFrameMsec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimNetMsec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimSimPhysicsMsec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimSimOtherMsec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimAgentMsec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimImagesMsec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimScriptMsec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimSpareMsec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimSimPhysicsStepMsec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimSimPhysicsShapeUpdateMsec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimSimPhysicsOtherMsec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimSleepMsec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugStatModeSimPumpIOMsec - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - - DebugViews - - Comment - Display debugging info for views. - Persist - 1 - Type - Boolean - Value - 0 - - DebugWindowProc - - Comment - Log windows messages - Persist - 1 - Type - Boolean - Value - 0 - - DefaultFemaleAvatar - - Comment - Default Female Avatar - Persist - 1 - Type - String - Value - Female Shape & Outfit - - DefaultMaleAvatar - - Comment - Default Male Avatar - Persist - 1 - Type - String - Value - Male Shape & Outfit - - - DefaultObjectTexture - - Comment - Texture used as 'Default' in texture picker. (UUID texture reference) - Persist - 1 - Type - String - Value - 89556747-24cb-43ed-920b-47caed15465f - - DefaultUploadCost - - Comment - Default sound/image/file upload cost(in case economy data is not available). - Persist - 1 - Type - U32 - Value - 10 - - DestinationGuideURL - - Comment - Destination guide contents - Persist - 1 - Type - String - Value - - - DisableCameraConstraints - - Comment - Disable the normal bounds put on the camera by avatar position - Persist - 1 - Type - Boolean - Value - 0 - - DisableMouseWarp - - Comment - Disable warping of the mouse to the center of the screen during alt-zoom and mouse look. Useful with certain input devices, mouse sharing programs like Synergy, or running under Parallels. - Persist - 1 - Type - Boolean - Value - 0 - - DisableExternalBrowser - - Comment - Disable opening an external browser. - Persist - 1 - Type - Boolean - Value - 0 - - DisableRendering - - Comment - Disable GL rendering and GUI (load testing) - Persist - 1 - Type - Boolean - Value - 0 - - DisableTextHyperlinkActions - - Comment - Disable highlighting and linking of URLs in XUI text boxes - Persist - 1 - Type - Boolean - Value - 0 - - DisableVerticalSync - - Comment - Update frames as fast as possible (FALSE = update frames between display scans) - Persist - 1 - Type - Boolean - Value - 1 - - EnableGroupChatPopups - - Comment - Enable Incoming Group Chat Popups - Persist - 1 - Type - Boolean - Value - 1 - - EnableIMChatPopups - - Comment - Enable Incoming IM Chat Popups - Persist - 1 - Type - Boolean - Value - 1 - - DisplayAvatarAgentTarget - - Comment - Show avatar positioning locators (animation debug) - Persist - 1 - Type - Boolean - Value - 0 - - DisplayChat - - Comment - Display Latest Chat message on LCD - Persist - 1 - Type - Boolean - Value - 1 - - DisplayDebug - - Comment - Display Network Information on LCD - Persist - 1 - Type - Boolean - Value - 1 - - DisplayDebugConsole - - Comment - Display Console Debug Information on LCD - Persist - 1 - Type - Boolean - Value - 1 - - DisplayIM - - Comment - Display Latest IM message on LCD - Persist - 1 - Type - Boolean - Value - 1 - - DisplayLinden - - Comment - Display Account Information on LCD - Persist - 1 - Type - Boolean - Value - 1 - - DisplayRegion - - Comment - Display Location information on LCD - Persist - 1 - Type - Boolean - Value - 1 - - DisplayTimecode - - Comment - Display timecode on screen - Persist - 1 - Type - Boolean - Value - 0 - - Disregard128DefaultDrawDistance - - Comment - Whether to use the auto default to 128 draw distance - Persist - 1 - Type - Boolean - Value - 1 - - Disregard96DefaultDrawDistance - - Comment - Whether to use the auto default to 96 draw distance - Persist - 1 - Type - Boolean - Value - 1 - - ClickActionBuyEnabled - - Comment - Enable click to buy actions in tool pie menu - Persist - 1 - Type - Boolean - Value - 1 - - ClickActionPayEnabled - - Comment - Enable click to pay actions in tool pie menu - Persist - 1 - Type - Boolean - Value - 1 - - DoubleClickAutoPilot - - Comment - Enable double-click auto pilot - Persist - 1 - Type - Boolean - Value - 0 - - DoubleClickTeleport - - Comment - Enable double-click to teleport where allowed - Persist - 1 - Type - Boolean - Value - 0 - - DoubleClickShowWorldMap - - Comment - Enable double-click to show world map from mini map - Persist - 1 - Type - Boolean - Value - 1 - - DragAndDropToolTipDelay - - Comment - Seconds before displaying tooltip when performing drag and drop operation - Persist - 1 - Type - F32 - Value - 0.10000000149 - - DragAndDropDistanceThreshold - - Comment - Number of pixels that mouse should move before triggering drag and drop mode - Persist - 1 - Type - S32 - Value - 3 - - DropShadowButton - - Comment - Drop shadow width for buttons (pixels) - Persist - 1 - Type - S32 - Value - 2 - - DropShadowFloater - - Comment - Drop shadow width for floaters (pixels) - Persist - 1 - Type - S32 - Value - 5 - - DropShadowSlider - - Comment - Drop shadow width for sliders (pixels) - Persist - 1 - Type - S32 - Value - 3 - - DropShadowTooltip - - Comment - Drop shadow width for tooltips (pixels) - Persist - 1 - Type - S32 - Value - 4 - - DumpVFSCaches - - Comment - Dump VFS caches on startup. - Persist - 1 - Type - Boolean - Value - 0 - - DynamicCameraStrength - - Comment - Amount camera lags behind avatar motion (0 = none, 30 = avatar velocity) - Persist - 1 - Type - F32 - Value - 2.0 - - EditCameraMovement - - Comment - When entering build mode, camera moves up above avatar - Persist - 1 - Type - Boolean - Value - 0 - - EditLinkedParts - - Comment - Select individual parts of linked objects - Persist - 0 - Type - Boolean - Value - 0 - - EffectScriptChatParticles - - Comment - 1 = normal behavior, 0 = disable display of swirling lights when scripts communicate - Persist - 1 - Type - Boolean - Value - 1 - - EnableGrab - - Comment - Use Ctrl+mouse to grab and manipulate objects - Persist - 1 - Type - Boolean - Value - 1 - - EnableAltZoom - - Comment - Use Alt+mouse to look at and zoom in on objects - Persist - 1 - Type - Boolean - Value - 1 - - EnableMouselook - - Comment - Allow first person perspective and mouse control of camera - Persist - 1 - Type - Boolean - Value - 1 - - EnableRippleWater - - Comment - Whether to use ripple water shader or not - Persist - 1 - Type - Boolean - Value - 1 - - EnableTextureAtlas - - Comment - Whether to use texture atlas or not - Persist - 1 - Type - Boolean - Value - 0 - - EnableUIHints - - Comment - Toggles UI hint popups - Persist - 1 - Type - Boolean - Value - 1 - - EnableVoiceChat - - Comment - Enable talking to other residents with a microphone - Persist - 1 - Type - Boolean - Value - 1 - - EnergyFromTop - - Comment - - Persist - 0 - Type - S32 - Value - 20 - - EnergyHeight - - Comment - - Persist - 0 - Type - S32 - Value - 40 - - EnergyWidth - - Comment - - Persist - 0 - Type - S32 - Value - 175 - - EventURL - - Comment - URL for Event website, displayed in the event floater - Persist - 0 - Type - String - Value - http://events.secondlife.com/viewer/embed/event/ - - EveryoneCopy - - Comment - Everyone can copy the newly created objects - Persist - 1 - Type - Boolean - Value - 0 - - FeatureManagerHTTPTable - - Comment - Base directory for HTTP feature/gpu table fetches - Persist - 1 - Type - String - Value - http://viewer-settings.secondlife.com - - FPSLogFrequency - - Comment - Seconds between display of FPS in log (0 for never) - Persist - 1 - Type - F32 - Value - 10.0 - - FilterItemsPerFrame - - Comment - Maximum number of inventory items to match against search filter every frame (lower to increase framerate while searching, higher to improve search speed) - Persist - 1 - Type - S32 - Value - 500 - - FindLandArea - - Comment - Enables filtering of land search results by area - Persist - 1 - Type - Boolean - Value - 0 - - FindLandPrice - - Comment - Enables filtering of land search results by price - Persist - 1 - Type - Boolean - Value - 1 - - FindLandType - - Comment - Controls which type of land you are searching for in Find Land interface ("All", "Auction", "For Sale") - Persist - 1 - Type - String - Value - All - - FindPeopleOnline - - Comment - Limits people search to only users who are logged on - Persist - 1 - Type - Boolean - Value - 1 - - FindPlacesPictures - - Comment - Display only results of find places that have pictures - Persist - 1 - Type - Boolean - Value - 1 - - FirstLoginThisInstall - - Comment - Specifies that you have not successfully logged in since you installed the latest update - Persist - 1 - Type - Boolean - Value - 1 - - FirstName - - Comment - Login first name - Persist - 1 - Type - String - Value - - - FirstPersonAvatarVisible - - Comment - Display avatar and attachments below neck while in mouselook - Persist - 1 - Type - Boolean - Value - 0 - - FirstRunThisInstall - - Comment - Specifies that you have not run the viewer since you installed the latest update - Persist - 1 - Type - Boolean - Value - 1 - - FirstSelectedDisabledPopups - - Comment - Return false if there is not disabled popup selected in the list of floater preferences popups - Persist - 0 - Type - Boolean - Value - 0 - - FirstSelectedEnabledPopups - - Comment - Return false if there is not enable popup selected in the list of floater preferences popups - Persist - 0 - Type - Boolean - Value - 0 - - FixedWeather - - Comment - Weather effects do not change over time - Persist - 1 - Type - Boolean - Value - 0 - - FloaterActiveSpeakersSortAscending - - Comment - Whether to sort up or down - Persist - 1 - Type - Boolean - Value - 1 - - FloaterActiveSpeakersSortColumn - - Comment - Column name to sort on - Persist - 1 - Type - String - Value - speaking_status - - FloaterMapNorth - - Comment - Floater Map North Label - Persist - 1 - Type - String - Value - N - - FloaterMapNorthEast - - Comment - Floater Map North-East Label - Persist - 1 - Type - String - Value - NE - - FloaterMapNorthWest - - Comment - Floater Map North-West Label - Persist - 1 - Type - String - Value - NW - - FloaterMapEast - - Comment - Floater Map East Label - Persist - 1 - Type - String - Value - E - - FloaterMapWest - - Comment - Floater Map West Label - Persist - 1 - Type - String - Value - W - - FloaterMapSouth - - Comment - Floater Map South Label - Persist - 1 - Type - String - Value - S - - FloaterMapSouthEast - - Comment - Floater Map South-East Label - Persist - 1 - Type - String - Value - SE - - FloaterMapSouthWest - - Comment - Floater Map South-West Label - Persist - 1 - Type - String - Value - SW - - - FloaterStatisticsRect - - Comment - Rectangle for chat history - Persist - 1 - Type - Rect - Value - - 0 - 400 - 250 - 0 - - - FlycamAbsolute - - Comment - Treat Flycam values as absolute positions (not deltas). - Persist - 1 - Type - Boolean - Value - 0 - - FlycamAxisDeadZone0 - - Comment - Flycam axis 0 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - FlycamAxisDeadZone1 - - Comment - Flycam axis 1 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - FlycamAxisDeadZone2 - - Comment - Flycam axis 2 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - FlycamAxisDeadZone3 - - Comment - Flycam axis 3 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - FlycamAxisDeadZone4 - - Comment - Flycam axis 4 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - FlycamAxisDeadZone5 - - Comment - Flycam axis 5 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - FlycamAxisDeadZone6 - - Comment - Flycam axis 6 dead zone. - Persist - 1 - Type - F32 - Value - 0.1 - - FlycamAxisScale0 - - Comment - Flycam axis 0 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - FlycamAxisScale1 - - Comment - Flycam axis 1 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - FlycamAxisScale2 - - Comment - Flycam axis 2 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - FlycamAxisScale3 - - Comment - Flycam axis 3 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - FlycamAxisScale4 - - Comment - Flycam axis 4 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - FlycamAxisScale5 - - Comment - Flycam axis 5 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - FlycamAxisScale6 - - Comment - Flycam axis 6 scaler. - Persist - 1 - Type - F32 - Value - 1.0 - - FlycamBuildModeScale - - Comment - Scale factor to apply to flycam movements when in build mode. - Persist - 1 - Type - F32 - Value - 1.0 - - FlycamFeathering - - Comment - Flycam feathering (less is softer) - Persist - 1 - Type - F32 - Value - 16.0 - - FlycamZoomDirect - - Comment - Map flycam zoom axis directly to camera zoom. - Persist - 1 - Type - Boolean - Value - 0 - - FlyingAtExit - - Comment - Was flying when last logged out, so fly when logging in - Persist - 1 - Type - Boolean - Value - 0 - - FocusOffsetRearView - - Comment - Initial focus point offset relative to avatar for the camera preset Rear View (x-axis is forward) - Persist - 1 - Type - Vector3D - Value - - 1.0 - 0.0 - 1.0 - - - FocusOffsetFrontView - - Comment - Initial focus point offset relative to avatar for the camera preset Front View - Persist - 1 - Type - Vector3D - Value - - 0.0 - 0.0 - 0.0 - - - FocusOffsetGroupView - - Comment - Initial focus point offset relative to avatar for the camera preset Group View - Persist - 1 - Type - Vector3D - Value - - 1.5 - 0.7 - 1.0 - - - FocusPosOnLogout - - Comment - Camera focus point when last logged out (global coordinates) - Persist - 1 - Type - Vector3D - Value - - 0.0 - 0.0 - 0.0 - - - FolderAutoOpenDelay - - Comment - Seconds before automatically expanding the folder under the mouse when performing inventory drag and drop - Persist - 1 - Type - F32 - Value - 0.75 - - FolderLoadingMessageWaitTime - - Comment - Seconds to wait before showing the LOADING... text in folder views - Persist - 1 - Type - F32 - Value - 0.5 - - FontScreenDPI - - Comment - Font resolution, higher is bigger (pixels per inch) - Persist - 1 - Type - F32 - Value - 96.0 - - ForceAssetFail - - Comment - Force wearable fetches to fail for this asset type. - Persist - 1 - Type - U32 - Value - 255 - - ForceShowGrid - - Comment - Always show grid dropdown on login screen - Persist - 1 - Type - Boolean - Value - 0 - - ForceMandatoryUpdate - - Comment - For QA: On next startup, forces the auto-updater to run - Persist - 1 - Type - Boolean - Value - 0 - - FreezeTime - - Comment - - Persist - 0 - Type - Boolean - Value - 0 - - FullScreenAspectRatio - - Comment - Aspect ratio of fullscreen display (width / height) - Persist - 1 - Type - F32 - Value - 3 - - FullScreenAutoDetectAspectRatio - - Comment - Automatically detect proper aspect ratio for fullscreen display - Persist - 1 - Type - Boolean - Value - 0 - - GesturesMarketplaceURL - - Comment - URL to the Gestures Marketplace - Persist - 0 - Type - String - Value - https://www.xstreetsl.com/modules.php?name=Marketplace&CategoryID=233 - - GridCrossSections - - Comment - Highlight cross sections of prims with grid manipulation plane. - Persist - 1 - Type - Boolean - Value - 0 - - GridDrawSize - - Comment - Visible extent of 2D snap grid (meters) - Persist - 1 - Type - F32 - Value - 12.0 - - GridMode - - Comment - Snap grid reference frame (0 = world, 1 = local, 2 = reference object) - Persist - 1 - Type - S32 - Value - 0 - - GridOpacity - - Comment - Grid line opacity (0.0 = completely transparent, 1.0 = completely opaque) - Persist - 1 - Type - F32 - Value - 0.699999988079 - - GridResolution - - Comment - Size of single grid step (meters) - Persist - 1 - Type - F32 - Value - 0.5 - - GridSubUnit - - Comment - Display fractional grid steps, relative to grid size - Persist - 1 - Type - Boolean - Value - 0 - - GridSubdivision - - Comment - Maximum number of times to divide single snap grid unit when GridSubUnit is true - Persist - 1 - Type - S32 - Value - 32 - - GroupNotifyBoxHeight - - Comment - Height of group notice messages - Persist - 1 - Type - S32 - Value - 260 - - GroupNotifyBoxWidth - - Comment - Width of group notice messages - Persist - 1 - Type - S32 - Value - 305 - - HelpUseLocal - - Comment - If set, always use this for help: skins/default/html/[LANGUAGE]/help-offline/index.html - Persist - 0 - Type - Boolean - Value - 0 - - HelpURLFormat - - Comment - URL pattern for help page; arguments will be encoded; see llviewerhelp.cpp:buildHelpURL for arguments - Persist - 1 - Type - String - Value - http://viewer-help.secondlife.com/[LANGUAGE]/[CHANNEL]/[VERSION]/[TOPIC][DEBUG_MODE] - - HomeSidePanelURL - - Comment - URL for the web page to display in the Home side panel - Persist - 1 - Type - String - Value - https://viewer-sidebar.secondlife.com/sidebar.html?p=[AUTH_TOKEN]&lang=[LANGUAGE]&channel=[CHANNEL]&version=[VERSION]&major=[VERSION_MAJOR]&minor=[VERSION_MINOR]&patch=[VERSION_PATCH]&build=[VERSION_BUILD]&firstlogin=[FIRST_LOGIN] - - SearchURL - - Comment - URL for Search website, displayed in the Find floater - Persist - 0 - Type - String - Value - http://search.secondlife.com/viewer/[CATEGORY]/?q=[QUERY]&p=[AUTH_TOKEN]&r=[MATURITY]&lang=[LANGUAGE]&g=[GODLIKE]&sid=[SESSION_ID]&rid=[REGION_ID]&pid=[PARCEL_ID]&channel=[CHANNEL]&version=[VERSION]&major=[VERSION_MAJOR]&minor=[VERSION_MINOR]&patch=[VERSION_PATCH]&build=[VERSION_BUILD] - - WebProfileURL - - Comment - URL for Web Profiles - Persist - 0 - Type - String - Value - https://my.secondlife.com/[AGENT_NAME] - - WebProfileNonProductionURL - - Comment - URL for Web Profiles on Non-Production grids - Persist - 0 - Type - String - Value - https://my-demo.secondlife.com/[AGENT_NAME] - - HighResSnapshot - - Comment - Double resolution of snapshot from current window resolution - Persist - 1 - Type - Boolean - Value - 0 - - HideSelectedObjects - - Comment - Hide Selected Objects - Persist - 1 - Type - Boolean - Value - 0 - - HostID - - Comment - Machine identifier for hosted Second Life instances - Persist - 0 - Type - String - Value - - - HtmlHelpLastPage - - Comment - Last URL visited via help system - Persist - 1 - Type - String - Value - - - IMShowTimestamps - - Comment - Show timestamps in IM - Persist - 1 - Type - Boolean - Value - 1 - - IMShowControlPanel - - Comment - Show IM Control Panel - Persist - 1 - Type - Boolean - Value - 1 - - IgnoreAllNotifications - - Comment - Ignore all notifications so we never need user input on them. - Persist - 1 - Type - Boolean - Value - 0 - - IgnorePixelDepth - - Comment - Ignore pixel depth settings. - Persist - 1 - Type - Boolean - Value - 0 - - ImagePipelineUseHTTP - - Comment - If TRUE use HTTP GET to fetch textures from the server - Persist - 1 - Type - Boolean - Value - 1 - - InactiveFloaterTransparency - - Comment - Transparency of inactive floaters (floaters that have no focus) - Persist - 1 - Type - F32 - Value - 0.65 - - InBandwidth - - Comment - Incoming bandwidth throttle (bps) - Persist - 1 - Type - F32 - Value - 0.0 - - InspectorFadeTime - - Comment - Fade out timing for inspectors - Persist - 1 - Type - F32 - Value - 0.5 - - InspectorShowTime - - Comment - Stay timing for inspectors - Persist - 1 - Type - F32 - Value - 3.0 - - InstallLanguage - - Comment - Language passed from installer (for UI) - Persist - 1 - Type - String - Value - default - - InventoryAutoOpenDelay - - Comment - Seconds before automatically opening inventory when mouse is over inventory button when performing inventory drag and drop - Persist - 1 - Type - F32 - Value - 1.0 - - InventoryLinking - - Comment - Enable ability to create links to folders and items via "Paste as link". - Persist - 1 - Type - Boolean - Value - 0 - - InventorySortOrder - - Comment - Specifies sort key for inventory items (+0 = name, +1 = date, +2 = folders always by name, +4 = system folders to top) - Persist - 1 - Type - U32 - Value - 7 - - InvertMouse - - Comment - When in mouselook, moving mouse up looks down and vice verse (FALSE = moving up looks up) - Persist - 1 - Type - Boolean - Value - 0 - - JoystickAvatarEnabled - - Comment - Enables the Joystick to control Avatar movement. - Persist - 1 - Type - Boolean - Value - 1 - - JoystickAxis0 - - Comment - Flycam hardware axis mapping for internal axis 0 ([0, 5]). - Persist - 1 - Type - S32 - Value - 1 - - JoystickAxis1 - - Comment - Flycam hardware axis mapping for internal axis 1 ([0, 5]). - Persist - 1 - Type - S32 - Value - 0 - - JoystickAxis2 - - Comment - Flycam hardware axis mapping for internal axis 2 ([0, 5]). - Persist - 1 - Type - S32 - Value - 2 - - JoystickAxis3 - - Comment - Flycam hardware axis mapping for internal axis 3 ([0, 5]). - Persist - 1 - Type - S32 - Value - 4 - - JoystickAxis4 - - Comment - Flycam hardware axis mapping for internal axis 4 ([0, 5]). - Persist - 1 - Type - S32 - Value - 3 - - JoystickAxis5 - - Comment - Flycam hardware axis mapping for internal axis 5 ([0, 5]). - Persist - 1 - Type - S32 - Value - 5 - - JoystickAxis6 - - Comment - Flycam hardware axis mapping for internal axis 6 ([0, 5]). - Persist - 1 - Type - S32 - Value - -1 - - JoystickBuildEnabled - - Comment - Enables the Joystick to move edited objects. - Persist - 1 - Type - Boolean - Value - 0 - - JoystickEnabled - - Comment - Enables Joystick Input. - Persist - 1 - Type - Boolean - Value - 0 - - JoystickFlycamEnabled - - Comment - Enables the Joystick to control the flycam. - Persist - 0 - Type - Boolean - Value - 1 - - JoystickInitialized - - Comment - Whether or not a joystick has been detected and initiailized. - Persist - 1 - Type - String - Value - - - JoystickMouselookYaw - - Comment - Pass joystick yaw to scripts in Mouselook. - Persist - 1 - Type - Boolean - Value - 1 - - JoystickRunThreshold - - Comment - Input threshold to initiate running - Persist - 1 - Type - F32 - Value - 0.25 - - KeepAspectForSnapshot - - Comment - Use full window when taking snapshot, regardless of requested image size - Persist - 1 - Type - Boolean - Value - 1 - - LandBrushSize - - Comment - Size of affected region when using teraform tool - Persist - 1 - Type - F32 - Value - 2.0 - - LCDDestination - - Comment - Which LCD to use - Persist - 1 - Type - S32 - Value - 0 - - LSLFindCaseInsensitivity - - Comment - Use case insensitivity when searching in LSL editor - Persist - 1 - Type - Boolean - Value - 0 - - LSLHelpURL - - Comment - URL that points to LSL help files, with [LSL_STRING] corresponding to the referenced LSL function or keyword - Persist - 1 - Type - String - Value - http://wiki.secondlife.com/wiki/[LSL_STRING] - - LagMeterShrunk - - Comment - Last large/small state for lag meter - Persist - 1 - Type - Boolean - Value - 0 - - Language - - Comment - Language specifier (for UI) - Persist - 1 - Type - String - Value - default - - LanguageIsPublic - - Comment - Let other residents see our language information - Persist - 1 - Type - Boolean - Value - 1 - - LastGPUClass - - Comment - [DO NOT MODIFY] previous GPU class for tracking hardware changes - Persist - 1 - Type - S32 - Value - -1 - - LastFeatureVersion - - Comment - [DO NOT MODIFY] Version number for tracking hardware changes - Persist - 1 - Type - S32 - Value - 0 - - LastFindPanel - - Comment - Controls which find operation appears by default when clicking "Find" button - Persist - 1 - Type - String - Value - find_all_panel - - LastName - - Comment - Login last name - Persist - 1 - Type - String - Value - - - LastPrefTab - - Comment - Last selected tab in preferences window - Persist - 1 - Type - S32 - Value - 0 - - LastMediaSettingsTab - - Comment - Last selected tab in media settings window - Persist - 1 - Type - S32 - Value - 0 - - LastRunVersion - - Comment - Version number of last instance of the viewer that you ran - Persist - 1 - Type - String - Value - 0.0.0 - - - LastSnapshotToEmailHeight - - Comment - The height of the last email snapshot, in px - Persist - 1 - Type - S32 - Value - 768 - - LastSnapshotToEmailWidth - - Comment - The width of the last email snapshot, in px - Persist - 1 - Type - S32 - Value - 1024 - - LastSnapshotToDiskHeight - - Comment - The height of the last disk snapshot, in px - Persist - 1 - Type - S32 - Value - 768 - - LastSnapshotToDiskWidth - - Comment - The width of the last disk snapshot, in px - Persist - 1 - Type - S32 - Value - 1024 - - LastSnapshotToInventoryHeight - - Comment - The height of the last texture snapshot, in px - Persist - 1 - Type - S32 - Value - 512 - - LastSnapshotToInventoryWidth - - Comment - The width of the last texture snapshot, in px - Persist - 1 - Type - S32 - Value - 512 - - LastSnapshotType - - Comment - Select this as next type of snapshot to take (0 = postcard, 1 = texture, 2 = local image) - Persist - 1 - Type - S32 - Value - 0 - - LeftClickShowMenu - - Comment - Left click opens pie menu (FALSE = left click touches or grabs object) - Persist - 1 - Type - Boolean - Value - 0 - - LimitDragDistance - - Comment - Limit translation of object via translate tool - Persist - 1 - Type - Boolean - Value - 1 - - LimitSelectDistance - - Comment - Disallow selection of objects beyond max select distance - Persist - 1 - Type - Boolean - Value - 1 - - LipSyncAah - - Comment - Aah (jaw opening) babble loop - Persist - 1 - Type - String - Value - 257998776531013446642343 - - LipSyncAahPowerTransfer - - Comment - Transfer curve for Voice Interface power to aah lip sync amplitude - Persist - 1 - Type - String - Value - 0000123456789 - - LipSyncEnabled - - Comment - 0 disable lip-sync, 1 enable babble loop - Persist - 1 - Type - Boolean - Value - 1 - - LipSyncOoh - - Comment - Ooh (mouth width) babble loop - Persist - 1 - Type - String - Value - 1247898743223344444443200000 - - LipSyncOohAahRate - - Comment - Rate to babble Ooh and Aah (/sec) - Persist - 1 - Type - F32 - Value - 24.0 - - LipSyncOohPowerTransfer - - Comment - Transfer curve for Voice Interface power to ooh lip sync amplitude - Persist - 1 - Type - String - Value - 0012345566778899 - - LocalCacheVersion - - Comment - Version number of cache - Persist - 1 - Type - S32 - Value - 0 - - LocalFileSystemBrowsingEnabled - - Comment - Enable/disable access to the local file system via the file picker - Persist - 1 - Type - Boolean - Value - 1 - - LoginSRVTimeout - - Comment - Duration in seconds of the login SRV request timeout - Persist - 0 - Type - F32 - Value - 10.0 - - LoginSRVPump - - Comment - Name of the message pump that handles SRV request - Persist - 0 - Type - String - Value - LLAres - - LogMessages - - Comment - Log network traffic - Persist - 1 - Type - Boolean - Value - 0 - - LogTextureNetworkTraffic - - Comment - Log network traffic for textures - Persist - 1 - Type - Boolean - Value - 0 - - LoginAsGod - - Comment - Attempt to login with god powers (Linden accounts only) - Persist - 1 - Type - Boolean - Value - 0 - - LoginLocation - - Comment - Login location ('last', 'home') - Persist - 1 - Type - String - Value - last - - LoginPage - - Comment - Login authentication page. - Persist - 1 - Type - String - Value - - - LosslessJ2CUpload - - Comment - Use lossless compression for small image uploads - Persist - 1 - Type - Boolean - Value - 0 - - MainloopTimeoutDefault - - Comment - Timeout duration for mainloop lock detection, in seconds. - Persist - 1 - Type - F32 - Value - 20.0 - - MapOverlayIndex - - Comment - Currently selected world map type - Persist - 1 - Type - S32 - Value - 0 - - MapScale - - Comment - World map zoom level (pixels per region) - Persist - 1 - Type - F32 - Value - 128.0 - - MapServerURL - - Comment - World map URL template for locating map tiles - Persist - 0 - Type - String - Value - http://map.secondlife.com.s3.amazonaws.com/ - - CurrentMapServerURL - - Comment - Current Session World map URL - Persist - 0 - Type - String - Value - - - MapShowEvents - - Comment - Show events on world map - Persist - 1 - Type - Boolean - Value - 1 - - MapShowInfohubs - - Comment - Show infohubs on the world map - Persist - 1 - Type - Boolean - Value - 1 - - MapShowLandForSale - - Comment - Show land for sale on world map - Persist - 1 - Type - Boolean - Value - 0 - - MapShowPeople - - Comment - Show other users on world map - Persist - 1 - Type - Boolean - Value - 1 - - MapShowTelehubs - - Comment - Show telehubs on world map - Persist - 1 - Type - Boolean - Value - 1 - - MiniMapAutoCenter - - Comment - Center the focal point of the minimap. - Persist - 0 - Type - Boolean - Value - 1 - - Marker - - Comment - [NOT USED] - Persist - 1 - Type - String - Value - - - MarketplaceURL - - Comment - URL to the Marketplace - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/ - - MarketplaceURL_objectFemale - - Comment - URL to the Marketplace Attachments Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/attachments - - MarketplaceURL_objectMale - - Comment - URL to the Marketplace Attachments Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/attachments - - MarketplaceURL_clothingFemale - - Comment - URL to the Marketplace Clothing Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/clothing_female_avatar - - MarketplaceURL_clothingMale - - Comment - URL to the Marketplace Clothing Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/clothing_male_avatar - - MarketplaceURL_bodypartFemale - - Comment - URL to the Marketplace Bodyparts Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com - - MarketplaceURL_bodypartMale - - Comment - URL to the Marketplace Bodyparts Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/ - - MarketplaceURL_glovesMale - - Comment - URL to the Marketplace Gloves Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/gloves_both_women_and_men - - MarketplaceURL_glovesFemale - - Comment - URL to the Marketplace Gloves Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/gloves_both_women_and_men - - MarketplaceURL_jacketFemale - - Comment - URL to the Marketplace Jacket Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/jacket_womens - - MarketplaceURL_jacketMale - - Comment - URL to the Marketplace Jacket Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/jacket_mens - - MarketplaceURL_shirtFemale - - Comment - URL to the Marketplace Shirt Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/shirt_womens - - MarketplaceURL_shirtMale - - Comment - URL to the Marketplace Shirt Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/shirt_mens - - MarketplaceURL_undershirtFemale - - Comment - URL to the Marketplace Undershirt Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/undershirt_womens - - MarketplaceURL_undershirtMale - - Comment - URL to the Marketplace Undershirt Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/undershirt_mens - - MarketplaceURL_skirtFemale - - Comment - URL to the Marketplace Skirt Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/skirts_women - - MarketplaceURL_skirtMale - - Comment - URL to the Marketplace Skirt Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/skirts_women - - MarketplaceURL_pantsFemale - - Comment - URL to the Marketplace Pants Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/pants_women - - MarketplaceURL_pantsMale - - Comment - URL to the Marketplace Pants Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/pants_men - - MarketplaceURL_underpantsFemale - - Comment - URL to the Marketplace Underwear Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/underwear_women - - MarketplaceURL_underpantsMale - - Comment - URL to the Marketplace Underwear Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/underwear_men - - MarketplaceURL_shoesFemale - - Comment - URL to the Marketplace Shoes Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/shoes_women - - MarketplaceURL_shoesMale - - Comment - URL to the Marketplace Shoes Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/shoes_men - - MarketplaceURL_socksFemale - - Comment - URL to the Marketplace Socks Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/socks_women - - MarketplaceURL_socksMale - - Comment - URL to the Marketplace Socks Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/socks_women - - MarketplaceURL_tattooMale - - Comment - URL to the Marketplace Tattoo Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/tattoo_both_women_and_men - - MarketplaceURL_tattooFemale - - Comment - URL to the Marketplace Tattoo Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/tattoo_both_women_and_men - - MarketplaceURL_hairFemale - - Comment - URL to the Marketplace Hair Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/womens_hair - - MarketplaceURL_hairMale - - Comment - URL to the Marketplace Hair Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/mens_hair - - MarketplaceURL_eyesFemale - - Comment - URL to the Marketplace Eyes Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/womens_eyes - - MarketplaceURL_eyesMale - - Comment - URL to the Marketplace Eyes Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/mens_eyes - - MarketplaceURL_shapeFemale - - Comment - URL to the Marketplace Shape Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/womens_shape - - MarketplaceURL_shapeMale - - Comment - URL to the Marketplace Shape Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/mens_shape - - MarketplaceURL_skinFemale - - Comment - URL to the Marketplace Skin Female - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/womens_skin - - MarketplaceURL_skinMale - - Comment - URL to the Marketplace Skins Male - Persist - 0 - Type - String - Value - http://marketplace.secondlife.com/trampoline/viewer21/mens_skin - - MaxDragDistance - - Comment - Maximum allowed translation distance in a single operation of translate tool (meters from start point) - Persist - 1 - Type - F32 - Value - 48.0 - - MaxSelectDistance - - Comment - Maximum allowed selection distance (meters from avatar) - Persist - 1 - Type - F32 - Value - 64.0 - - MaxWearableWaitTime - - Comment - Max seconds to wait for wearable assets to fetch. - Persist - 1 - Type - F32 - Value - 60.0 - - MediaControlFadeTime - - Comment - Amount of time (in seconds) that the media control fades - Persist - 1 - Type - F32 - Value - 1.5 - - MediaControlTimeout - - Comment - Amount of time (in seconds) for media controls to fade with no mouse activity - Persist - 1 - Type - F32 - Value - 3.0 - - MediaEnablePopups - - Comment - If true, enable targeted links and javascript in media to open new media browser windows without a prompt. - Persist - 1 - Type - Boolean - Value - 0 - - MediaOnAPrimUI - - Comment - Whether or not to show the "link sharing" UI - Persist - 1 - Type - Boolean - Value - 1 - - MediaPerformanceManagerDebug - - Comment - Whether to show debug data for the media performance manager in the nearby media list. - Persist - 1 - Type - Boolean - Value - 0 - - MediaShowOnOthers - - Comment - Whether or not to show media on other avatars - Persist - 1 - Type - Boolean - Value - 0 - - MediaShowOutsideParcel - - Comment - Whether or not to show media from outside the current parcel - Persist - 1 - Type - Boolean - Value - 1 - - MediaShowWithinParcel - - Comment - Whether or not to show media within the current parcel - Persist - 1 - Type - Boolean - Value - 1 - - MediaTentativeAutoPlay - - Comment - This is a tentative flag that may be temporarily set off by the user, until she teleports - Persist - 0 - Type - Boolean - Value - 1 - - MemoryLogFrequency - - Comment - Seconds between display of Memory in log (0 for never) - Persist - 1 - Type - F32 - Value - 600.0 - - MemProfiling - - Comment - You want to use tcmalloc's memory profiling options. - Persist - 1 - Type - Boolean - Value - 0 - - MenuAccessKeyTime - - Comment - Time (seconds) in which the menu key must be tapped to move focus to the menu bar - Persist - 1 - Type - F32 - Value - 0.25 - - MenuBarHeight - - Comment - - Persist - 0 - Type - S32 - Value - 18 - - MenuBarWidth - - Comment - - Persist - 0 - Type - S32 - Value - 410 - - MePanelOpened - - Comment - Indicates that Me Panel was opened at least once after Viewer was installed - Persist - 1 - Type - Boolean - Value - 0 - - MigrateCacheDirectory - - Comment - Check for old version of disk cache to migrate to current location - Persist - 1 - Type - Boolean - Value - 1 - - MiniMapPrimMaxRadius - - Comment - Radius of the largest prim to show on the MiniMap. Increasing beyond 256 may cause client lag. - Persist - 1 - Type - F32 - Value - 256.0 - - MiniMapRotate - - Comment - Rotate miniature world map to avatar direction - Persist - 1 - Type - Boolean - Value - 1 - - MiniMapScale - - Comment - Miniature world map zoom level (pixels per region) - Persist - 1 - Type - F32 - Value - 128.0 - - MouseSensitivity - - Comment - Controls responsiveness of mouse when in mouselook mode (fraction or multiple of default mouse sensitivity) - Persist - 1 - Type - F32 - Value - 3.0 - - MouseSmooth - - Comment - Smooths out motion of mouse when in mouselook mode. - Persist - 1 - Type - Boolean - Value - 0 - - MouseSun - - Comment - - Persist - 0 - Type - Boolean - Value - 0 - - MuteAmbient - - Comment - Ambient sound effects, such as wind noise, play at 0 volume - Persist - 1 - Type - Boolean - Value - 0 - - MuteAudio - - Comment - All audio plays at 0 volume (streaming audio still takes up bandwidth, for example) - Persist - 1 - Type - Boolean - Value - 0 - - MuteMedia - - Comment - Media plays at 0 volume (streaming audio still takes up bandwidth) - Persist - 1 - Type - Boolean - Value - 0 - - MuteMusic - - Comment - Music plays at 0 volume (streaming audio still takes up bandwidth) - Persist - 1 - Type - Boolean - Value - 0 - - MuteSounds - - Comment - Sound effects play at 0 volume - Persist - 1 - Type - Boolean - Value - 0 - - MuteUI - - Comment - UI sound effects play at 0 volume - Persist - 1 - Type - Boolean - Value - 0 - - MuteVoice - - Comment - Voice plays at 0 volume (streaming audio still takes up bandwidth) - Persist - 1 - Type - Boolean - Value - 0 - - MuteWhenMinimized - - Comment - Mute audio when SL window is minimized - Persist - 1 - Type - Boolean - Value - 0 - - MyOutfitsAutofill - - Comment - Always autofill My Outfits from library when empty (else happens just once). - Persist - 1 - Type - Boolean - Value - 0 - - NearMeRange - - Comment - Search radius for nearby avatars - Persist - 1 - Type - F32 - Value - 130 - - NextOwnerCopy - - Comment - Newly created objects can be copied by next owner - Persist - 1 - Type - Boolean - Value - 0 - - NextOwnerModify - - Comment - Newly created objects can be modified by next owner - Persist - 1 - Type - Boolean - Value - 0 - - NextOwnerTransfer - - Comment - Newly created objects can be resold or given away by next owner - Persist - 1 - Type - Boolean - Value - 1 - - NewCacheLocation - - Comment - Change the location of the local disk cache to this - Persist - 1 - Type - String - Value - - - NewCacheLocationTopFolder - - Comment - Change the top folder location of the local disk cache to this - Persist - 1 - Type - String - Value - - - NextLoginLocation - - Comment - Location to log into by default. - Persist - 1 - Type - String - Value - - - NoAudio - - Comment - Disable audio playback. - Persist - 1 - Type - Boolean - Value - 0 - - NoHardwareProbe - - Comment - Disable hardware probe. - Persist - 1 - Type - Boolean - Value - 0 - - NoInventoryLibrary - - Comment - Do not request inventory library. - Persist - 1 - Type - Boolean - Value - 0 - - NoPreload - - Comment - Disable sound and image preload. - Persist - 1 - Type - Boolean - Value - 0 - - NoVerifySSLCert - - Comment - Do not verify SSL peers. - Persist - 1 - Type - Boolean - Value - 0 - - NotifyBoxHeight - - Comment - Height of notification messages - Persist - 1 - Type - S32 - Value - 200 - - NotifyBoxWidth - - Comment - Width of notification messages - Persist - 1 - Type - S32 - Value - 305 - - NotificationToastLifeTime - - Comment - Number of seconds while a notification toast exists - Persist - 1 - Type - S32 - Value - 5 - - NotificationTipToastLifeTime - - Comment - Number of seconds while a notification tip toast exist - Persist - 1 - Type - S32 - Value - 10 - - ToastFadingTime - - Comment - Number of seconds while a toast is fading - Persist - 1 - Type - S32 - Value - 1 - - NearbyToastFadingTime - - Comment - Number of seconds while a nearby chat toast is fading - Persist - 1 - Type - S32 - Value - 3 - - NearbyToastLifeTime - - Comment - Number of seconds while a nearby chat toast exists - Persist - 1 - Type - S32 - Value - 23 - - StartUpToastLifeTime - - Comment - Number of seconds while a StartUp toast exist - Persist - 1 - Type - S32 - Value - 5 - - ToastGap - - Comment - Gap between toasts on a screen (min. value is 5) - Persist - 1 - Type - S32 - Value - 7 - - ToastButtonWidth - - Comment - Default width of buttons in the toast. - Notes: - If required width will be less then this one, a button will be reshaped to default size , otherwise to required - Change of this parameter will affect the layout of buttons in notification toast. - Persist - 1 - Type - S32 - Value - 90 - - ChannelBottomPanelMargin - - Comment - Space from a lower toast to the Bottom Tray - Persist - 1 - Type - S32 - Value - 35 - - NotificationChannelRightMargin - - Comment - Space between toasts and a right border of an area where they can appear - Persist - 1 - Type - S32 - Value - 5 - - NotificationChannelHeightRatio - - Comment - Notification channel and World View ratio(0.0 - always show 1 notification, 1.0 - max ratio). - Persist - 1 - Type - F32 - Value - 0.5 - - OverflowToastHeight - - Comment - Height of an overflow toast - Persist - 1 - Type - S32 - Value - 72 - - NotifyMoneyChange - - Comment - Pop up notifications for all L$ transactions - Persist - 1 - Type - Boolean - Value - 1 - - NotifyTipDuration - - Comment - Length of time that notification tips stay on screen (seconds) - Persist - 1 - Type - F32 - Value - 4.0 - - NumSessions - - Comment - Number of successful logins to Second Life - Persist - 1 - Type - S32 - Value - 0 - - NumpadControl - - Comment - How numpad keys control your avatar. 0 = Like the normal arrow keys, 1 = Numpad moves avatar when numlock is off, 2 = Numpad moves avatar regardless of numlock (use this if you have no numlock) - Persist - 1 - Type - S32 - Value - 0 - - ObjectCacheEnabled - - Comment - Enable the object cache. - Persist - 1 - Type - Boolean - Value - 1 - - OpenDebugStatAdvanced - - Comment - Expand advanced performance stats display - Persist - 1 - Type - Boolean - Value - 0 - - OpenDebugStatBasic - - Comment - Expand basic performance stats display - Persist - 1 - Type - Boolean - Value - 1 - - OpenDebugStatNet - - Comment - Expand network stats display - Persist - 1 - Type - Boolean - Value - 1 - - OpenDebugStatRender - - Comment - Expand render stats display - Persist - 1 - Type - Boolean - Value - 1 - - OpenDebugStatSim - - Comment - Expand simulator performance stats display - Persist - 1 - Type - Boolean - Value - 1 - - OpenDebugStatTexture - - Comment - Expand Texture performance stats display - Persist - 1 - Type - Boolean - Value - 0 - - OpenDebugStatPhysicsDetails - - Comment - Expand Physics Details performance stats display - Persist - 1 - Type - Boolean - Value - 0 - - OpenDebugStatSimTime - - Comment - Expand Simulator Time performance stats display - Persist - 1 - Type - Boolean - Value - 0 - - OpenDebugStatSimTimeDetails - - Comment - Expand Simulator Time Details performance stats display - Persist - 1 - Type - Boolean - Value - 0 - - OutBandwidth - - Comment - Outgoing bandwidth throttle (bps) - Persist - 1 - Type - F32 - Value - 0.0 - - OverlayTitle - - Comment - Controls watermark text message displayed on screen when "ShowOverlayTitle" is enabled (one word, underscores become spaces) - Persist - 1 - Type - String - Value - Set_via_OverlayTitle_in_settings.xml - - PTTCurrentlyEnabled - - Comment - Use Push to Talk mode - Persist - 0 - Type - Boolean - Value - 1 - - PacketDropPercentage - - Comment - Percentage of packets dropped by the client. - Persist - 1 - Type - F32 - Value - 0.0 - - ParcelMediaAutoPlayEnable - - Comment - Auto play parcel media when available - Persist - 1 - Type - Boolean - Value - 1 - - ParticipantListShowIcons - - Comment - Show/hide people icons in participant list - Persist - 1 - Type - Boolean - Value - 1 - - PerAccountSettingsFile - - Comment - Persisted client settings file name (per user). - Persist - 0 - Type - String - Value - - - PermissionsCautionEnabled - - Comment - When enabled, changes the handling of script permission requests to help avoid accidental granting of certain permissions, such as the debit permission - Persist - 0 - Type - Boolean - Value - 1 - - PermissionsCautionNotifyBoxHeight - - Comment - Height of caution-style notification messages - Persist - 0 - Type - S32 - Value - 344 - - PickerContextOpacity - - Comment - Controls overall opacity of context frustrum connecting color and texture pickers with their swatches - Persist - 1 - Type - F32 - Value - 0.34999999404 - - PicksPerSecondMouseMoving - - Comment - How often to perform hover picks while the mouse is moving (picks per second) - Persist - 1 - Type - F32 - Value - 5.0 - - PicksPerSecondMouseStationary - - Comment - How often to perform hover picks while the mouse is stationary (picks per second) - Persist - 1 - Type - F32 - Value - 0.0 - - PieMenuLineWidth - - Comment - Width of lines in pie menu display (pixels) - Persist - 1 - Type - F32 - Value - 2.5 - - PingInterpolate - - Comment - Extrapolate object position along velocity vector based on ping delay - Persist - 1 - Type - Boolean - Value - 0 - - PitchFromMousePosition - - Comment - Vertical range over which avatar head tracks mouse position (degrees of head rotation from top of window to bottom) - Persist - 1 - Type - F32 - Value - 90.0 - - PlayTypingAnim - - Comment - Your avatar plays the typing animation whenever you type in the chat bar - Persist - 1 - Type - Boolean - Value - 1 - - PluginAttachDebuggerToPlugins - - Comment - If true, attach a debugger session to each plugin process as it's launched. - Persist - 1 - Type - Boolean - Value - 0 - - PluginInstancesCPULimit - - Comment - Amount of total plugin CPU usage before inworld plugins start getting turned down to "slideshow" priority. Set to 0 to disable this check. - Persist - 1 - Type - F32 - Value - 0.9 - - - PlainTextChatHistory - - Comment - Enable/Disable plain text chat history style - Persist - 1 - Type - Boolean - Value - 0 - - - PluginInstancesLow - - Comment - Limit on the number of inworld media plugins that will run at "low" priority - Persist - 1 - Type - U32 - Value - 4 - - PluginInstancesNormal - - Comment - Limit on the number of inworld media plugins that will run at "normal" or higher priority - Persist - 1 - Type - U32 - Value - 2 - - PluginInstancesTotal - - Comment - Hard limit on the number of plugins that will be instantiated at once for inworld media - Persist - 1 - Type - U32 - Value - 8 - - - PluginUseReadThread - - Comment - Use a separate thread to read incoming messages from plugins - Persist - 1 - Type - Boolean - Value - 0 - - - PrecachingDelay - - Comment - Delay when logging in to load world before showing it (seconds) - Persist - 1 - Type - F32 - Value - 6.0 - - PreferredMaturity - - Comment - Setting for the user's preferred maturity level (consts in indra_constants.h) - Persist - 1 - Type - U32 - Value - 13 - - PrimMediaMasterEnabled - - Comment - Whether or not Media on a Prim is enabled. - Persist - 1 - Type - Boolean - Value - 1 - - PrimMediaControlsUseHoverControlSet - - Comment - Whether or not hovering over prim media uses minimal "hover" controls or the authored control set. - Persist - 1 - Type - Boolean - Value - 0 - - PrimMediaDragNDrop - - Comment - Enable drag and drop of URLs onto prim faces - Persist - 1 - Type - Boolean - Value - 1 - - PrimMediaMaxRetries - - Comment - Maximum number of retries for media queries. - Persist - 1 - Type - U32 - Value - 4 - - PrimMediaRequestQueueDelay - - Comment - Timer delay for fetching media from the queue (in seconds). - Persist - 1 - Type - F32 - Value - 1.0 - - PrimMediaRetryTimerDelay - - Comment - Timer delay for retrying on media queries (in seconds). - Persist - 1 - Type - F32 - Value - 5.0 - - PrimMediaMaxSortedQueueSize - - Comment - Maximum number of objects the viewer will load media for initially - Persist - 1 - Type - U32 - Value - 100000 - - PrimMediaMaxRoundRobinQueueSize - - Comment - Maximum number of objects the viewer will continuously update media for - Persist - 1 - Type - U32 - Value - 100000 - - ProbeHardwareOnStartup - - Comment - Query current hardware configuration on application startup - Persist - 1 - Type - Boolean - Value - 1 - - PurgeCacheOnNextStartup - - Comment - Clear local file cache next time viewer is run - Persist - 1 - Type - Boolean - Value - 0 - - PurgeCacheOnStartup - - Comment - Clear local file cache every time viewer is run - Persist - 1 - Type - Boolean - Value - 0 - - PushToTalkButton - - Comment - Which button or keyboard key is used for push-to-talk - Persist - 1 - Type - String - Value - MiddleMouse - - PushToTalkToggle - - Comment - Should the push-to-talk button behave as a toggle - Persist - 1 - Type - Boolean - Value - 1 - - QAMode - - Comment - Enable Testing Features. - Persist - 1 - Type - Boolean - Value - 0 - - QAModeEventHostPort - - Comment - Port on which lleventhost should listen - Persist - 0 - Type - S32 - Value - -1 - - QAModeTermCode - - Comment - On LL_ERRS, terminate with this code instead of OS message box - Persist - 0 - Type - S32 - Value - -1 - - QuietSnapshotsToDisk - - Comment - Take snapshots to disk without playing animation or sound - Persist - 1 - Type - Boolean - Value - 0 - - QuitAfterSeconds - - Comment - The duration allowed before quitting. - Persist - 1 - Type - F32 - Value - 0.0 - - QuitAfterSecondsOfAFK - - Comment - The duration allowed after being AFK before quitting. - Persist - 1 - Type - F32 - Value - 0.0 - - QuitOnLoginActivated - - Comment - Quit if login page is activated (used when auto login is on and users must not be able to login manually) - Persist - 1 - Type - Boolean - Value - 0 - - RadioLandBrushAction - - Comment - Last selected land modification operation (0 = flatten, 1 = raise, 2 = lower, 3 = smooth, 4 = roughen, 5 = revert) - Persist - 1 - Type - S32 - Value - 0 - - RadioLandBrushSize - - Comment - Size of land modification brush (0 = small, 1 = medium, 2 = large) - Persist - 1 - Type - S32 - Value - 0 - - LandBrushForce - - Comment - Multiplier for land modification brush force. - Persist - 1 - Type - F32 - Value - 1.0 - - MediaBrowserWindowLimit - - Comment - Maximum number of media brower windows that can be open at once in the media browser floater (0 for no limit) - Persist - 1 - Type - S32 - Value - 5 - - WebContentWindowLimit - - Comment - Maximum number of web brower windows that can be open at once in the Web content floater (0 for no limit) - Persist - 1 - Type - S32 - Value - 5 - - MediaRollOffRate - - Comment - Multiplier to change rate of media attenuation - Persist - 1 - Type - F32 - Value - 0.125 - - MediaRollOffMin - - Comment - Adjusts the distance at which media attentuation starts - Persist - 1 - Type - F32 - Value - 5.0 - - MediaRollOffMax - - Comment - Distance at which media volume is set to 0 - Persist - 1 - Type - F32 - Value - 30.0 - - RecentItemsSortOrder - - Comment - Specifies sort key for recent inventory items (+0 = name, +1 = date, +2 = folders always by name, +4 = system folders to top) - Persist - 1 - Type - U32 - Value - 1 - - RectangleSelectInclusive - - Comment - Select objects that have at least one vertex inside selection rectangle - Persist - 1 - Type - Boolean - Value - 1 - - RegInClient - - Comment - Experimental: Embed registration in login screen - Persist - 1 - Type - Boolean - Value - 0 - - QuickBuyCurrency - - Comment - Toggle between HTML based currency purchase floater and legacy XUI version - Persist - 1 - Type - Boolean - Value - 0 - - RegionTextureSize - - Comment - Terrain texture dimensions (power of 2) - Persist - 1 - Type - U32 - Value - 256 - - RememberPassword - - Comment - Keep password (in encrypted form) for next login - Persist - 1 - Type - Boolean - Value - 1 - - RenderAnisotropic - - Comment - Render textures using anisotropic filtering - Persist - 1 - Type - Boolean - Value - 0 - - RenderAppleUseMultGL - - Comment - Whether we want to use multi-threaded OpenGL on Apple hardware (requires restart of SL). - Persist - 1 - Type - Boolean - Value - 0 - - RenderAttachedLights - - Comment - Render lighted prims that are attached to avatars - Persist - 1 - Type - Boolean - Value - 1 - - RenderAttachedParticles - - Comment - Render particle systems that are attached to avatars - Persist - 1 - Type - Boolean - Value - 1 - - RenderAvatar - - Comment - Render Avatars - Persist - 0 - Type - Boolean - Value - 1 - - RenderAvatarCloth - - Comment - Controls if avatars use wavy cloth - Persist - 1 - Type - Boolean - Value - 1 - - RenderAvatarLODFactor - - Comment - Controls level of detail of avatars (multiplier for current screen area when calculated level of detail) - Persist - 1 - Type - F32 - Value - 0.5 - - RenderAvatarMaxVisible - - Comment - Maximum number of avatars to display at any one time - Persist - 1 - Type - S32 - Value - 12 - - RenderAvatarVP - - Comment - Use vertex programs to perform hardware skinning of avatar - Persist - 1 - Type - Boolean - Value - 1 - - - RenderShadowNearDist - - Comment - Near clip plane of shadow camera (affects precision of depth shadows). - Persist - 1 - Type - Vector3 - Value - - 256 - 256 - 256 - - - RenderShadowClipPlanes - - Comment - Near clip plane split distances for shadow map frusta. - Persist - 1 - Type - Vector3 - Value - - 1.0 - 12.0 - 32.0 - - - RenderShadowSplitExponent - - Comment - Near clip plane split distances for shadow map frusta (x=perspective, y=ortho, z=transition rate). - Persist - 1 - Type - Vector3 - Value - - 3.0 - 3.0 - 2.0 - - - RenderShadowOrthoClipPlanes - - Comment - Near clip plane split distances for orthographic shadow map frusta. - Persist - 1 - Type - Vector3 - Value - - 4.0 - 8.0 - 24.0 - - - RenderShadowProjOffset - - Comment - Amount to scale distance to virtual origin of shadow perspective projection. - Persist - 1 - Type - F32 - Value - 2.0 - - RenderShadowSlopeThreshold - - Comment - Cutoff slope value for points to affect perspective shadow generation - Persist - 1 - Type - F32 - Value - 0.0 - - RenderShadowProjExponent - - Comment - Exponent applied to transition between ortho and perspective shadow projections based on viewing angle and light vector. - Persist - 1 - Type - F32 - Value - 0.5 - - RenderSSAOScale - - Comment - Scaling factor for the area to sample for occluders (pixels at 1 meter away, inversely varying with distance) - Persist - 1 - Type - F32 - Value - 500.0 - - RenderSSAOMaxScale - - Comment - Maximum screen radius for sampling (pixels) - Persist - 1 - Type - U32 - Value - 200 - - RenderSSAOFactor - - Comment - Occlusion sensitivity factor for ambient occlusion (larger is more) - Persist - 1 - Type - F32 - Value - 0.30 - - RenderSSAOEffect - - Comment - Multiplier for (1) value and (2) saturation (HSV definition), for areas which are totally occluded. Blends with original color for partly-occluded areas. (Third component is unused.) - Persist - 1 - Type - Vector3 - Value - - 0.40 - 1.00 - 0.00 - - - RenderBumpmapMinDistanceSquared - - Comment - Maximum distance at which to render bumpmapped primitives (distance in meters, squared) - Persist - 1 - Type - F32 - Value - 100.0 - - RenderNormalMapScale - - Comment - Scaler applied to height map when generating normal maps - Persist - 1 - Type - F32 - Value - 64 - - RenderCubeMap - - Comment - Whether we can render the cube map or not - Persist - 1 - Type - Boolean - Value - 1 - - RenderDebugAlphaMask - - Comment - Test Alpha Masking Cutoffs. - Persist - 1 - Type - F32 - Value - 0.5 - - RenderDebugGL - - Comment - Enable strict GL debugging. - Persist - 1 - Type - Boolean - Value - 0 - - RenderDebugPipeline - - Comment - Enable strict pipeline debugging. - Persist - 1 - Type - Boolean - Value - 0 - - RenderDebugTextureBind - - Comment - Enable texture bind performance test. - Persist - 1 - Type - Boolean - Value - 0 - - RenderDelayCreation - - Comment - Throttle creation of drawables. - Persist - 1 - Type - Boolean - Value - 0 - - RenderAnimateRes - - Comment - Animate rezing prims. - Persist - 1 - Type - Boolean - Value - 0 - - - RenderAnimateTrees - - Comment - Use GL matrix ops to animate tree branches. - Persist - 1 - Type - Boolean - Value - 0 - - - RenderGIRange - - Comment - Distance to cut off GI effect. - Persist - 1 - Type - F32 - Value - 96 - - - RenderGILuminance - - Comment - Luminance factor of global illumination contribution. - Persist - 1 - Type - F32 - Value - 0.075 - - - RenderGIBrightness - - Comment - Brightness factor of global illumination contribution. - Persist - 1 - Type - F32 - Value - 0.3 - - - RenderGINoise - - Comment - Noise of position sampling for GI photon mapping. - Persist - 1 - Type - F32 - Value - 0.7 - - - RenderGIAttenuation - - Comment - Distance attenuation factor for indirect lighting. - Persist - 1 - Type - F32 - Value - 0.1 - - - RenderGIBlurBrightness - - Comment - Brightness factor of global illumination blur effect. - Persist - 1 - Type - F32 - Value - 1.025 - - - RenderGIBlurEdgeWeight - - Comment - Edge weight for GI soften filter (sharpness). - Persist - 1 - Type - F32 - Value - 0.8 - - - RenderGIBlurIncrement - - Comment - Increment of scale for each pass of global illumination blur effect. - Persist - 1 - Type - F32 - Value - 0.8 - - - RenderLuminanceScale - - Comment - Luminance value scalar for darkening effect. - Persist - 1 - Type - F32 - Value - 1.0 - - - RenderSunLuminanceScale - - Comment - Sun Luminance value scalar for darkening effect. - Persist - 1 - Type - F32 - Value - 1.0 - - - RenderSunLuminanceOffset - - Comment - Sun Luminance value offset for darkening effect. - Persist - 1 - Type - F32 - Value - 0 - - - RenderLuminanceDetail - - Comment - Mipmap level to use for luminance - Persist - 1 - Type - F32 - Value - 16.0 - - - RenderEdgeDepthCutoff - - Comment - Cutoff for depth difference that amounts to an edge. - Persist - 1 - Type - F32 - Value - 0.01 - - RenderEdgeNormCutoff - - Comment - Cutoff for normal difference that amounts to an edge. - Persist - 1 - Type - F32 - Value - 0.25 - - - RenderDeferredAlphaSoften - - Comment - Scalar for softening alpha surfaces (for soft particles). - Persist - 1 - Type - F32 - Value - 0.75 - - RenderDeferredNoise - - Comment - Noise scalar to hide banding in deferred render. - Persist - 1 - Type - F32 - Value - 4 - - RenderDeferredSpotShadowBias - - Comment - Bias value for spot shadows (prevent shadow acne). - Persist - 1 - Type - F32 - Value - -64.0 - - RenderDeferredSpotShadowOffset - - Comment - Offset value for spot shadows (prevent shadow acne). - Persist - 1 - Type - F32 - Value - 0.8 - - - RenderShadowBias - - Comment - Bias value for shadows (prevent shadow acne). - Persist - 1 - Type - F32 - Value - -0.008 - - RenderShadowOffset - - Comment - Offset value for shadows (prevent shadow acne). - Persist - 1 - Type - F32 - Value - 0.01 - - - RenderShadowBiasError - - Comment - Error scale for shadow bias (based on altitude). - Persist - 1 - Type - F32 - Value - 0 - - RenderShadowOffsetError - - Comment - Error scale for shadow offset (based on altitude). - Persist - 1 - Type - F32 - Value - 0 - - - RenderSpotLightsInNondeferred - - Comment - Whether to support projectors as spotlights when Lighting and Shadows is disabled - Persist - 1 - Type - Boolean - Value - 0 - - - RenderSpotShadowBias - - Comment - Bias value for shadows (prevent shadow acne). - Persist - 1 - Type - F32 - Value - 0.0 - - RenderSpotShadowOffset - - Comment - Offset value for shadows (prevent shadow acne). - Persist - 1 - Type - F32 - Value - 0.04 - - - RenderShadowResolutionScale - - Comment - Scale of shadow map resolution vs. screen resolution - Persist - 1 - Type - F32 - Value - 1.0 - - - RenderDeferredTreeShadowBias - - Comment - Bias value for tree shadows (prevent shadow acne). - Persist - 1 - Type - F32 - Value - 1.0 - - RenderDeferredTreeShadowOffset - - Comment - Offset value for tree shadows (prevent shadow acne). - Persist - 1 - Type - F32 - Value - 1.0 - - - RenderHoverGlowEnable - - Comment - Show glow effect when hovering on interactive objects. - Persist - 1 - Type - Boolean - Value - 0 - - - RenderHighlightFadeTime - - Comment - Transition time for mouseover highlights. - Persist - 1 - Type - F32 - Value - 0.1 - - - RenderHighlightBrightness - - Comment - Brightness of mouseover highlights. - Persist - 1 - Type - F32 - Value - 4.0 - - - RenderHighlightThickness - - Comment - Thickness of mouseover highlights. - Persist - 1 - Type - F32 - Value - 0.6 - - - RenderHighlightColor - - Comment - Brightness of mouseover highlights. - Persist - 1 - Type - Color4 - Value - - 0.4 - 0.98 - 0.93 - 1.0 - - - - RenderSpecularResX - - Comment - Spec map resolution. - Persist - 1 - Type - U32 - Value - 128 - - - RenderSpecularResY - - Comment - Spec map resolution. - Persist - 1 - Type - U32 - Value - 128 - - - RenderSpecularExponent - - Comment - Specular exponent for generating spec map - Persist - 1 - Type - F32 - Value - 8 - - - RenderDeferred - - Comment - Use deferred rendering pipeline. - Persist - 1 - Type - Boolean - Value - 0 - - - RenderDeferredGI - - Comment - Enable GI in deferred renderer. - Persist - 1 - Type - Boolean - Value - 0 - - - RenderDeferredSun - - Comment - Execute sunlight shader in deferred renderer. - Persist - 1 - Type - Boolean - Value - 1 - - - RenderDeferredAtmospheric - - Comment - Execute atmospheric shader in deferred renderer. - Persist - 1 - Type - Boolean - Value - 1 - - - RenderDeferredSSAO - - Comment - Execute screen space ambient occlusion shader in deferred renderer. - Persist - 1 - Type - Boolean - Value - 1 - - - RenderDeferredBlurLight - - Comment - Execute shadow softening shader in deferred renderer. - Persist - 1 - Type - Boolean - Value - 1 - - - RenderDeferredLocalLights - - Comment - Execute local lighting shader in deferred renderer. - Persist - 1 - Type - Boolean - Value - 1 - - - RenderDeferredFullscreenLights - - Comment - Execute local lighting shader in deferred renderer. - Persist - 1 - Type - Boolean - Value - 1 - - - RenderDeferredSunWash - - Comment - Amount local lights are washed out by sun. - Persist - 1 - Type - F32 - Value - 0.5 - - RenderShadowNoise - - Comment - Magnitude of noise on shadow samples. - Persist - 1 - Type - F32 - Value - -0.0001 - - RenderShadowErrorCutoff - - Comment - Cutoff error value to use ortho instead of perspective projection. - Persist - 1 - Type - F32 - Value - 5.0 - - RenderShadowFOVCutoff - - Comment - Cutoff FOV to use ortho instead of perspective projection. - Persist - 1 - Type - F32 - Value - 1.1 - - - RenderShadowGaussian - - Comment - Gaussian coefficients for the two shadow/SSAO blurring passes (z component unused). - Persist - 1 - Type - Vector3 - Value - - 3.0 - 2.0 - 0.0 - - - - RenderShadowBlurSize - - Comment - Scale of shadow softening kernel. - Persist - 1 - Type - F32 - Value - 1.4 - - RenderShadowBlurSamples - - Comment - Number of samples to take for each pass of shadow blur (value range 1-16). Actual number of samples is value * 2 - 1. - Persist - 1 - Type - U32 - Value - 4 - - RenderShadowBlurDistFactor - - Comment - Distance scaler for shadow blur. - Persist - 1 - Type - F32 - Value - 0.1 - - - RenderGIAmbiance - - Comment - Ambiance factor of global illumination contribution. - Persist - 1 - Type - F32 - Value - 0.5 - - - RenderGIMinRenderSize - - Comment - Minimum size of objects to put into GI source map. - Persist - 1 - Type - F32 - Value - 0.5 - - - RenderGIBlurColorCurve - - Comment - Color curve for GI softening kernel - Persist - 1 - Type - Vector3 - Value - - 1.0 - 0.6 - 0.02 - - - - RenderGIBlurPasses - - Comment - Scale of GI softening kernel. - Persist - 1 - Type - U32 - Value - 4 - - - RenderGIBlurSize - - Comment - Scale of GI softening kernel. - Persist - 1 - Type - F32 - Value - 4.0 - - RenderGIBlurSamples - - Comment - Number of samples to take for each pass of GI blur (value range 1-16). Actual number of samples is value * 2 - 1. - Persist - 1 - Type - U32 - Value - 16 - - RenderGIBlurDistFactor - - Comment - Distance scaler for GI blur. - Persist - 1 - Type - F32 - Value - 0.0 - - - RenderDynamicLOD - - Comment - Dynamically adjust level of detail. - Persist - 1 - Type - Boolean - Value - 1 - - RenderFSAASamples - - Comment - Number of samples to use for FSAA (0 = no AA). - Persist - 1 - Type - U32 - Value - 0 - - RenderFarClip - - Comment - Distance of far clip plane from camera (meters) - Persist - 1 - Type - F32 - Value - 256.0 - - RenderAutoMaskAlphaNonDeferred - - Comment - Use alpha masks where appropriate, in the non-deferred (non-'Lighting and Shadows') graphics mode - Persist - 1 - Type - Boolean - Value - 0 - - RenderAutoMaskAlphaDeferred - - Comment - Use alpha masks where appropriate, in the deferred ('Lighting and Shadows') graphics mode - Persist - 1 - Type - Boolean - Value - 1 - - RenderFlexTimeFactor - - Comment - Controls level of detail of flexible objects (multiplier for amount of time spent processing flex objects) - Persist - 1 - Type - F32 - Value - 1.0 - - RenderFogRatio - - Comment - Distance from camera where fog reaches maximum density (fraction or multiple of far clip distance) - Persist - 1 - Type - F32 - Value - 4.0 - - RenderGamma - - Comment - Sets gamma exponent for renderer - Persist - 1 - Type - F32 - Value - 0.0 - - RenderGammaFull - - Comment - Use fully controllable gamma correction, instead of faster, hard-coded gamma correction of 2. - Persist - 1 - Type - Boolean - Value - 1.0 - - RenderGlow - - Comment - Render bloom post effect. - Persist - 1 - Type - Boolean - Value - 1 - - RenderGlowIterations - - Comment - Number of times to iterate the glow (higher = wider and smoother but slower) - Persist - 1 - Type - S32 - Value - 2 - - RenderGlowLumWeights - - Comment - Weights for each color channel to be used in calculating luminance (should add up to 1.0) - Persist - 1 - Type - Vector3 - Value - - 0.299 - 0.587 - 0.114 - - - RenderGlowMaxExtractAlpha - - Comment - Max glow alpha value for brightness extraction to auto-glow. - Persist - 1 - Type - F32 - Value - 0.065 - - RenderGlowMinLuminance - - Comment - Min luminance intensity necessary to consider an object bright enough to automatically glow. - Persist - 1 - Type - F32 - Value - 2.5 - - RenderGlowResolutionPow - - Comment - Glow map resolution power of two. - Persist - 1 - Type - S32 - Value - 9 - - RenderGlowStrength - - Comment - Additive strength of glow. - Persist - 1 - Type - F32 - Value - 0.35 - - RenderGlowWarmthAmount - - Comment - Amount of warmth extraction to use (versus luminance extraction). 0 = lum, 1.0 = warmth - Persist - 1 - Type - F32 - Value - 0.0 - - RenderGlowWarmthWeights - - Comment - Weight of each color channel used before finding the max warmth - Persist - 1 - Type - Vector3 - Value - - 1.0 - 0.5 - 0.7 - - - RenderGlowWidth - - Comment - Glow sample size (higher = wider and softer but eventually more pixelated) - Persist - 1 - Type - F32 - Value - 1.3 - - RenderGround - - Comment - Determines whether we can render the ground pool or not - Persist - 1 - Type - Boolean - Value - 1 - - RenderHUDInSnapshot - - Comment - Display HUD attachments in snapshot - Persist - 1 - Type - Boolean - Value - 0 - - RenderHUDParticles - - Comment - Display particle systems in HUD attachments (experimental) - Persist - 1 - Type - Boolean - Value - 0 - - RenderHighlightSelections - - Comment - Show selection outlines on objects - Persist - 0 - Type - Boolean - Value - 1 - - RenderHiddenSelections - - Comment - Show selection lines on objects that are behind other objects - Persist - 1 - Type - Boolean - Value - 1 - - RenderHideGroupTitle - - Comment - Don't show my group title in my name label - Persist - 1 - Type - Boolean - Value - 0 - - NameTagShowGroupTitles - - Comment - Show group titles in name labels - Persist - 1 - Type - Boolean - Value - 0 - - NameTagShowDisplayNames - - Comment - Show display names in name labels - Persist - 1 - Type - Boolean - Value - 1 - - NameTagShowFriends - - Comment - Highlight the name tags of your friends - Persist - 1 - Type - Boolean - Value - 0 - - NameTagShowUsernames - - Comment - Show usernames in avatar name tags - Persist - 1 - Type - Boolean - Value - 1 - - RenderInitError - - Comment - Error occured while initializing GL - Persist - 1 - Type - Boolean - Value - 0 - - RenderLightRadius - - Comment - Render the radius of selected lights - Persist - 1 - Type - Boolean - Value - 0 - - RenderMaxPartCount - - Comment - Maximum number of particles to display on screen - Persist - 1 - Type - S32 - Value - 4096 - - RenderMaxNodeSize - - Comment - Maximum size of a single node's vertex data (in KB). - Persist - 1 - Type - S32 - Value - 8192 - - RenderMaxVBOSize - - Comment - Maximum size of a vertex buffer (in KB). - Persist - 1 - Type - S32 - Value - 512 - - RenderNameFadeDuration - - Comment - Time interval over which to fade avatar names (seconds) - Persist - 1 - Type - F32 - Value - 1.0 - - RenderNameShowSelf - - Comment - Display own name above avatar - Persist - 1 - Type - Boolean - Value - 1 - - RenderNameShowTime - - Comment - Fade avatar names after specified time (seconds) - Persist - 1 - Type - F32 - Value - 10.0 - - RenderObjectBump - - Comment - Show bumpmapping on primitives - Persist - 1 - Type - Boolean - Value - 1 - - RenderQualityPerformance - - Comment - Which graphics settings you've chosen - Persist - 1 - Type - U32 - Value - 1 - - RenderReflectionDetail - - Comment - Detail of reflection render pass. - Persist - 1 - Type - S32 - Value - 2 - - RenderShadowDetail - - Comment - Detail of shadows. - Persist - 1 - Type - S32 - Value - 2 - - - RenderReflectionRes - - Comment - Reflection map resolution. - Persist - 1 - Type - S32 - Value - 64 - - RenderResolutionDivisor - - Comment - Divisor for rendering 3D scene at reduced resolution. - Persist - 1 - Type - U32 - Value - 1 - - RenderShaderLightingMaxLevel - - Comment - Max lighting level to use in the shader (class 3 is default, 2 is less lights, 1 is sun/moon only. Works around shader compiler bugs on certain platforms.) - Persist - 1 - Type - S32 - Value - 3 - - RenderShaderLODThreshold - - Comment - Fraction of draw distance defining the switch to a different shader LOD - Persist - 1 - Type - F32 - Value - 1.0 - - RenderShaderParticleThreshold - - Comment - Fraction of draw distance to not use shader on particles - Persist - 1 - Type - F32 - Value - 0.25 - - RenderSunDynamicRange - - Comment - Defines what percent brighter the sun is than local point lights (1.0 = 100% brighter. Value should not be less than 0. ). - Persist - 1 - Type - F32 - Value - 1.0 - - RenderTerrainDetail - - Comment - Detail applied to terrain texturing (0 = none, 1 or 2 = full) - Persist - 1 - Type - S32 - Value - 2 - - RenderTerrainLODFactor - - Comment - Controls level of detail of terrain (multiplier for current screen area when calculated level of detail) - Persist - 1 - Type - F32 - Value - 1.0 - - RenderTerrainScale - - Comment - Terrain detail texture scale - Persist - 1 - Type - F32 - Value - 12.0 - - RenderTextureMemoryMultiple - - Comment - Multiple of texture memory value to use (should fit: 0 < value <= 1.0) - Persist - 1 - Type - F32 - Value - 1.0 - - RenderTrackerBeacon - - Comment - Display tracking arrow and beacon to target avatar, teleport destination - Persist - 1 - Type - Boolean - Value - 1 - - RenderTransparentWater - - Comment - Render water as transparent. Setting to false renders water as opaque with a simple texture applied. - Persist - 1 - Type - Boolean - Value - 1 - - RenderTreeLODFactor - - Comment - Controls level of detail of vegetation (multiplier for current screen area when calculated level of detail) - Persist - 1 - Type - F32 - Value - 0.5 - - RenderUIInSnapshot - - Comment - Display user interface in snapshot - Persist - 1 - Type - Boolean - Value - 0 - - RenderUIBuffer - - Comment - Cache ui render in a screen aligned buffer. - Persist - 1 - Type - Boolean - Value - 0 - - RenderUnloadedAvatar - - Comment - Show avatars which haven't finished loading - Persist - 1 - Type - Boolean - Value - 0 - - RenderUseFBO - - Comment - Whether we want to use GL_EXT_framebuffer_objects. - Persist - 1 - Type - Boolean - Value - 1 - - RenderUseTriStrips - - Comment - Use triangle strips for rendering prims. - Persist - 1 - Type - Boolean - Value - 0 - - RenderUseFarClip - - Comment - If false, frustum culling will ignore far clip plane. - Persist - 1 - Type - Boolean - Value - 1 - - RenderUseImpostors - - Comment - Whether we want to use impostors for far away avatars. - Persist - 1 - Type - Boolean - Value - 1 - - RenderUseShaderLOD - - Comment - Whether we want to have different shaders for LOD - Persist - 1 - Type - Boolean - Value - 1 - - RenderUseShaderNearParticles - - Comment - Whether we want to use shaders on near particles - Persist - 1 - Type - Boolean - Value - 0 - - RenderVBOEnable - - Comment - Use GL Vertex Buffer Objects - Persist - 1 - Type - Boolean - Value - 1 - - RenderUseStreamVBO - - Comment - Use VBO's for stream buffers - Persist - 1 - Type - Boolean - Value - 1 - - RenderVolumeLODFactor - - Comment - Controls level of detail of primitives (multiplier for current screen area when calculated level of detail) - Persist - 1 - Type - F32 - Value - 1.0 - - RenderWater - - Comment - Display water - Persist - 1 - Type - Boolean - Value - 1 - - RenderWaterMipNormal - - Comment - Use mip maps for water normal map. - Persist - 1 - Type - Boolean - Value - 1 - - RenderWaterRefResolution - - Comment - Water planar reflection resolution. - Persist - 1 - Type - S32 - Value - 512 - - RenderParcelSelection - - Comment - Display selected parcel outline - Persist - 1 - Type - Boolean - Value - 1 - - RotateRight - - Comment - Make the agent rotate to its right. - Persist - 1 - Type - Boolean - Value - 0 - - RotationStep - - Comment - All rotations via rotation tool are constrained to multiples of this unit (degrees) - Persist - 1 - Type - F32 - Value - 1.0 - - SafeMode - - Comment - Reset preferences, run in safe mode. - Persist - 1 - Type - Boolean - Value - 0 - - SaveMinidump - - Comment - Save minidump for developer debugging on crash - Persist - 1 - Type - Boolean - Value - 1 - - ScaleShowAxes - - Comment - Show indicator of selected scale axis when scaling - Persist - 1 - Type - Boolean - Value - 0 - - ScaleStretchTextures - - Comment - Stretch textures along with object when scaling - Persist - 1 - Type - Boolean - Value - 1 - - ScaleUniform - - Comment - Scale selected objects evenly about center of selection - Persist - 1 - Type - Boolean - Value - 0 - - ScriptHelpFollowsCursor - - Comment - Scripting help window updates contents based on script editor contents under text cursor - Persist - 1 - Type - Boolean - Value - 0 - - ScriptsCanShowUI - - Comment - Allow LSL calls (such as LLMapDestination) to spawn viewer UI - Persist - 1 - Type - Boolean - Value - 1 - - SecondLifeEnterprise - - Comment - Enables Second Life Enterprise features - Persist - 1 - Type - Boolean - Value - 0 - - SelectMovableOnly - - Comment - Select only objects you can move - Persist - 1 - Type - Boolean - Value - 0 - - SelectOwnedOnly - - Comment - Select only objects you own - Persist - 1 - Type - Boolean - Value - 0 - - SelectionHighlightAlpha - - Comment - Opacity of selection highlight (0.0 = completely transparent, 1.0 = completely opaque) - Persist - 1 - Type - F32 - Value - 0.40000000596 - - SelectionHighlightAlphaTest - - Comment - Alpha value below which pixels are displayed on selection highlight line (0.0 = show all pixels, 1.0 = show now pixels) - Persist - 1 - Type - F32 - Value - 0.1 - - SelectionHighlightThickness - - Comment - Thickness of selection highlight line (fraction of view distance) - Persist - 1 - Type - F32 - Value - 0.00999999977648 - - SelectionHighlightUAnim - - Comment - Rate at which texture animates along U direction in selection highlight line (fraction of texture per second) - Persist - 1 - Type - F32 - Value - 0.0 - - SelectionHighlightUScale - - Comment - Scale of texture display on selection highlight line (fraction of texture size) - Persist - 1 - Type - F32 - Value - 0.1 - - SelectionHighlightVAnim - - Comment - Rate at which texture animates along V direction in selection highlight line (fraction of texture per second) - Persist - 1 - Type - F32 - Value - 0.5 - - SelectionHighlightVScale - - Comment - Scale of texture display on selection highlight line (fraction of texture size) - Persist - 1 - Type - F32 - Value - 1.0 - - ServerChoice - - Comment - [DO NOT MODIFY] Controls which grid you connect to - Persist - 1 - Type - S32 - Value - 0 - - ShareWithGroup - - Comment - Newly created objects are shared with the currently active group - Persist - 1 - Type - Boolean - Value - 0 - - ShowAdvancedGraphicsSettings - - Comment - Show advanced graphics settings - Persist - 1 - Type - Boolean - Value - 0 - - ShowAllObjectHoverTip - - Comment - Show descriptive tooltip when mouse hovers over non-interactive and interactive objects. - Persist - 1 - Type - Boolean - Value - 0 - - AvatarNameTagMode - - Comment - Select Avatar Name Tag Mode - Persist - 1 - Type - S32 - Value - 1 - - ShowAxes - - Comment - Render coordinate frame at your position - Persist - 1 - Type - Boolean - Value - 0 - - ShowBanLines - - Comment - Show in-world ban/access borders - Persist - 1 - Type - Boolean - Value - 1 - - ShowBuildButton - - Comment - Shows/Hides Build button in the bottom tray. - Persist - 1 - Type - Boolean - Value - 0 - - ShowCameraButton - - Comment - Show/Hide View button in the bottom tray. - Persist - 1 - Type - Boolean - Value - 1 - - ShowConsoleWindow - - Comment - Show log in separate OS window - Persist - 1 - Type - Boolean - Value - 0 - - NavBarShowCoordinates - - Comment - Show coordinates in navigation bar - Persist - 1 - Type - Boolean - Value - 0 - - NavBarShowParcelProperties - - Comment - Show parcel property icons in navigation bar - Persist - 1 - Type - Boolean - Value - 1 - - ShowBetaGrids - - Comment - Display the beta grids in the grid selection control. - Persist - 1 - Type - Boolean - Value - 1 - - ShowCrosshairs - - Comment - Display crosshairs when in mouselook mode - Persist - 1 - Type - Boolean - Value - 1 - - ShowDebugConsole - - Comment - Show log in SL window - Persist - 1 - Type - Boolean - Value - 0 - - ShowEmptyFoldersWhenSearching - - Comment - Shows folders that do not have any visible contents when applying a filter to inventory - Persist - 1 - Type - Boolean - Value - 0 - - ShowGestureButton - - Comment - Shows/Hides Gesture button in the bottom tray. - Persist - 1 - Type - Boolean - Value - 1 - - ShowHoverTips - - Comment - Show descriptive tooltip when mouse hovers over items in world - Persist - 1 - Type - Boolean - Value - 1 - - ShowLandHoverTip - - Comment - Show descriptive tooltip when mouse hovers over land - Persist - 1 - Type - Boolean - Value - 0 - - ShowMiniMapButton - - Comment - Shows/Hides Mini-Map button in the bottom tray. - Persist - 1 - Type - Boolean - Value - 0 - - ShowMoveButton - - Comment - Shows/Hides Move button in the bottom tray. - Persist - 1 - Type - Boolean - Value - 1 - - ShowScriptErrors - - Comment - Show script errors - Persist - 1 - Type - Boolean - Value - 1 - - ShowScriptErrorsLocation - - Comment - Show script error in chat or window - Persist - 1 - Type - S32 - Value - 1 - - ShowSearchButton - - Comment - Shows/Hides Search button in the bottom tray. - Persist - 1 - Type - Boolean - Value - 0 - - ShowSnapshotButton - - Comment - Shows/Hides Snapshot button button in the bottom tray. - Persist - 1 - Type - Boolean - Value - 1 - - ShowObjectRenderingCost - - Comment - Show the object rendering cost in build tools - Persist - 1 - Type - Boolean - Value - 1 - - ShowNavbarFavoritesPanel - - Comment - Show/Hide Navigation Bar Favorites Panel - Persist - 1 - Type - Boolean - Value - 1 - - ShowNavbarNavigationPanel - - Comment - Show/Hide Navigation Bar Navigation Panel - Persist - 1 - Type - Boolean - Value - 1 - - ShowWorldMapButton - - Comment - Shows/Hides Map button in the bottom tray. - Persist - 1 - Type - Boolean - Value - 0 - - ShowMiniLocationPanel - - Comment - Show/Hide Mini-Location Panel - Persist - 1 - Type - Boolean - Value - 0 - - SidebarCameraMovement - - Comment - Reflects world rect changing while changing sidebar visibility. - Persist - 1 - Type - Boolean - Value - 0 - - GroupListShowIcons - - Comment - Show/hide group icons in the group list - Persist - 1 - Type - Boolean - Value - 1 - - FriendsListShowIcons - - Comment - Show/hide online and all friends icons in the friend list - Persist - 1 - Type - Boolean - Value - 1 - - FriendsListShowPermissions - - Comment - Show/hide permission icons in the friend list - Persist - 1 - Type - Boolean - Value - 1 - - NearbyListShowIcons - - Comment - Show/hide people icons in nearby list - Persist - 1 - Type - Boolean - Value - 1 - - RecentListShowIcons - - Comment - Show/hide people icons in recent list - Persist - 1 - Type - Boolean - Value - 1 - - FriendsSortOrder - - Comment - Specifies sort order for friends (0 = by name, 1 = by online status) - Persist - 1 - Type - U32 - Value - 0 - - NearbyPeopleSortOrder - - Comment - Specifies sort order for nearby people (0 = by name, 3 = by distance, 4 = by most recent) - Persist - 1 - Type - U32 - Value - 4 - - RecentPeopleSortOrder - - Comment - Specifies sort order for recent people (0 = by name, 2 = by most recent) - Persist - 1 - Type - U32 - Value - 2 - - ShowPGSearchAll - - Comment - Display results of search All that are flagged as general - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 1 - - ShowMatureSearchAll - - Comment - Display results of search All that are flagged as moderate - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 0 - - ShowAdultSearchAll - - Comment - Display results of search All that are flagged as adult - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 0 - - ShowPGGroups - - Comment - Display results of find groups that are flagged as general - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 1 - - ShowMatureGroups - - Comment - Display results of find groups that are flagged as moderate - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 0 - - ShowAdultGroups - - Comment - Display results of find groups that are flagged as adult - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 0 - - ShowPGClassifieds - - Comment - Display results of find classifieds that are flagged as general - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 1 - - ShowMatureClassifieds - - Comment - Display results of find classifieds that are flagged as moderate - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 0 - - ShowAdultClassifieds - - Comment - Display results of find classifieds that are flagged as adult - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 0 - - ShowPGEvents - - Comment - Display results of find events that are flagged as general - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 1 - - ShowMatureEvents - - Comment - Display results of find events that are flagged as moderate - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 0 - - ShowAdultEvents - - Comment - Display results of find events that are flagged as adult - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 0 - - ShowPGLand - - Comment - Display results of find land sales that are flagged as general - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 1 - - ShowMatureLand - - Comment - Display results of find land sales that are flagged as moderate - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 0 - - ShowAdultLand - - Comment - Display results of find land sales that are flagged as adult - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 0 - - ShowPGSims - - Comment - Display results of find places or find popular that are in general sims - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 1 - - ShowMatureSims - - Comment - Display results of find places or find popular that are in moderate sims - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 0 - - ShowAdultSims - - Comment - Display results of find places or find popular that are in adult sims - Persist - 1 - HideFromEditor - 1 - Type - Boolean - Value - 0 - - ShowNearClip - - Comment - - Persist - 0 - Type - Boolean - Value - 0 - - ShowNewInventory - - Comment - Automatically views new notecards/textures/landmarks - Persist - 1 - Type - Boolean - Value - 1 - - ShowInInventory - - Comment - Automatically opens inventory to show accepted objects - Persist - 1 - Type - Boolean - Value - 1 - - ShowObjectUpdates - - Comment - Show when update messages are received for individual objects - Persist - 0 - Type - Boolean - Value - 0 - - ShowOverlayTitle - - Comment - Prints watermark text message on screen - Persist - 1 - Type - Boolean - Value - 0 - - ShowParcelOwners - - Comment - - Persist - 0 - Type - Boolean - Value - 0 - - ShowPermissions - - Comment - - Persist - 0 - Type - Boolean - Value - 0 - - ShowPropertyLines - - Comment - Show line overlay demarking property boundaries - Persist - 1 - Type - Boolean - Value - 0 - - ShowNetStats - - Comment - Show the Search Bar in the Status Overlay - Persist - 1 - Type - Boolean - Value - 0 - - ShowSelectionBeam - - Comment - Show selection particle beam when selecting or interacting with objects. - Persist - 1 - Type - Boolean - Value - 1 - - ShowStartLocation - - Comment - Display starting location menu on login screen - Persist - 1 - Type - Boolean - Value - 0 - - ShowTangentBasis - - Comment - Render normal and binormal (debugging bump mapping) - Persist - 1 - Type - Boolean - Value - 0 - - ShowToolBar - - Comment - Show toolbar at bottom of screen - Persist - 1 - Type - Boolean - Value - 1 - - ShowTutorial - - Comment - Show tutorial window on login - Persist - 1 - Type - Boolean - Value - 0 - - ShowVoiceVisualizersInCalls - - Comment - Enables in-world voice visualizers, voice gestures and lip-sync while in group or P2P calls. - Persist - 1 - Type - Boolean - Value - 0 - - SkinCurrent - - Comment - The currently selected skin. - Persist - 1 - Type - String - Value - default - - SkinningSettingsFile - - Comment - Client skin color setting file name (per install). - Persist - 0 - Type - String - Value - - - SkyAmbientScale - - Comment - Controls strength of ambient, or non-directional light from the sun and moon (fraction or multiple of default ambient level) - Persist - 1 - Type - F32 - Value - 0.300000011921 - - SkyEditPresets - - Comment - Whether to be able to edit the sky defaults or not - Persist - 1 - Type - Boolean - Value - 0 - - SkyNightColorShift - - Comment - Controls moonlight color (base color applied to moon as light source) - Persist - 1 - Type - Color3 - Value - - 0.67 - 0.67 - 1.0 - - - SkyOverrideSimSunPosition - - Comment - - Persist - 0 - Type - Boolean - Value - 0 - - SkySunDefaultPosition - - Comment - Default position of sun in sky (direction in world coordinates) - Persist - 1 - Type - Vector3 - Value - - 1.0 - 0.0 - 0.1 - - - SkyUseClassicClouds - - Comment - Whether to use the old Second Life particle clouds or not - Persist - 1 - Type - Boolean - Value - 1 - - SnapEnabled - - Comment - Enable snapping to grid - Persist - 1 - Type - Boolean - Value - 1 - - SnapMargin - - Comment - Controls maximum distance between windows before they auto-snap together (pixels) - Persist - 1 - Type - S32 - Value - 10 - - SnapToMouseCursor - - Comment - When snapping to grid, center object on nearest grid point to mouse cursor - Persist - 1 - Type - Boolean - Value - 0 - - SnapshotFormat - - Comment - Save snapshots in this format (0 = PNG, 1 = JPEG, 2 = BMP) - Persist - 1 - Type - S32 - Value - 0 - - SnapshotLocalLastResolution - - Comment - Take next local snapshot at this resolution - Persist - 1 - Type - S32 - Value - 0 - - SnapshotPostcardLastResolution - - Comment - Take next postcard snapshot at this resolution - Persist - 1 - Type - S32 - Value - 0 - - SnapshotQuality - - Comment - Quality setting of postcard JPEGs (0 = worst, 100 = best) - Persist - 1 - Type - S32 - Value - 75 - - SnapshotSharingEnabled - - Comment - Enable uploading of snapshots to a web service. - Persist - 1 - Type - Boolean - Value - 0 - - SnapshotConfigURL - - Comment - URL to fetch Snapshot Sharing configuration data from. - Persist - 1 - Type - String - Value - http://photos.apps.staging.avatarsunited.com/viewer_config - - SnapshotTextureLastResolution - - Comment - Take next texture snapshot at this resolution - Persist - 1 - Type - S32 - Value - 0 - - SpeedTest - - Comment - Performance testing mode, no network - Persist - 1 - Type - Boolean - Value - 0 - - StatsAutoRun - - Comment - Play back autopilot - Persist - 1 - Type - Boolean - Value - 0 - - StatsFile - - Comment - Filename for stats logging output - Persist - 1 - Type - String - Value - fs.txt - - StatsNumRuns - - Comment - Loop autopilot playback this number of times - Persist - 1 - Type - S32 - Value - -1 - - StatsPilotFile - - Comment - Filename for stats logging autopilot path - Persist - 1 - Type - String - Value - pilot.txt - - StatsQuitAfterRuns - - Comment - Quit application after this number of autopilot playback runs - Persist - 1 - Type - Boolean - Value - 0 - - StatsSessionTrackFrameStats - - Comment - Track rendering and network statistics - Persist - 1 - Type - Boolean - Value - 0 - - StatsSummaryFile - - Comment - Filename for stats logging summary - Persist - 1 - Type - String - Value - fss.txt - - SystemLanguage - - Comment - Language indicated by system settings (for UI) - Persist - 1 - Type - String - Value - en - - TabToTextFieldsOnly - - Comment - TAB key takes you to next text entry field, instead of next widget - Persist - 1 - Type - Boolean - Value - 0 - - TerrainColorHeightRange - - Comment - Altitude range over which a given terrain texture has effect (meters) - Persist - 1 - Type - F32 - Value - 60.0 - - TerrainColorStartHeight - - Comment - Starting altitude for terrain texturing (meters) - Persist - 1 - Type - F32 - Value - 20.0 - - TextureDecodeDisabled - - Comment - If TRUE, do not fetch and decode any textures - Persist - 1 - Type - Boolean - Value - 0 - - TextureDisable - - Comment - If TRUE, do not load textures for in-world content - Persist - 1 - Type - Boolean - Value - 0 - - TextureDiscardLevel - - Comment - Specify texture resolution (0 = highest, 5 = lowest) - Persist - 1 - Type - U32 - Value - 0 - - TextureLoadFullRes - - Comment - If TRUE, always load textures at full resolution (discard = 0) - Persist - 1 - Type - Boolean - Value - 0 - - TextureMemory - - Comment - Amount of memory to use for textures in MB (0 = autodetect) - Persist - 1 - Type - S32 - Value - 0 - - TexturePickerShowFolders - - Comment - Show folders with no texures in texture picker - Persist - 1 - Type - Boolean - Value - 1 - - TexturePickerSortOrder - - Comment - Specifies sort key for textures in texture picker (+0 = name, +1 = date, +2 = folders always by name, +4 = system folders to top) - Persist - 1 - Type - U32 - Value - 2 - - ThrottleBandwidthKBPS - - Comment - Maximum allowable downstream bandwidth (kilo bits per second) - Persist - 1 - Type - F32 - Value - 500.0 - - UpdaterMaximumBandwidth - - Comment - Maximum allowable downstream bandwidth for updater service (kilo bits per second) - Persist - 1 - Type - F32 - Value - 500.0 - - ToolTipDelay - - Comment - Seconds before displaying tooltip when mouse stops over UI element - Persist - 1 - Type - F32 - Value - 0.699999988079 - - ToolTipFadeTime - - Comment - Seconds over which tooltip fades away - Persist - 1 - Type - F32 - Value - 0.2 - - ToolTipVisibleTimeFar - - Comment - Fade tooltip after after time passes (seconds) while mouse not near tooltip - Persist - 1 - Type - F32 - Value - 1.0 - - ToolTipVisibleTimeNear - - Comment - Fade tooltip after after time passes (seconds) while mouse near tooltip or original position - Persist - 1 - Type - F32 - Value - 10.0 - - ToolTipVisibleTimeOver - - Comment - Fade tooltip after after time passes (seconds) while mouse over tooltip - Persist - 1 - Type - F32 - Value - 1000.0 - - ToolboxAutoMove - - Comment - [NOT USED] - Persist - 1 - Type - Boolean - Value - 0 - - TrackFocusObject - - Comment - Camera tracks last object zoomed on - Persist - 1 - Type - Boolean - Value - 1 - - TranslateLanguage - - Comment - Translate Language specifier - Persist - 1 - Type - String - Value - default - - TranslateChat - - Comment - Translate incoming chat messages - Persist - 1 - Type - Boolean - Value - 0 - - TutorialURL - - Comment - URL for tutorial menu item, set automatically during login - Persist - 0 - Type - String - Value - - - TypeAheadTimeout - - Comment - Time delay before clearing type-ahead buffer in lists (seconds) - Persist - 1 - Type - F32 - Value - 1.5 - - UIAutoScale - - Comment - Keep UI scale consistent across different resolutions - Persist - 1 - Type - Boolean - Value - 1 - - UIAvatariconctrlSymbolHPad - - Comment - UI Avatar Icon Control Symbol Horizontal Pad - Persist - 1 - Type - S32 - Value - 2 - - UIAvatariconctrlSymbolVPad - - Comment - UI Avatar Icon Control Symbol Vertical Pad - Persist - 1 - Type - S32 - Value - 2 - - UIAvatariconctrlSymbolSize - - Comment - UI Avatar Icon Control Symbol Size - Persist - 1 - Type - S32 - Value - 5 - - UIAvatariconctrlSymbolPosition - - Comment - UI Avatar Icon Control Symbol Position (TopLeft|TopRight|BottomLeft|BottomRight) - Persist - 1 - Type - String - Value - BottomRight - - UIButtonOrigHPad - - Comment - UI Button Original Horizontal Pad - Persist - 1 - Type - S32 - Value - 6 - - UICheckboxctrlBtnSize - - Comment - UI Checkbox Control Button Size - Persist - 1 - Type - S32 - Value - 13 - - UICheckboxctrlHeight - - Comment - UI Checkbox Control Height - Persist - 1 - Type - S32 - Value - 16 - - UICheckboxctrlHPad - - Comment - UI Checkbox Control Horizontal Pad - Persist - 1 - Type - S32 - Value - 2 - - UICheckboxctrlSpacing - - Comment - UI Checkbox Control Spacing - Persist - 1 - Type - S32 - Value - 5 - - UICheckboxctrlVPad - - Comment - UI Checkbox Control Vertical Pad - Persist - 1 - Type - S32 - Value - 2 - - UICloseBoxFromTop - - Comment - Distance from top of floater to top of close box icon, pixels - Persist - 1 - Type - S32 - Value - 5 - - UIExtraTriangleHeight - - Comment - UI extra triangle height - Persist - 1 - Type - S32 - Value - -2 - - UIExtraTriangleWidth - - Comment - UI extra triangle width - Persist - 1 - Type - S32 - Value - 2 - - UIFloaterCloseBoxSize - - Comment - Size of UI floater close box size - Persist - 1 - Type - S32 - Value - 16 - - UIFloaterHPad - - Comment - Size of UI floater horizontal pad - Persist - 1 - Type - S32 - Value - 6 - - UIFloaterTestBool - - Comment - Example saved setting for the test floater - Persist - 1 - Type - Boolean - Value - 0 - - UIFloaterTitleVPad - - Comment - Distance from top of floater to top of title string, pixels - Persist - 1 - Type - S32 - Value - 7 - - UIImgDefaultEyesUUID - - Comment - - Persist - 0 - Type - String - Value - 6522e74d-1660-4e7f-b601-6f48c1659a77 - - UIImgDefaultGlovesUUID - - Comment - - Persist - 0 - Type - String - Value - 5748decc-f629-461c-9a36-a35a221fe21f - - UIImgDefaultHairUUID - - Comment - - Persist - 0 - Type - String - Value - 7ca39b4c-bd19-4699-aff7-f93fd03d3e7b - - UIImgDefaultJacketUUID - - Comment - - Persist - 0 - Type - String - Value - 5748decc-f629-461c-9a36-a35a221fe21f - - UIImgDefaultPantsUUID - - Comment - - Persist - 0 - Type - String - Value - 5748decc-f629-461c-9a36-a35a221fe21f - - UIImgDefaultShirtUUID - - Comment - - Persist - 0 - Type - String - Value - 5748decc-f629-461c-9a36-a35a221fe21f - - UIImgDefaultShoesUUID - - Comment - - Persist - 0 - Type - String - Value - 5748decc-f629-461c-9a36-a35a221fe21f - - UIImgDefaultSkirtUUID - - Comment - - Persist - 0 - Type - String - Value - 5748decc-f629-461c-9a36-a35a221fe21f - - UIImgDefaultSocksUUID - - Comment - - Persist - 0 - Type - String - Value - 5748decc-f629-461c-9a36-a35a221fe21f - - UIImgDefaultAlphaUUID - - Comment - - Persist - 0 - Type - String - Value - 5748decc-f629-461c-9a36-a35a221fe21f - - UIImgDefaultUnderwearUUID - - Comment - - Persist - 0 - Type - String - Value - 5748decc-f629-461c-9a36-a35a221fe21f - - StartUpChannelUUID - - Comment - - Persist - 0 - Type - String - Value - B56AF90D-6684-48E4-B1E4-722D3DEB2CB6 - - NearByChatChannelUUID - - Comment - - Persist - 0 - Type - String - Value - E1158BD6-661C-4981-9DAD-4DCBFF062502 - - NotificationChannelUUID - - Comment - - Persist - 0 - Type - String - Value - AEED3193-8709-4693-8558-7452CCA97AE5 - - AlertChannelUUID - - Comment - - Persist - 0 - Type - String - Value - F3E07BC8-A973-476D-8C7F-F3B7293975D1 - - UIImgWhiteUUID - - Comment - - Persist - 0 - Type - String - Value - 5748decc-f629-461c-9a36-a35a221fe21f - - UILineEditorCursorThickness - - Comment - UI Line Editor Cursor Thickness - Persist - 1 - Type - S32 - Value - 2 - - UIMaxComboWidth - - Comment - Maximum width of combo box - Persist - 1 - Type - S32 - Value - 500 - - UIMinimizedWidth - - Comment - Size of UI floater minimized width - Persist - 1 - Type - S32 - Value - 160 - - UIMultiSliderctrlSpacing - - Comment - UI multi slider ctrl spacing - Persist - 1 - Type - S32 - Value - 4 - - UIMultiTrackHeight - - Comment - UI multi track height - Persist - 1 - Type - S32 - Value - 6 - - UIPreeditMarkerBrightness - - Comment - UI Preedit Marker Brightness - Persist - 1 - Type - F32 - Value - 0.4 - - UIPreeditMarkerGap - - Comment - UI Preedit Marker Gap - Persist - 1 - Type - S32 - Value - 1 - - UIPreeditMarkerPosition - - Comment - UI Preedit Marker Position - Persist - 1 - Type - S32 - Value - 4 - - UIPreeditMarkerThickness - - Comment - UI Preedit Marker Thickness - Persist - 1 - Type - S32 - Value - 1 - - UIPreeditStandoutBrightness - - Comment - UI Preedit Standout Brightness - Persist - 1 - Type - F32 - Value - 0.6 - - UIPreeditStandoutGap - - Comment - UI Preedit Standout Gap - Persist - 1 - Type - S32 - Value - 1 - - UIPreeditStandoutPosition - - Comment - UI Preedit Standout Position - Persist - 1 - Type - S32 - Value - 4 - - UIPreeditStandoutThickness - - Comment - UI Preedit Standout Thickness - Persist - 1 - Type - S32 - Value - 2 - - UIResizeBarHeight - - Comment - Size of UI resize bar height - Persist - 1 - Type - S32 - Value - 3 - - UIResizeBarOverlap - - Comment - Size of UI resize bar overlap - Persist - 1 - Type - S32 - Value - 1 - - UIScaleFactor - - Comment - Size of UI relative to default layout on 1024x768 screen - Persist - 1 - Type - F32 - Value - 1.0 - - UIScrollbarSize - - Comment - UI scrollbar size - Persist - 1 - Type - S32 - Value - 15 - - UISliderctrlHeight - - Comment - UI slider ctrl height - Persist - 1 - Type - S32 - Value - 16 - - UISliderctrlSpacing - - Comment - UI slider ctrl spacing - Persist - 1 - Type - S32 - Value - 4 - - UISndAlert - - Comment - Sound file for alerts (uuid for sound asset) - Persist - 1 - Type - String - Value - ed124764-705d-d497-167a-182cd9fa2e6c - - UISndBadKeystroke - - Comment - Sound file for invalid keystroke (uuid for sound asset) - Persist - 1 - Type - String - Value - 2ca849ba-2885-4bc3-90ef-d4987a5b983a - - UISndClick - - Comment - Sound file for mouse click (uuid for sound asset) - Persist - 1 - Type - String - Value - 4c8c3c77-de8d-bde2-b9b8-32635e0fd4a6 - - UISndClickRelease - - Comment - Sound file for mouse button release (uuid for sound asset) - Persist - 1 - Type - String - Value - 4c8c3c77-de8d-bde2-b9b8-32635e0fd4a6 - - UISndDebugSpamToggle - - Comment - Log UI sound effects as they are played - Persist - 1 - Type - Boolean - Value - 0 - - UISndHealthReductionF - - Comment - Sound file for female pain (uuid for sound asset) - Persist - 1 - Type - String - Value - 219c5d93-6c09-31c5-fb3f-c5fe7495c115 - - UISndHealthReductionM - - Comment - Sound file for male pain (uuid for sound asset) - Persist - 1 - Type - String - Value - e057c244-5768-1056-c37e-1537454eeb62 - - UISndHealthReductionThreshold - - Comment - Amount of health reduction required to trigger "pain" sound - Persist - 1 - Type - F32 - Value - 10.0 - - UISndInvalidOp - - Comment - Sound file for invalid operations (uuid for sound asset) - Persist - 1 - Type - String - Value - 4174f859-0d3d-c517-c424-72923dc21f65 - - UISndMoneyChangeDown - - Comment - Sound file for L$ balance increase (uuid for sound asset) - Persist - 1 - Type - String - Value - 104974e3-dfda-428b-99ee-b0d4e748d3a3 - - UISndMoneyChangeThreshold - - Comment - Amount of change in L$ balance required to trigger "money" sound - Persist - 1 - Type - F32 - Value - 50.0 - - UISndMoneyChangeUp - - Comment - Sound file for L$ balance decrease(uuid for sound asset) - Persist - 1 - Type - String - Value - 77a018af-098e-c037-51a6-178f05877c6f - - UISndNewIncomingIMSession - - Comment - Sound file for new instant message session(uuid for sound asset) - Persist - 1 - Type - String - Value - 67cc2844-00f3-2b3c-b991-6418d01e1bb7 - - UISndObjectCreate - - Comment - Sound file for object creation (uuid for sound asset) - Persist - 1 - Type - String - Value - f4a0660f-5446-dea2-80b7-6482a082803c - - UISndObjectDelete - - Comment - Sound file for object deletion (uuid for sound asset) - Persist - 1 - Type - String - Value - 0cb7b00a-4c10-6948-84de-a93c09af2ba9 - - UISndObjectRezIn - - Comment - Sound file for rezzing objects (uuid for sound asset) - Persist - 1 - Type - String - Value - 3c8fc726-1fd6-862d-fa01-16c5b2568db6 - - UISndObjectRezOut - - Comment - Sound file for derezzing objects (uuid for sound asset) - Persist - 1 - Type - String - Value - 00000000-0000-0000-0000-000000000000 - - UISndSnapshot - - Comment - Sound file for taking a snapshot (uuid for sound asset) - Persist - 1 - Type - String - Value - 3d09f582-3851-c0e0-f5ba-277ac5c73fb4 - - UISndStartIM - - Comment - Sound file for starting a new IM session (uuid for sound asset) - Persist - 1 - Type - String - Value - c825dfbc-9827-7e02-6507-3713d18916c1 - - UISndTeleportOut - - Comment - Sound file for teleporting (uuid for sound asset) - Persist - 1 - Type - String - Value - d7a9a565-a013-2a69-797d-5332baa1a947 - - UISndTyping - - Comment - Sound file for starting to type a chat message (uuid for sound asset) - Persist - 1 - Type - String - Value - 5e191c7b-8996-9ced-a177-b2ac32bfea06 - - UISndWindowClose - - Comment - Sound file for closing a window (uuid for sound asset) - Persist - 1 - Type - String - Value - 2c346eda-b60c-ab33-1119-b8941916a499 - - UISndWindowOpen - - Comment - Sound file for opening a window (uuid for sound asset) - Persist - 1 - Type - String - Value - c80260ba-41fd-8a46-768a-6bf236360e3a - - UISpinctrlBtnHeight - - Comment - UI spin control button height - Persist - 1 - Type - S32 - Value - 11 - - UISpinctrlBtnWidth - - Comment - UI spin control button width - Persist - 1 - Type - S32 - Value - 16 - - UISpinctrlDefaultLabelWidth - - Comment - UI spin control default label width - Persist - 1 - Type - S32 - Value - 10 - - UISpinctrlSpacing - - Comment - UI spin control spacing - Persist - 1 - Type - S32 - Value - 2 - - UITabCntrArrowBtnSize - - Comment - UI Tab Container Arrow Button Size - Persist - 1 - Type - S32 - Value - 16 - - UITabCntrvArrowBtnSize - - Comment - UI Tab Container V Arrow Button Size - Persist - 1 - Type - S32 - Value - 16 - - UITabCntrvPad - - Comment - UI Tab Container V Pad - Persist - 1 - Type - S32 - Value - 0 - - UITabCntrButtonPanelOverlap - - Comment - UI Tab Container Button Panel Overlap - Persist - 1 - Type - S32 - Value - 1 - - UITabCntrCloseBtnSize - - Comment - UI Tab Container Close Button Size - Persist - 1 - Type - S32 - Value - 16 - - UITabCntrTabHPad - - Comment - UI Tab Container Tab Horizontal Pad - Persist - 1 - Type - S32 - Value - 4 - - UITabCntrTabPartialWidth - - Comment - UI Tab Container Tab Partial Width - Persist - 1 - Type - S32 - Value - 12 - - UITabCntrVertTabMinWidth - - Comment - UI Tab Container Vertical Tab Minimum Width - Persist - 1 - Type - S32 - Value - 100 - - UITabPadding - - Comment - UI Tab Padding - Persist - 1 - Type - S32 - Value - 15 - - UpdaterServiceSetting - - Comment - Configure updater service. - Persist - 1 - Type - U32 - Value - 3 - - UpdaterServiceCheckPeriod - - Comment - Default period between update checking. - Persist - 1 - Type - U32 - Value - 3600 - - UpdaterServiceURL - - Comment - Default location for the updater service. - Persist - 0 - Type - String - Value - https://update.secondlife.com - - UpdaterServicePath - - Comment - Path on the update server host. - Persist - 0 - Type - String - Value - update - - UpdaterServiceProtocolVersion - - Comment - The update protocol version to use. - Persist - 0 - Type - String - Value - v1.0 - - UploadBakedTexOld - - Comment - Forces the baked texture pipeline to upload using the old method. - Persist - 1 - Type - Boolean - Value - 0 - - UseAltKeyForMenus - - Comment - Access menus via keyboard by tapping Alt - Persist - 1 - Type - Boolean - Value - 0 - - UseChatBubbles - - Comment - Show chat above avatars head in chat bubbles - Persist - 1 - Type - Boolean - Value - 0 - - UseCircuitCodeMaxRetries - - Comment - Max timeout count for the initial UseCircuitCode message - Persist - 0 - Type - S32 - Value - 3 - - UseCircuitCodeTimeout - - Comment - Timeout duration in seconds for the initial UseCircuitCode message - Persist - 0 - Type - F32 - Value - 5.0 - - UseDebugLogin - - Comment - Provides extra control over which grid to connect to - Persist - 1 - Type - Boolean - Value - 0 - - UseDebugMenus - - Comment - Turns on "Debug" menu - Persist - 1 - Type - Boolean - Value - 0 - - UseDefaultColorPicker - - Comment - Use color picker supplied by operating system - Persist - 1 - Type - Boolean - Value - 0 - - UseDisplayNames - - Comment - Use new, changeable, unicode names - Persist - 1 - Type - Boolean - Value - 1 - - UseEnergy - - Comment - - Persist - 0 - Type - Boolean - Value - 1 - - UseExternalBrowser - - Comment - Use default browser when opening web pages instead of in-world browser. - Persist - 1 - Type - Boolean - Value - 1 - - UseFreezeFrame - - Comment - Freeze time when taking snapshots. - Persist - 1 - Type - Boolean - Value - 0 - - UseOcclusion - - Comment - Enable object culling based on occlusion (coverage) by other objects - Persist - 1 - Type - Boolean - Value - 1 - - RenderDelayVBUpdate - - Comment - Delay vertex buffer updates until just before rendering - Persist - 1 - Type - Boolean - Value - 1 - - SpeakerParticipantDefaultOrder - - Comment - Order for displaying speakers in voice controls. 0 = alphabetical. 1 = recent. - Persist - 1 - Type - U32 - Value - 1 - - SpeakerParticipantRemoveDelay - - Comment - Timeout to remove participants who is not in channel before removed from list of active speakers (text/voice chat) - Persist - 1 - Type - F32 - Value - 10.0 - - UseNewWalkRun - - Comment - Replace standard walk/run animations with new ones. - Persist - 1 - Type - Boolean - Value - 1 - - UseStartScreen - - Comment - Whether to load a start screen image or not. - Persist - 1 - Type - Boolean - Value - 1 - - UseWebPagesOnPrims - - Comment - [NOT USED] - Persist - 1 - Type - Boolean - Value - 0 - - UserConnectionPort - - Comment - Port that this client transmits on. - Persist - 1 - Type - U32 - Value - 0 - - UserLogFile - - Comment - User specified log file name. - Persist - 1 - Type - String - Value - - - UserLoginInfo - - Comment - Users loging data. - Persist - 1 - Type - LLSD - Value - - VFSOldSize - - Comment - [DO NOT MODIFY] Controls resizing of local file cache - Persist - 1 - Type - U32 - Value - 0 - - VFSSalt - - Comment - [DO NOT MODIFY] Controls local file caching behavior - Persist - 1 - Type - U32 - Value - 1 - - VectorizeEnable - - Comment - Enable general vector operations and data alignment. - Persist - 1 - Type - Boolean - Value - 0 - - VectorizePerfTest - - Comment - Test SSE/vectorization performance and choose fastest version. - Persist - 1 - Type - Boolean - Value - 1 - - VectorizeProcessor - - Comment - 0=Compiler Default, 1=SSE, 2=SSE2, autodetected - Persist - 0 - Type - U32 - Value - 0 - - VectorizeSkin - - Comment - Enable vector operations for avatar skinning. - Persist - 1 - Type - Boolean - Value - 1 - - VelocityInterpolate - - Comment - Extrapolate object motion from last packet based on received velocity - Persist - 1 - Type - Boolean - Value - 1 - - InterpolationTime - - Comment - How long to extrapolate object motion after last packet received - Persist - 1 - Type - F32 - Value - 3 - - InterpolationPhaseOut - - Comment - Seconds to phase out interpolated motion - Persist - 1 - Type - F32 - Value - 1 - - VerboseLogs - - Comment - Display source file and line number for each log item for debugging purposes - Persist - 1 - Type - Boolean - Value - 0 - - VertexShaderEnable - - Comment - Enable/disable all GLSL shaders (debug) - Persist - 1 - Type - Boolean - Value - 0 - - VivoxAutoPostCrashDumps - - Comment - If true, SLVoice will automatically send crash dumps directly to Vivox. - Persist - 1 - Type - Boolean - Value - 0 - - VivoxDebugLevel - - Comment - Logging level to use when launching the vivox daemon - Persist - 1 - Type - String - Value - -1 - - VivoxDebugSIPURIHostName - - Comment - Hostname portion of vivox SIP URIs (empty string for the default). - Persist - 1 - Type - String - Value - - - VivoxDebugVoiceAccountServerURI - - Comment - URI to the vivox account management server (empty string for the default). - Persist - 1 - Type - String - Value - - - VivoxVoiceHost - - Comment - Client SLVoice host to connect to - Persist - 1 - Type - String - Value - 127.0.0.1 - - VivoxVoicePort - - Comment - Client SLVoice port to connect to - Persist - 1 - Type - U32 - Value - 44125 - - VoiceCallsFriendsOnly - - Comment - Only accept voice calls from residents on your friends list - Persist - 1 - Type - Boolean - Value - 0 - - VoiceCallsRejectAll - - Comment - Silently reject all incoming voice calls. - Persist - 1 - Type - Boolean - Value - 0 - - VoiceDisableMic - - Comment - Completely disable the ability to open the mic. - Persist - 1 - Type - Boolean - Value - 0 - - VoiceEffectExpiryWarningTime - - Comment - How much notice to give of Voice Morph subscriptions expiry, in seconds. - Persist - 1 - Type - S32 - Value - 259200 - - VoiceMorphingEnabled - - Comment - Whether or not to enable Voice Morphs and show the UI. - Persist - 0 - Type - Boolean - Value - 1 - - AutoDisengageMic - - Comment - Automatically turn off the microphone when ending IM calls. - Persist - 1 - Type - Boolean - Value - 1 - - VoiceEarLocation - - Comment - Location of the virtual ear for voice - Persist - 1 - Type - S32 - Value - 0 - - VoiceHost - - Comment - Client SLVoice host to connect to - Persist - 1 - Type - String - Value - 127.0.0.1 - - VoiceImageLevel0 - - Comment - Texture UUID for voice image level 0 - Persist - 1 - Type - String - Value - 041ee5a0-cb6a-9ac5-6e49-41e9320507d5 - - VoiceImageLevel1 - - Comment - Texture UUID for voice image level 1 - Persist - 1 - Type - String - Value - 29de489d-0491-fb00-7dab-f9e686d31e83 - - VoiceImageLevel2 - - Comment - Texture UUID for voice image level 2 - Persist - 1 - Type - String - Value - 29de489d-0491-fb00-7dab-f9e686d31e83 - - VoiceImageLevel3 - - Comment - Texture UUID for voice image level 3 - Persist - 1 - Type - String - Value - 29de489d-0491-fb00-7dab-f9e686d31e83 - - VoiceImageLevel4 - - Comment - Texture UUID for voice image level 4 - Persist - 1 - Type - String - Value - 29de489d-0491-fb00-7dab-f9e686d31e83 - - VoiceImageLevel5 - - Comment - Texture UUID for voice image level 5 - Persist - 1 - Type - String - Value - 29de489d-0491-fb00-7dab-f9e686d31e83 - - VoiceImageLevel6 - - Comment - Texture UUID for voice image level 6 - Persist - 1 - Type - String - Value - 29de489d-0491-fb00-7dab-f9e686d31e83 - - VoiceInputAudioDevice - - Comment - Audio input device to use for voice - Persist - 1 - Type - String - Value - Default - - VoiceLogFile - - Comment - Log file to use when launching the voice daemon - Persist - 1 - Type - String - Value - - - VoiceOutputAudioDevice - - Comment - Audio output device to use for voice - Persist - 1 - Type - String - Value - Default - - VoiceParticipantLeftRemoveDelay - - Comment - Timeout to remove participants who has left Voice chat from the list in Voice Controls Panel - Persist - 1 - Type - S32 - Value - 10 - - VoicePort - - Comment - Client SLVoice port to connect to - Persist - 1 - Type - U32 - Value - 44125 - - WarningsAsChat - - Comment - Display warning messages in chat history - Persist - 1 - Type - Boolean - Value - 0 - - VoiceServerType - - Comment - The type of voice server to connect to. - Persist - 0 - Type - String - Value - vivox - - WLSkyDetail - - Comment - Controls vertex detail on the WindLight sky. Lower numbers will give better performance and uglier skies. - Persist - 1 - Type - U32 - Value - 64 - - WatchdogEnabled - - Comment - Controls whether the thread watchdog timer is activated. - Persist - 0 - Type - S32 - Value - -1 - - WaterEditPresets - - Comment - Whether to be able to edit the water defaults or not - Persist - 1 - Type - Boolean - Value - 0 - - WaterGLFogDensityScale - - Comment - Maps shader water fog density to gl fog density - Persist - 1 - Type - F32 - Value - 0.02 - - WaterGLFogDepthFloor - - Comment - Controls how dark water gl fog can get - Persist - 1 - Type - F32 - Value - 0.25 - - WaterGLFogDepthScale - - Comment - Controls how quickly gl fog gets dark under water - Persist - 1 - Type - F32 - Value - 50.0 - - WellIconFlashCount - - Comment - Number of flashes of IM Well and Notification Well icons after which flashing buttons stay lit up. Requires restart. - Persist - 1 - Type - S32 - Value - 3 - - WellIconFlashPeriod - - Comment - Period at which IM Well and Notification Well icons flash (seconds). Requires restart. - Persist - 1 - Type - F32 - Value - 0.25 - - WindLightUseAtmosShaders - - Comment - Whether to enable or disable WindLight atmospheric shaders. - Persist - 1 - Type - Boolean - Value - 1 - - WindowFullScreen - - Comment - SL viewer window full screen - Persist - 1 - Type - Boolean - Value - 0 - - WindowHeight - - Comment - SL viewer window height - Persist - 1 - Type - S32 - Value - 738 - - WindowMaximized - - Comment - SL viewer window maximized on login - Persist - 1 - Type - Boolean - Value - 0 - - WindowWidth - - Comment - SL viewer window width - Persist - 1 - Type - S32 - Value - 1024 - - WindowX - - Comment - X coordinate of lower left corner of SL viewer window, relative to primary display (pixels) - Persist - 1 - Type - S32 - Value - 10 - - WindowY - - Comment - Y coordinate of lower left corner of SL viewer window, relative to primary display (pixels) - Persist - 1 - Type - S32 - Value - 10 - - XferThrottle - - Comment - Maximum allowable downstream bandwidth for asset transfers (bits per second) - Persist - 1 - Type - F32 - Value - 150000.0 - - ExternalEditor - - Comment - Path to program used to edit LSL scripts and XUI files, e.g.: /usr/bin/gedit --new-window "%s" - Persist - 1 - Type - String - Value - - - YawFromMousePosition - - Comment - Horizontal range over which avatar head tracks mouse position (degrees of head rotation from left of window to right) - Persist - 1 - Type - F32 - Value - 90.0 - - YouAreHereDistance - - Comment - Radius of distance for banner that indicates if the resident is "on" the Place.(meters from avatar to requested place) - Persist - 1 - Type - F32 - Value - 10.0 - - YieldTime - - Comment - Yield some time to the local host. - Persist - 1 - Type - S32 - Value - -1 - - ZoomDirect - - Comment - Map Joystick zoom axis directly to camera zoom. - Persist - 1 - Type - Boolean - Value - 0 - - ZoomTime - - Comment - Time of transition between different camera modes (seconds) - Persist - 1 - Type - F32 - Value - 0.40000000596 - - particlesbeacon - - Comment - Beacon / Highlight particle generators - Persist - 1 - Type - Boolean - Value - 0 - - physicalbeacon - - Comment - Beacon / Highlight physical objects - Persist - 1 - Type - Boolean - Value - 1 - - renderbeacons - - Comment - Beacon / Highlight particle generators - Persist - 1 - Type - Boolean - Value - 0 - - renderhighlights - - Comment - Beacon / Highlight scripted objects with touch function - Persist - 1 - Type - Boolean - Value - 1 - - scriptsbeacon - - Comment - Beacon / Highlight scripted objects - Persist - 1 - Type - Boolean - Value - 0 - - scripttouchbeacon - - Comment - Beacon / Highlight scripted objects with touch function - Persist - 1 - Type - Boolean - Value - 1 - - ShowDeviceSettings - - Comment - Show device settings - Persist - 1 - Type - Boolean - Value - 0 - - SLURLDragNDrop - - Comment - Enable drag and drop of SLURLs onto the viewer - Persist - 1 - Type - Boolean - Value - 1 - - soundsbeacon - - Comment - Beacon / Highlight sound generators - Persist - 1 - Type - Boolean - Value - 0 - - LogTextureDownloadsToViewerLog - - Comment - Send texture download details to the viewer log - Persist - 1 - Type - Boolean - Value - 0 - - LogTextureDownloadsToSimulator - - Comment - Send a digest of texture info to the sim - Persist - 1 - Type - Boolean - Value - 0 - - TextureLoggingThreshold - - Comment - Specifies the byte threshold at which texture download data should be sent to the sim. - Persist - 1 - Type - U32 - Value - 1 - - - - - ShowVoiceChannelPopup - - Comment - Controls visibility of the current voice channel popup above the voice tab - Persist - 1 - Type - Boolean - Value - 0 - - ShowVolumeSettingsPopup - - Comment - Show individual volume slider for voice, sound effects, etc - Persist - 1 - Type - Boolean - Value - 0 - - max_texture_dimension_X - - Comment - Maximum texture width for user uploaded textures - Persist - 1 - Type - S32 - Value - 2048 - - max_texture_dimension_Y - - Comment - Maximum texture height for user uploaded textures - Persist - 1 - Type - S32 - Value - 2048 - - - teleport_offer_invitation_max_length - - Comment - Maximum length of teleport offer invitation line editor. 254 - max_location_url_length(76) = 178 - Persist - 1 - Type - S32 - Value - 178 - - always_showable_floaters - - Comment - Floaters that can be shown despite mouselook mode - Persist - 1 - Type - LLSD - Value - - snapshot - postcard - mini_map - - - LandmarksSortedByDate - - Comment - Reflects landmarks panel sorting order. - Persist - 1 - Type - Boolean - Value - 1 - - OutfitOperationsTimeout - - Comment - Timeout for outfit related operations. - Persist - 1 - Type - S32 - Value - 180 - - HeightUnits - - Comment - Determines which metric units are used: 1(TRUE) for meter and 0(FALSE) for foot. - Persist - 1 - Type - Boolean - Value - 1 - - TipToastMessageLineCount - - Comment - Max line count of text message on tip toast. - Persist - 1 - Type - S32 - Value - 10 - - NotMovingHintTimeout - - Comment - Number of seconds to wait for resident to move before displaying move hint. - Persist - 1 - Type - F32 - Value - 120.0 - - DestinationGuideHintTimeout - - Comment - Number of seconds to wait before telling resident about destination guide. - Persist - 1 - Type - F32 - Value - 1200.0 - - SidePanelHintTimeout - - Comment - Number of seconds to wait before telling resident about side panel. - Persist - 1 - Type - F32 - Value - 300.0 - - GroupMembersSortOrder - - Comment - The order by which group members will be sorted (name|donated|online) - Persist - 1 - Type - String - Value - name - - SessionSettingsFile - - Comment - Settings that are a applied per session (not saved). - Persist - 1 - Type - String - Value - - - UserSessionSettingsFile - - Comment - User settings that are a applied per session (not saved). - Persist - 1 - Type - String - Value - - - OpenSidePanelsInFloaters - - Comment - If true, will always open side panel contents in a floater. - Persist - 1 - Type - Boolean - Value - 0 - - AvatarInspectorTooltipDelay - - Comment - Seconds before displaying avatar inspector tooltip - Persist - 1 - Type - F32 - Value - 0.35 - - ObjectInspectorTooltipDelay - - Comment - Seconds before displaying object inspector tooltip - Persist - 1 - Type - F32 - Value - 0.35 - - SLURLTeleportDirectly - - Comment - Clicking on a slurl will teleport you directly instead of opening places panel - Persist - 1 - Type - Boolean - Value - 0 - - EnableClassifieds - - Comment - Enable creation of new classified ads from web link - Persist - 1 - Type - Boolean - Value - 1 - - EnableGroupInfo - - Comment - Enable viewing and editing of group info from web link - Persist - 1 - Type - Boolean - Value - 1 - - EnablePicks - - Comment - Enable editing of picks from web link - Persist - 1 - Type - Boolean - Value - 1 - - EnableWorldMap - - Comment - Enable opening world map from web link - Persist - 1 - Type - Boolean - Value - 1 - - SearchFromAddressBar - - Comment - Can enter search queries into navigation address bar - Persist - 1 - Type - Boolean - Value - 1 - - LogInventoryDecline - - Comment - Log in system chat whenever an inventory offer is declined - Persist - 1 - Type - Boolean - Value - 1 - - UseHTTPInventory - - Comment - Allow use of http inventory transfers instead of UDP - Persist - 1 - Type - Boolean - Value - 1 - - - + + + + CrashHostUrl + + Comment + A URL pointing to a crash report handler; overrides cluster negotiation to locate crash handler. + Persist + 1 + Type + String + Value + + + AFKTimeout + + Comment + Time before automatically setting AFK (away from keyboard) mode (seconds, 0=never). + Valid values are: 0, 120, 300, 600, 1800 + Persist + 1 + Type + S32 + Value + 300 + + AdminMenu + + Comment + Enable the debug admin menu from the main menu. Note: This will just allow the menu to be shown; this does not grant admin privileges. + Persist + 0 + Type + Boolean + Value + 0 + + ActiveFloaterTransparency + + Comment + Transparency of active floaters (floaters that have focus) + Persist + 1 + Type + F32 + Value + 0.95 + + AdvanceSnapshot + + Comment + Display advanced parameter settings in snaphot interface + Persist + 1 + Type + Boolean + Value + 0 + + AgentPause + + Comment + Ask the simulator to stop updating the agent while enabled + Persist + 0 + Type + Boolean + Value + 0 + + AlertedUnsupportedHardware + + Comment + Set if there's unsupported hardware and we've already done a notification. + Persist + 1 + Type + Boolean + Value + 0 + + AllowMultipleViewers + + Comment + Allow multiple viewers. + Persist + 1 + Type + Boolean + Value + 0 + + AllowTapTapHoldRun + + Comment + Tapping a direction key twice and holding it down makes avatar run + Persist + 1 + Type + Boolean + Value + 1 + + AnimateTextures + + Comment + Enable texture animation (debug) + Persist + 1 + Type + Boolean + Value + 1 + + AnimationDebug + + Comment + Show active animations in a bubble above avatars head + Persist + 1 + Type + Boolean + Value + 0 + + AppearanceCameraMovement + + Comment + When entering appearance editing mode, camera zooms in on currently selected portion of avatar + Persist + 1 + Type + Boolean + Value + 1 + + ApplyColorImmediately + + Comment + Preview selections in color picker immediately + Persist + 1 + Type + Boolean + Value + 1 + + ApplyTextureImmediately + + Comment + Preview selections in texture picker immediately + Persist + 1 + Type + Boolean + Value + 1 + + ArrowKeysAlwaysMove + + Comment + While cursor is in chat entry box, arrow keys still control your avatar + Persist + 1 + Type + Boolean + Value + 0 + + AskedAboutCrashReports + + Comment + Turns off dialog asking if you want to enable crash reporting + Persist + 1 + Type + Boolean + Value + 0 + + AuctionShowFence + + Comment + When auctioning land, include parcel boundary marker in snapshot + Persist + 1 + Type + Boolean + Value + 1 + + AudioLevelAmbient + + Comment + Audio level of environment sounds + Persist + 1 + Type + F32 + Value + 0.5 + + AudioLevelDoppler + + Comment + Scale of doppler effect on moving audio sources (1.0 = normal, <1.0 = diminished doppler effect, >1.0 = enhanced doppler effect) + Persist + 1 + Type + F32 + Value + 1.0 + + AudioLevelMaster + + Comment + Master audio level, or overall volume + Persist + 1 + Type + F32 + Value + 1.0 + + AudioLevelMedia + + Comment + Audio level of Quicktime movies + Persist + 1 + Type + F32 + Value + 0.5 + + AudioLevelMic + + Comment + Audio level of microphone input + Persist + 1 + Type + F32 + Value + 1.0 + + AudioLevelMusic + + Comment + Audio level of streaming music + Persist + 1 + Type + F32 + Value + 0.5 + + AudioLevelRolloff + + Comment + Controls the distance-based dropoff of audio volume (fraction or multiple of default audio rolloff) + Persist + 1 + Type + F32 + Value + 1.0 + + AudioLevelSFX + + Comment + Audio level of in-world sound effects + Persist + 1 + Type + F32 + Value + 0.5 + + AudioLevelUI + + Comment + Audio level of UI sound effects + Persist + 1 + Type + F32 + Value + 0.5 + + AudioLevelVoice + + Comment + Audio level of voice chat + Persist + 1 + Type + F32 + Value + 0.5 + + AudioLevelWind + + Comment + Audio level of wind noise when standing still + Persist + 1 + Type + F32 + Value + 0.5 + + AudioStreamingMedia + + Comment + Enable streaming + Persist + 1 + Type + Boolean + Value + 1 + + AudioStreamingMusic + + Comment + Enable streaming audio + Persist + 1 + Type + Boolean + Value + 1 + + AuditTexture + + Comment + Enable texture auditting. + Persist + 1 + Type + Boolean + Value + 0 + + AutoAcceptNewInventory + + Comment + Automatically accept new notecards/textures/landmarks + Persist + 1 + Type + Boolean + Value + 0 + + AutoLeveling + + Comment + Keep Flycam level. + Persist + 1 + Type + Boolean + Value + 1 + + AutoLoadWebProfiles + + Comment + Automatically load ALL profile webpages without asking first. + Persist + 1 + Type + Boolean + Value + 0 + + AutoLogin + + Comment + Login automatically using last username/password combination + Persist + 0 + Type + Boolean + Value + 0 + + AutoMimeDiscovery + + Comment + Enable viewer mime type discovery of media URLs + Persist + 1 + Type + Boolean + Value + 0 + + AutoPilotLocksCamera + + Comment + Keep camera position locked when avatar walks to selected position + Persist + 1 + Type + Boolean + Value + 0 + + AutoSnapshot + + Comment + Update snapshot when camera stops moving, or any parameter changes + Persist + 1 + Type + Boolean + Value + 0 + + AutomaticFly + + Comment + Fly by holding jump key or using "Fly" command (FALSE = fly by using "Fly" command only) + Persist + 1 + Type + Boolean + Value + 1 + + AvalinePhoneSeparator + + Comment + Separator of phone parts to have Avaline numbers human readable in Voice Control Panel + Persist + 1 + Type + String + Value + - + + AvatarAxisDeadZone0 + + Comment + Avatar axis 0 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + AvatarAxisDeadZone1 + + Comment + Avatar axis 1 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + AvatarAxisDeadZone2 + + Comment + Avatar axis 2 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + AvatarAxisDeadZone3 + + Comment + Avatar axis 3 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + AvatarAxisDeadZone4 + + Comment + Avatar axis 4 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + AvatarAxisDeadZone5 + + Comment + Avatar axis 5 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + AvatarAxisScale0 + + Comment + Avatar axis 0 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + AvatarAxisScale1 + + Comment + Avatar axis 1 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + AvatarAxisScale2 + + Comment + Avatar axis 2 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + AvatarAxisScale3 + + Comment + Avatar axis 3 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + AvatarAxisScale4 + + Comment + Avatar axis 4 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + AvatarAxisScale5 + + Comment + Avatar axis 5 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + AvatarBacklight + + Comment + Add rim lighting to avatar rendering to approximate shininess of skin + Persist + 1 + Type + Boolean + Value + 1 + + AvatarFeathering + + Comment + Avatar feathering (less is softer) + Persist + 1 + Type + F32 + Value + 16.0 + + AvatarPickerSortOrder + + Comment + Specifies sort key for textures in avatar picker (+0 = name, +1 = date, +2 = folders always by name, +4 = system folders to top) + Persist + 1 + Type + U32 + Value + 2 + + AvatarPickerURL + + Comment + Avatar picker contents + Persist + 1 + Type + String + Value + + + AvatarBakedTextureUploadTimeout + + Comment + Specifes the maximum time in seconds to wait before sending your baked textures for avatar appearance. Set to 0 to disable and wait until all baked textures are at highest resolution. + Persist + 1 + Type + U32 + Value + 60 + + AvatarBakedLocalTextureUpdateTimeout + + Comment + Specifes the maximum time in seconds to wait before updating your appearance during appearance mode. + Persist + 1 + Type + U32 + Value + 10 + + + AvatarSex + + Comment + + Persist + 0 + Type + U32 + Value + 0 + + BackgroundYieldTime + + Comment + Amount of time to yield every frame to other applications when SL is not the foreground window (milliseconds) + Persist + 1 + Type + S32 + Value + 40 + + BottomPanelNew + + Comment + Enable the new bottom panel + Persist + 1 + Type + Boolean + Value + 0 + + BrowserHomePage + + Comment + [NOT USED] + Persist + 1 + Type + String + Value + http://www.secondlife.com + + BrowserIgnoreSSLCertErrors + + Comment + FOR TESTING ONLY: Tell the built-in web browser to ignore SSL cert errors. + Persist + 1 + Type + Boolean + Value + 0 + + BlockAvatarAppearanceMessages + + Comment + Ignores appearance messages (for simulating Ruth) + Persist + 1 + Type + Boolean + Value + 0 + + BlockSomeAvatarAppearanceVisualParams + + Comment + Drop around 50% of VisualParam occurances in appearance messages (for simulating Ruth) + Persist + 1 + Type + Boolean + Value + 0 + + BrowserProxyAddress + + Comment + Address for the Web Proxy] + Persist + 1 + Type + String + Value + + + BrowserProxyEnabled + + Comment + Use Web Proxy + Persist + 1 + Type + Boolean + Value + 0 + + BrowserProxyExclusions + + Comment + [NOT USED] + Persist + 1 + Type + String + Value + + + BrowserProxyPort + + Comment + Port for Web Proxy + Persist + 1 + Type + S32 + Value + 3128 + + BrowserProxySocks45 + + Comment + [NOT USED] + Persist + 1 + Type + S32 + Value + 5 + + BuildAxisDeadZone0 + + Comment + Build axis 0 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + BuildAxisDeadZone1 + + Comment + Build axis 1 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + BuildAxisDeadZone2 + + Comment + Build axis 2 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + BuildAxisDeadZone3 + + Comment + Build axis 3 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + BuildAxisDeadZone4 + + Comment + Build axis 4 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + BuildAxisDeadZone5 + + Comment + Build axis 5 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + BuildAxisScale0 + + Comment + Build axis 0 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + BuildAxisScale1 + + Comment + Build axis 1 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + BuildAxisScale2 + + Comment + Build axis 2 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + BuildAxisScale3 + + Comment + Build axis 3 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + BuildAxisScale4 + + Comment + Build axis 4 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + BuildAxisScale5 + + Comment + Build axis 5 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + BuildFeathering + + Comment + Build feathering (less is softer) + Persist + 1 + Type + F32 + Value + 16.0 + + BulkChangeIncludeAnimations + + Comment + Bulk permission changes affect animations + Persist + 1 + Type + Boolean + Value + 1 + + BulkChangeIncludeBodyParts + + Comment + Bulk permission changes affect body parts + Persist + 1 + Type + Boolean + Value + 1 + + BulkChangeIncludeClothing + + Comment + Bulk permission changes affect clothing + Persist + 1 + Type + Boolean + Value + 1 + + BulkChangeIncludeGestures + + Comment + Bulk permission changes affect gestures + Persist + 1 + Type + Boolean + Value + 1 + + BulkChangeIncludeNotecards + + Comment + Bulk permission changes affect notecards + Persist + 1 + Type + Boolean + Value + 1 + + BulkChangeIncludeObjects + + Comment + Bulk permission changes affect objects + Persist + 1 + Type + Boolean + Value + 1 + + BulkChangeIncludeScripts + + Comment + Bulk permission changes affect scripts + Persist + 1 + Type + Boolean + Value + 1 + + BulkChangeIncludeSounds + + Comment + Bulk permission changes affect sounds + Persist + 1 + Type + Boolean + Value + 1 + + BulkChangeIncludeTextures + + Comment + Bulk permission changes affect textures + Persist + 1 + Type + Boolean + Value + 1 + + BulkChangeEveryoneCopy + + Comment + Bulk changed objects can be copied by everyone + Persist + 1 + Type + Boolean + Value + 0 + + BulkChangeNextOwnerCopy + + Comment + Bulk changed objects can be copied by next owner + Persist + 1 + Type + Boolean + Value + 0 + + BulkChangeNextOwnerModify + + Comment + Bulk changed objects can be modified by next owner + Persist + 1 + Type + Boolean + Value + 0 + + BulkChangeNextOwnerTransfer + + Comment + Bulk changed objects can be resold or given away by next owner + Persist + 1 + Type + Boolean + Value + 0 + + BulkChangeShareWithGroup + + Comment + Bulk changed objects are shared with the currently active group + Persist + 1 + Type + Boolean + Value + 0 + + ButtonFlashCount + + Comment + Number of flashes after which flashing buttons stay lit up + Persist + 1 + Type + S32 + Value + 8 + + ButtonFlashRate + + Comment + Frequency at which buttons flash (hz) + Persist + 1 + Type + F32 + Value + 1.25 + + ButtonHPad + + Comment + Default horizontal spacing between buttons (pixels) + Persist + 1 + Type + S32 + Value + 4 + + ButtonHeight + + Comment + Default height for normal buttons (pixels) + Persist + 1 + Type + S32 + Value + 23 + + ButtonHeightSmall + + Comment + Default height for small buttons (pixels) + Persist + 1 + Type + S32 + Value + 23 + + CacheLocation + + Comment + Controls the location of the local disk cache + Persist + 1 + Type + String + Value + + + CacheLocationTopFolder + + Comment + Controls the top folder location of the the local disk cache + Persist + 1 + Type + String + Value + + + CacheNumberOfRegionsForObjects + + Comment + Controls number of regions to be cached for objects. + Persist + 1 + Type + U32 + Value + 128 + + CacheSize + + Comment + Controls amount of hard drive space reserved for local file caching in MB + Persist + 1 + Type + U32 + Value + 512 + + CacheValidateCounter + + Comment + Used to distribute cache validation + Persist + 1 + Type + U32 + Value + 0 + + CameraMouseWheelZoom + + Comment + Camera zooms in and out with mousewheel + Persist + 1 + Type + S32 + Value + 5 + + CameraAngle + + Comment + Camera field of view angle (Radians) + Persist + 1 + Type + F32 + Value + 1.047197551 + + CameraOffset + + Comment + Render with camera offset from view frustum (rendering debug) + Persist + 1 + Type + Boolean + Value + 0 + + CameraOffsetBuild + + Comment + Default camera position relative to focus point when entering build mode + Persist + 1 + Type + Vector3 + Value + + -6.0 + 0.0 + 6.0 + + + CameraOffsetRearView + + Comment + Initial camera offset from avatar in Rear View + Persist + 1 + Type + Vector3 + Value + + -3.0 + 0.0 + 0.75 + + + CameraOffsetFrontView + + Comment + Initial camera offset from avatar in Front View + Persist + 1 + Type + Vector3 + Value + + 2.2 + 0.0 + 0.0 + + + CameraOffsetGroupView + + Comment + Initial camera offset from avatar in Group View + Persist + 1 + Type + Vector3 + Value + + -1.0 + 0.7 + 0.5 + + + CameraOffsetScale + + Comment + Scales the default offset + Persist + 1 + Type + F32 + Value + 1.0 + + CameraPosOnLogout + + Comment + Camera position when last logged out (global coordinates) + Persist + 1 + Type + Vector3D + Value + + 0.0 + 0.0 + 0.0 + + + CameraPositionSmoothing + + Comment + Smooths camera position over time + Persist + 1 + Type + F32 + Value + 1.0 + + CameraPreset + + Comment + Preset camera position - view (0 - rear, 1 - front, 2 - group) + Persist + 1 + Type + U32 + Value + 0 + + CertStore + + Comment + Specifies the Certificate Store for certificate trust verification + Persist + 1 + Type + String + Value + default + + ChatBarStealsFocus + + Comment + Whenever keyboard focus is removed from the UI, and the chat bar is visible, the chat bar takes focus + Persist + 1 + Type + Boolean + Value + 1 + + LetterKeysFocusChatBar + + Comment + When printable characters keys (possibly with Shift held) are pressed, the chatbar takes focus + Persist + 1 + Type + S32 + Value + 0 + + ChatBubbleOpacity + + Comment + Opacity of chat bubble background (0.0 = completely transparent, 1.0 = completely opaque) + Persist + 1 + Type + F32 + Value + 0.5 + + ChatFontSize + + Comment + Size of chat text in chat console (0 = small, 1 = big) + Persist + 1 + Type + S32 + Value + 1 + + ChatFullWidth + + Comment + Chat console takes up full width of SL window + Persist + 1 + Type + Boolean + Value + 1 + + ChatHistoryTornOff + + Comment + Show chat history window separately from Communicate window. + Persist + 1 + Type + Boolean + Value + 0 + + ChatOnlineNotification + + Comment + Provide notifications for when friend log on and off of SL + Persist + 1 + Type + Boolean + Value + 1 + + ChatPersistTime + + Comment + Time for which chat stays visible in console (seconds) + Persist + 1 + Type + F32 + Value + 20.0 + + ChatShowTimestamps + + Comment + Show timestamps in chat + Persist + 1 + Type + Boolean + Value + 1 + + ChatVisible + + Comment + Chat bar is visible + Persist + 1 + Type + Boolean + Value + 1 + + ChatWindow + + Comment + Show chat in multiple windows(by default) or in one multi-tabbed window(requires restart) + Persist + 1 + Type + S32 + Value + 0 + + CheesyBeacon + + Comment + Enable cheesy beacon effects + Persist + 1 + Type + Boolean + Value + 0 + + ClientSettingsFile + + Comment + Client settings file name (per install). + Persist + 0 + Type + String + Value + + + CloseChatOnReturn + + Comment + Close chat after hitting return + Persist + 1 + Type + Boolean + Value + 0 + + CloseSnapshotOnKeep + + Comment + Close snapshot window after saving snapshot + Persist + 1 + Type + Boolean + Value + 1 + + CmdLineDisableVoice + + Comment + Disable Voice. + Persist + 0 + Type + Boolean + Value + 0 + + CmdLineGridChoice + + Comment + The user's grid choice or ip address. + Persist + 0 + Type + String + Value + + + CmdLineHelperURI + + Comment + Command line specified helper web CGI prefix to use. + Persist + 0 + Type + String + Value + + + CmdLineLoginURI + + Comment + Command line specified login server and CGI prefix to use. + Persist + 0 + Type + LLSD + Value + + + + + CompressSnapshotsToDisk + + Comment + Compress snapshots saved to disk (Using JPEG 2000) + Persist + 1 + Type + Boolean + Value + 0 + + ConnectAsGod + + Comment + Log in a god if you have god access. + Persist + 1 + Type + Boolean + Value + 0 + + ConnectionPort + + Comment + Custom connection port number + Persist + 1 + Type + U32 + Value + 13000 + + ConnectionPortEnabled + + Comment + Use the custom connection port? + Persist + 1 + Type + Boolean + Value + 0 + + ConsoleBackgroundOpacity + + Comment + Opacity of chat console (0.0 = completely transparent, 1.0 = completely opaque) + Persist + 1 + Type + F32 + Value + 0.700 + + ConsoleBufferSize + + Comment + Size of chat console history (lines of chat) + Persist + 1 + Type + S32 + Value + 40 + + ConsoleMaxLines + + Comment + Max number of lines of chat text visible in console. + Persist + 1 + Type + S32 + Value + 40 + + ContactsTornOff + + Comment + Show contacts window separately from Communicate window. + Persist + 1 + Type + Boolean + Value + 0 + + CookiesEnabled + + Comment + Accept cookies from Web sites? + Persist + 1 + Type + Boolean + Value + 1 + + BrowserJavascriptEnabled + + Comment + Enable Javascript in the built-in Web browser? + Persist + 1 + Type + Boolean + Value + 1 + + BrowserPluginsEnabled + + Comment + Enable Web plugins in the built-in Web browser? + Persist + 1 + Type + Boolean + Value + 1 + + ChatBarCustomWidth + + Comment + Stores customized width of chat bar. + Persist + 1 + Type + S32 + Value + 0 + + CreateToolCopyCenters + + Comment + + Persist + 0 + Type + Boolean + Value + 1 + + CreateToolCopyRotates + + Comment + + Persist + 0 + Type + Boolean + Value + 0 + + CreateToolCopySelection + + Comment + + Persist + 0 + Type + Boolean + Value + 0 + + CreateToolKeepSelected + + Comment + After using create tool, keep the create tool active + Persist + 1 + Type + Boolean + Value + 0 + + Cursor3D + + Comment + Treat Joystick values as absolute positions (not deltas). + Persist + 1 + Type + Boolean + Value + 1 + + CurrentGrid + + Comment + Currently Selected Grid + Persist + 1 + Type + String + Value + + + CustomServer + + Comment + Specifies IP address or hostname of grid to which you connect + Persist + 1 + Type + String + Value + + + DebugAvatarRezTime + + Comment + Display times for avatars to resolve. + Persist + 1 + Type + Boolean + Value + 0 + + DebugAvatarLocalTexLoadedTime + + Comment + Display time for loading avatar local textures. + Persist + 1 + Type + Boolean + Value + 0 + + DebugBeaconLineWidth + + Comment + Size of lines for Debug Beacons + Persist + 1 + Type + S32 + Value + 1 + + DebugInventoryFilters + + Comment + Turn on debugging display for inventory filtering + Persist + 1 + Type + Boolean + Value + 0 + + DebugPermissions + + Comment + Log permissions for selected inventory items + Persist + 1 + Type + Boolean + Value + 0 + + DebugPluginDisableTimeout + + Comment + Disable the code which watches for plugins that are crashed or hung + Persist + 1 + Type + Boolean + Value + 0 + + DebugShowColor + + Comment + Show color under cursor + Persist + 1 + Type + Boolean + Value + 0 + + DebugShowMemory + + Comment + Show Total Allocated Memory + Persist + 1 + Type + Boolean + Value + 0 + + DebugShowRenderInfo + + Comment + Show depth buffer contents + Persist + 1 + Type + Boolean + Value + 0 + + DebugShowRenderMatrices + + Comment + Display values of current view and projection matrices. + Persist + 1 + Type + Boolean + Value + 0 + + DebugShowTextureInfo + + Comment + Show inertested texture info + Persist + 1 + Type + Boolean + Value + 0 + + DebugShowTime + + Comment + Show time info + Persist + 1 + Type + Boolean + Value + 0 + + DebugShowXUINames + + Comment + Show tooltips with XUI path to widget + Persist + 0 + Type + Boolean + Value + 0 + + DebugStatModeFPS + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeBandwidth + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModePacketLoss + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatMode + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeKTrisDrawnFr + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeKTrisDrawnSec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeTotalObjs + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeNewObjs + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeTextureCount + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeRawCount + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeGLMem + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeFormattedMem + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeRawMem + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeBoundMem + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModePacketsIn + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModePacketsOut + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeObjects + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeTexture + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeAsset + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeLayers + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeActualIn + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeActualOut + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeVFSPendingOps + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeTimeDialation + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimFPS + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModePhysicsFPS + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModePinnedObjects + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeLowLODObjects + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeMemoryAllocated + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeAgentUpdatesSec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeMainAgents + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeChildAgents + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimObjects + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimActiveObjects + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimActiveScripts + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimScriptEvents + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimInPPS + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimOutPPS + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimPendingDownloads + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + SimPendingUploads + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimTotalUnackedBytes + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimFrameMsec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimNetMsec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimSimPhysicsMsec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimSimOtherMsec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimAgentMsec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimImagesMsec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimScriptMsec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimSpareMsec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimSimPhysicsStepMsec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimSimPhysicsShapeUpdateMsec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimSimPhysicsOtherMsec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimSleepMsec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugStatModeSimPumpIOMsec + + Comment + Mode of stat in Statistics floater + Persist + 1 + Type + S32 + Value + -1 + + DebugViews + + Comment + Display debugging info for views. + Persist + 1 + Type + Boolean + Value + 0 + + DebugWindowProc + + Comment + Log windows messages + Persist + 1 + Type + Boolean + Value + 0 + + DefaultFemaleAvatar + + Comment + Default Female Avatar + Persist + 1 + Type + String + Value + Female Shape & Outfit + + DefaultMaleAvatar + + Comment + Default Male Avatar + Persist + 1 + Type + String + Value + Male Shape & Outfit + + + DefaultObjectTexture + + Comment + Texture used as 'Default' in texture picker. (UUID texture reference) + Persist + 1 + Type + String + Value + 89556747-24cb-43ed-920b-47caed15465f + + DefaultUploadCost + + Comment + Default sound/image/file upload cost(in case economy data is not available). + Persist + 1 + Type + U32 + Value + 10 + + DestinationGuideURL + + Comment + Destination guide contents + Persist + 1 + Type + String + Value + + + DisableCameraConstraints + + Comment + Disable the normal bounds put on the camera by avatar position + Persist + 1 + Type + Boolean + Value + 0 + + DisableMouseWarp + + Comment + Disable warping of the mouse to the center of the screen during alt-zoom and mouse look. Useful with certain input devices, mouse sharing programs like Synergy, or running under Parallels. + Persist + 1 + Type + Boolean + Value + 0 + + DisableExternalBrowser + + Comment + Disable opening an external browser. + Persist + 1 + Type + Boolean + Value + 0 + + DisableRendering + + Comment + Disable GL rendering and GUI (load testing) + Persist + 1 + Type + Boolean + Value + 0 + + DisableTextHyperlinkActions + + Comment + Disable highlighting and linking of URLs in XUI text boxes + Persist + 1 + Type + Boolean + Value + 0 + + DisableVerticalSync + + Comment + Update frames as fast as possible (FALSE = update frames between display scans) + Persist + 1 + Type + Boolean + Value + 1 + + EnableGroupChatPopups + + Comment + Enable Incoming Group Chat Popups + Persist + 1 + Type + Boolean + Value + 1 + + EnableIMChatPopups + + Comment + Enable Incoming IM Chat Popups + Persist + 1 + Type + Boolean + Value + 1 + + DisplayAvatarAgentTarget + + Comment + Show avatar positioning locators (animation debug) + Persist + 1 + Type + Boolean + Value + 0 + + DisplayChat + + Comment + Display Latest Chat message on LCD + Persist + 1 + Type + Boolean + Value + 1 + + DisplayDebug + + Comment + Display Network Information on LCD + Persist + 1 + Type + Boolean + Value + 1 + + DisplayDebugConsole + + Comment + Display Console Debug Information on LCD + Persist + 1 + Type + Boolean + Value + 1 + + DisplayIM + + Comment + Display Latest IM message on LCD + Persist + 1 + Type + Boolean + Value + 1 + + DisplayLinden + + Comment + Display Account Information on LCD + Persist + 1 + Type + Boolean + Value + 1 + + DisplayRegion + + Comment + Display Location information on LCD + Persist + 1 + Type + Boolean + Value + 1 + + DisplayTimecode + + Comment + Display timecode on screen + Persist + 1 + Type + Boolean + Value + 0 + + Disregard128DefaultDrawDistance + + Comment + Whether to use the auto default to 128 draw distance + Persist + 1 + Type + Boolean + Value + 1 + + Disregard96DefaultDrawDistance + + Comment + Whether to use the auto default to 96 draw distance + Persist + 1 + Type + Boolean + Value + 1 + + ClickActionBuyEnabled + + Comment + Enable click to buy actions in tool pie menu + Persist + 1 + Type + Boolean + Value + 1 + + ClickActionPayEnabled + + Comment + Enable click to pay actions in tool pie menu + Persist + 1 + Type + Boolean + Value + 1 + + DoubleClickAutoPilot + + Comment + Enable double-click auto pilot + Persist + 1 + Type + Boolean + Value + 0 + + DoubleClickTeleport + + Comment + Enable double-click to teleport where allowed + Persist + 1 + Type + Boolean + Value + 0 + + DoubleClickShowWorldMap + + Comment + Enable double-click to show world map from mini map + Persist + 1 + Type + Boolean + Value + 1 + + DragAndDropToolTipDelay + + Comment + Seconds before displaying tooltip when performing drag and drop operation + Persist + 1 + Type + F32 + Value + 0.10000000149 + + DragAndDropDistanceThreshold + + Comment + Number of pixels that mouse should move before triggering drag and drop mode + Persist + 1 + Type + S32 + Value + 3 + + DropShadowButton + + Comment + Drop shadow width for buttons (pixels) + Persist + 1 + Type + S32 + Value + 2 + + DropShadowFloater + + Comment + Drop shadow width for floaters (pixels) + Persist + 1 + Type + S32 + Value + 5 + + DropShadowSlider + + Comment + Drop shadow width for sliders (pixels) + Persist + 1 + Type + S32 + Value + 3 + + DropShadowTooltip + + Comment + Drop shadow width for tooltips (pixels) + Persist + 1 + Type + S32 + Value + 4 + + DumpVFSCaches + + Comment + Dump VFS caches on startup. + Persist + 1 + Type + Boolean + Value + 0 + + DynamicCameraStrength + + Comment + Amount camera lags behind avatar motion (0 = none, 30 = avatar velocity) + Persist + 1 + Type + F32 + Value + 2.0 + + EditCameraMovement + + Comment + When entering build mode, camera moves up above avatar + Persist + 1 + Type + Boolean + Value + 0 + + EditLinkedParts + + Comment + Select individual parts of linked objects + Persist + 0 + Type + Boolean + Value + 0 + + EffectScriptChatParticles + + Comment + 1 = normal behavior, 0 = disable display of swirling lights when scripts communicate + Persist + 1 + Type + Boolean + Value + 1 + + EnableGrab + + Comment + Use Ctrl+mouse to grab and manipulate objects + Persist + 1 + Type + Boolean + Value + 1 + + EnableAltZoom + + Comment + Use Alt+mouse to look at and zoom in on objects + Persist + 1 + Type + Boolean + Value + 1 + + EnableMouselook + + Comment + Allow first person perspective and mouse control of camera + Persist + 1 + Type + Boolean + Value + 1 + + EnableRippleWater + + Comment + Whether to use ripple water shader or not + Persist + 1 + Type + Boolean + Value + 1 + + EnableTextureAtlas + + Comment + Whether to use texture atlas or not + Persist + 1 + Type + Boolean + Value + 0 + + EnableUIHints + + Comment + Toggles UI hint popups + Persist + 1 + Type + Boolean + Value + 1 + + EnableVoiceChat + + Comment + Enable talking to other residents with a microphone + Persist + 1 + Type + Boolean + Value + 1 + + EnergyFromTop + + Comment + + Persist + 0 + Type + S32 + Value + 20 + + EnergyHeight + + Comment + + Persist + 0 + Type + S32 + Value + 40 + + EnergyWidth + + Comment + + Persist + 0 + Type + S32 + Value + 175 + + EventURL + + Comment + URL for Event website, displayed in the event floater + Persist + 0 + Type + String + Value + http://events.secondlife.com/viewer/embed/event/ + + EveryoneCopy + + Comment + Everyone can copy the newly created objects + Persist + 1 + Type + Boolean + Value + 0 + + FeatureManagerHTTPTable + + Comment + Base directory for HTTP feature/gpu table fetches + Persist + 1 + Type + String + Value + http://viewer-settings.secondlife.com + + FPSLogFrequency + + Comment + Seconds between display of FPS in log (0 for never) + Persist + 1 + Type + F32 + Value + 10.0 + + FilterItemsPerFrame + + Comment + Maximum number of inventory items to match against search filter every frame (lower to increase framerate while searching, higher to improve search speed) + Persist + 1 + Type + S32 + Value + 500 + + FindLandArea + + Comment + Enables filtering of land search results by area + Persist + 1 + Type + Boolean + Value + 0 + + FindLandPrice + + Comment + Enables filtering of land search results by price + Persist + 1 + Type + Boolean + Value + 1 + + FindLandType + + Comment + Controls which type of land you are searching for in Find Land interface ("All", "Auction", "For Sale") + Persist + 1 + Type + String + Value + All + + FindPeopleOnline + + Comment + Limits people search to only users who are logged on + Persist + 1 + Type + Boolean + Value + 1 + + FindPlacesPictures + + Comment + Display only results of find places that have pictures + Persist + 1 + Type + Boolean + Value + 1 + + FirstLoginThisInstall + + Comment + Specifies that you have not successfully logged in since you installed the latest update + Persist + 1 + Type + Boolean + Value + 1 + + FirstName + + Comment + Login first name + Persist + 1 + Type + String + Value + + + FirstPersonAvatarVisible + + Comment + Display avatar and attachments below neck while in mouselook + Persist + 1 + Type + Boolean + Value + 0 + + FirstRunThisInstall + + Comment + Specifies that you have not run the viewer since you installed the latest update + Persist + 1 + Type + Boolean + Value + 1 + + FirstSelectedDisabledPopups + + Comment + Return false if there is not disabled popup selected in the list of floater preferences popups + Persist + 0 + Type + Boolean + Value + 0 + + FirstSelectedEnabledPopups + + Comment + Return false if there is not enable popup selected in the list of floater preferences popups + Persist + 0 + Type + Boolean + Value + 0 + + FixedWeather + + Comment + Weather effects do not change over time + Persist + 1 + Type + Boolean + Value + 0 + + FloaterActiveSpeakersSortAscending + + Comment + Whether to sort up or down + Persist + 1 + Type + Boolean + Value + 1 + + FloaterActiveSpeakersSortColumn + + Comment + Column name to sort on + Persist + 1 + Type + String + Value + speaking_status + + FloaterMapNorth + + Comment + Floater Map North Label + Persist + 1 + Type + String + Value + N + + FloaterMapNorthEast + + Comment + Floater Map North-East Label + Persist + 1 + Type + String + Value + NE + + FloaterMapNorthWest + + Comment + Floater Map North-West Label + Persist + 1 + Type + String + Value + NW + + FloaterMapEast + + Comment + Floater Map East Label + Persist + 1 + Type + String + Value + E + + FloaterMapWest + + Comment + Floater Map West Label + Persist + 1 + Type + String + Value + W + + FloaterMapSouth + + Comment + Floater Map South Label + Persist + 1 + Type + String + Value + S + + FloaterMapSouthEast + + Comment + Floater Map South-East Label + Persist + 1 + Type + String + Value + SE + + FloaterMapSouthWest + + Comment + Floater Map South-West Label + Persist + 1 + Type + String + Value + SW + + + FloaterStatisticsRect + + Comment + Rectangle for chat history + Persist + 1 + Type + Rect + Value + + 0 + 400 + 250 + 0 + + + FlycamAbsolute + + Comment + Treat Flycam values as absolute positions (not deltas). + Persist + 1 + Type + Boolean + Value + 0 + + FlycamAxisDeadZone0 + + Comment + Flycam axis 0 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + FlycamAxisDeadZone1 + + Comment + Flycam axis 1 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + FlycamAxisDeadZone2 + + Comment + Flycam axis 2 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + FlycamAxisDeadZone3 + + Comment + Flycam axis 3 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + FlycamAxisDeadZone4 + + Comment + Flycam axis 4 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + FlycamAxisDeadZone5 + + Comment + Flycam axis 5 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + FlycamAxisDeadZone6 + + Comment + Flycam axis 6 dead zone. + Persist + 1 + Type + F32 + Value + 0.1 + + FlycamAxisScale0 + + Comment + Flycam axis 0 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + FlycamAxisScale1 + + Comment + Flycam axis 1 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + FlycamAxisScale2 + + Comment + Flycam axis 2 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + FlycamAxisScale3 + + Comment + Flycam axis 3 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + FlycamAxisScale4 + + Comment + Flycam axis 4 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + FlycamAxisScale5 + + Comment + Flycam axis 5 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + FlycamAxisScale6 + + Comment + Flycam axis 6 scaler. + Persist + 1 + Type + F32 + Value + 1.0 + + FlycamBuildModeScale + + Comment + Scale factor to apply to flycam movements when in build mode. + Persist + 1 + Type + F32 + Value + 1.0 + + FlycamFeathering + + Comment + Flycam feathering (less is softer) + Persist + 1 + Type + F32 + Value + 16.0 + + FlycamZoomDirect + + Comment + Map flycam zoom axis directly to camera zoom. + Persist + 1 + Type + Boolean + Value + 0 + + FlyingAtExit + + Comment + Was flying when last logged out, so fly when logging in + Persist + 1 + Type + Boolean + Value + 0 + + FocusOffsetRearView + + Comment + Initial focus point offset relative to avatar for the camera preset Rear View (x-axis is forward) + Persist + 1 + Type + Vector3D + Value + + 1.0 + 0.0 + 1.0 + + + FocusOffsetFrontView + + Comment + Initial focus point offset relative to avatar for the camera preset Front View + Persist + 1 + Type + Vector3D + Value + + 0.0 + 0.0 + 0.0 + + + FocusOffsetGroupView + + Comment + Initial focus point offset relative to avatar for the camera preset Group View + Persist + 1 + Type + Vector3D + Value + + 1.5 + 0.7 + 1.0 + + + FocusPosOnLogout + + Comment + Camera focus point when last logged out (global coordinates) + Persist + 1 + Type + Vector3D + Value + + 0.0 + 0.0 + 0.0 + + + FolderAutoOpenDelay + + Comment + Seconds before automatically expanding the folder under the mouse when performing inventory drag and drop + Persist + 1 + Type + F32 + Value + 0.75 + + FolderLoadingMessageWaitTime + + Comment + Seconds to wait before showing the LOADING... text in folder views + Persist + 1 + Type + F32 + Value + 0.5 + + FontScreenDPI + + Comment + Font resolution, higher is bigger (pixels per inch) + Persist + 1 + Type + F32 + Value + 96.0 + + ForceAssetFail + + Comment + Force wearable fetches to fail for this asset type. + Persist + 1 + Type + U32 + Value + 255 + + ForceShowGrid + + Comment + Always show grid dropdown on login screen + Persist + 1 + Type + Boolean + Value + 0 + + ForceMandatoryUpdate + + Comment + For QA: On next startup, forces the auto-updater to run + Persist + 1 + Type + Boolean + Value + 0 + + FreezeTime + + Comment + + Persist + 0 + Type + Boolean + Value + 0 + + FullScreenAspectRatio + + Comment + Aspect ratio of fullscreen display (width / height) + Persist + 1 + Type + F32 + Value + 3 + + FullScreenAutoDetectAspectRatio + + Comment + Automatically detect proper aspect ratio for fullscreen display + Persist + 1 + Type + Boolean + Value + 0 + + GesturesMarketplaceURL + + Comment + URL to the Gestures Marketplace + Persist + 0 + Type + String + Value + https://www.xstreetsl.com/modules.php?name=Marketplace&CategoryID=233 + + GridCrossSections + + Comment + Highlight cross sections of prims with grid manipulation plane. + Persist + 1 + Type + Boolean + Value + 0 + + GridDrawSize + + Comment + Visible extent of 2D snap grid (meters) + Persist + 1 + Type + F32 + Value + 12.0 + + GridMode + + Comment + Snap grid reference frame (0 = world, 1 = local, 2 = reference object) + Persist + 1 + Type + S32 + Value + 0 + + GridOpacity + + Comment + Grid line opacity (0.0 = completely transparent, 1.0 = completely opaque) + Persist + 1 + Type + F32 + Value + 0.699999988079 + + GridResolution + + Comment + Size of single grid step (meters) + Persist + 1 + Type + F32 + Value + 0.5 + + GridSubUnit + + Comment + Display fractional grid steps, relative to grid size + Persist + 1 + Type + Boolean + Value + 0 + + GridSubdivision + + Comment + Maximum number of times to divide single snap grid unit when GridSubUnit is true + Persist + 1 + Type + S32 + Value + 32 + + GroupNotifyBoxHeight + + Comment + Height of group notice messages + Persist + 1 + Type + S32 + Value + 260 + + GroupNotifyBoxWidth + + Comment + Width of group notice messages + Persist + 1 + Type + S32 + Value + 305 + + HelpUseLocal + + Comment + If set, always use this for help: skins/default/html/[LANGUAGE]/help-offline/index.html + Persist + 0 + Type + Boolean + Value + 0 + + HelpURLFormat + + Comment + URL pattern for help page; arguments will be encoded; see llviewerhelp.cpp:buildHelpURL for arguments + Persist + 1 + Type + String + Value + http://viewer-help.secondlife.com/[LANGUAGE]/[CHANNEL]/[VERSION]/[TOPIC][DEBUG_MODE] + + HomeSidePanelURL + + Comment + URL for the web page to display in the Home side panel + Persist + 1 + Type + String + Value + https://viewer-sidebar.secondlife.com/sidebar.html?p=[AUTH_TOKEN]&lang=[LANGUAGE]&channel=[CHANNEL]&version=[VERSION]&major=[VERSION_MAJOR]&minor=[VERSION_MINOR]&patch=[VERSION_PATCH]&build=[VERSION_BUILD]&firstlogin=[FIRST_LOGIN] + + SearchURL + + Comment + URL for Search website, displayed in the Find floater + Persist + 0 + Type + String + Value + http://search.secondlife.com/viewer/[CATEGORY]/?q=[QUERY]&p=[AUTH_TOKEN]&r=[MATURITY]&lang=[LANGUAGE]&g=[GODLIKE]&sid=[SESSION_ID]&rid=[REGION_ID]&pid=[PARCEL_ID]&channel=[CHANNEL]&version=[VERSION]&major=[VERSION_MAJOR]&minor=[VERSION_MINOR]&patch=[VERSION_PATCH]&build=[VERSION_BUILD] + + WebProfileURL + + Comment + URL for Web Profiles + Persist + 0 + Type + String + Value + https://my.secondlife.com/[AGENT_NAME] + + WebProfileNonProductionURL + + Comment + URL for Web Profiles on Non-Production grids + Persist + 0 + Type + String + Value + https://my-demo.secondlife.com/[AGENT_NAME] + + HighResSnapshot + + Comment + Double resolution of snapshot from current window resolution + Persist + 1 + Type + Boolean + Value + 0 + + HideSelectedObjects + + Comment + Hide Selected Objects + Persist + 1 + Type + Boolean + Value + 0 + + HostID + + Comment + Machine identifier for hosted Second Life instances + Persist + 0 + Type + String + Value + + + HtmlHelpLastPage + + Comment + Last URL visited via help system + Persist + 1 + Type + String + Value + + + IMShowTimestamps + + Comment + Show timestamps in IM + Persist + 1 + Type + Boolean + Value + 1 + + IMShowControlPanel + + Comment + Show IM Control Panel + Persist + 1 + Type + Boolean + Value + 1 + + IgnoreAllNotifications + + Comment + Ignore all notifications so we never need user input on them. + Persist + 1 + Type + Boolean + Value + 0 + + IgnorePixelDepth + + Comment + Ignore pixel depth settings. + Persist + 1 + Type + Boolean + Value + 0 + + ImagePipelineUseHTTP + + Comment + If TRUE use HTTP GET to fetch textures from the server + Persist + 1 + Type + Boolean + Value + 1 + + InactiveFloaterTransparency + + Comment + Transparency of inactive floaters (floaters that have no focus) + Persist + 1 + Type + F32 + Value + 0.65 + + InBandwidth + + Comment + Incoming bandwidth throttle (bps) + Persist + 1 + Type + F32 + Value + 0.0 + + InspectorFadeTime + + Comment + Fade out timing for inspectors + Persist + 1 + Type + F32 + Value + 0.5 + + InspectorShowTime + + Comment + Stay timing for inspectors + Persist + 1 + Type + F32 + Value + 3.0 + + InstallLanguage + + Comment + Language passed from installer (for UI) + Persist + 1 + Type + String + Value + default + + InventoryAutoOpenDelay + + Comment + Seconds before automatically opening inventory when mouse is over inventory button when performing inventory drag and drop + Persist + 1 + Type + F32 + Value + 1.0 + + InventoryLinking + + Comment + Enable ability to create links to folders and items via "Paste as link". + Persist + 1 + Type + Boolean + Value + 0 + + InventorySortOrder + + Comment + Specifies sort key for inventory items (+0 = name, +1 = date, +2 = folders always by name, +4 = system folders to top) + Persist + 1 + Type + U32 + Value + 7 + + InvertMouse + + Comment + When in mouselook, moving mouse up looks down and vice verse (FALSE = moving up looks up) + Persist + 1 + Type + Boolean + Value + 0 + + JoystickAvatarEnabled + + Comment + Enables the Joystick to control Avatar movement. + Persist + 1 + Type + Boolean + Value + 1 + + JoystickAxis0 + + Comment + Flycam hardware axis mapping for internal axis 0 ([0, 5]). + Persist + 1 + Type + S32 + Value + 1 + + JoystickAxis1 + + Comment + Flycam hardware axis mapping for internal axis 1 ([0, 5]). + Persist + 1 + Type + S32 + Value + 0 + + JoystickAxis2 + + Comment + Flycam hardware axis mapping for internal axis 2 ([0, 5]). + Persist + 1 + Type + S32 + Value + 2 + + JoystickAxis3 + + Comment + Flycam hardware axis mapping for internal axis 3 ([0, 5]). + Persist + 1 + Type + S32 + Value + 4 + + JoystickAxis4 + + Comment + Flycam hardware axis mapping for internal axis 4 ([0, 5]). + Persist + 1 + Type + S32 + Value + 3 + + JoystickAxis5 + + Comment + Flycam hardware axis mapping for internal axis 5 ([0, 5]). + Persist + 1 + Type + S32 + Value + 5 + + JoystickAxis6 + + Comment + Flycam hardware axis mapping for internal axis 6 ([0, 5]). + Persist + 1 + Type + S32 + Value + -1 + + JoystickBuildEnabled + + Comment + Enables the Joystick to move edited objects. + Persist + 1 + Type + Boolean + Value + 0 + + JoystickEnabled + + Comment + Enables Joystick Input. + Persist + 1 + Type + Boolean + Value + 0 + + JoystickFlycamEnabled + + Comment + Enables the Joystick to control the flycam. + Persist + 0 + Type + Boolean + Value + 1 + + JoystickInitialized + + Comment + Whether or not a joystick has been detected and initiailized. + Persist + 1 + Type + String + Value + + + JoystickMouselookYaw + + Comment + Pass joystick yaw to scripts in Mouselook. + Persist + 1 + Type + Boolean + Value + 1 + + JoystickRunThreshold + + Comment + Input threshold to initiate running + Persist + 1 + Type + F32 + Value + 0.25 + + KeepAspectForSnapshot + + Comment + Use full window when taking snapshot, regardless of requested image size + Persist + 1 + Type + Boolean + Value + 1 + + LandBrushSize + + Comment + Size of affected region when using teraform tool + Persist + 1 + Type + F32 + Value + 2.0 + + LCDDestination + + Comment + Which LCD to use + Persist + 1 + Type + S32 + Value + 0 + + LSLFindCaseInsensitivity + + Comment + Use case insensitivity when searching in LSL editor + Persist + 1 + Type + Boolean + Value + 0 + + LSLHelpURL + + Comment + URL that points to LSL help files, with [LSL_STRING] corresponding to the referenced LSL function or keyword + Persist + 1 + Type + String + Value + http://wiki.secondlife.com/wiki/[LSL_STRING] + + LagMeterShrunk + + Comment + Last large/small state for lag meter + Persist + 1 + Type + Boolean + Value + 0 + + Language + + Comment + Language specifier (for UI) + Persist + 1 + Type + String + Value + default + + LanguageIsPublic + + Comment + Let other residents see our language information + Persist + 1 + Type + Boolean + Value + 1 + + LastGPUClass + + Comment + [DO NOT MODIFY] previous GPU class for tracking hardware changes + Persist + 1 + Type + S32 + Value + -1 + + LastFeatureVersion + + Comment + [DO NOT MODIFY] Version number for tracking hardware changes + Persist + 1 + Type + S32 + Value + 0 + + LastFindPanel + + Comment + Controls which find operation appears by default when clicking "Find" button + Persist + 1 + Type + String + Value + find_all_panel + + LastName + + Comment + Login last name + Persist + 1 + Type + String + Value + + + LastPrefTab + + Comment + Last selected tab in preferences window + Persist + 1 + Type + S32 + Value + 0 + + LastMediaSettingsTab + + Comment + Last selected tab in media settings window + Persist + 1 + Type + S32 + Value + 0 + + LastRunVersion + + Comment + Version number of last instance of the viewer that you ran + Persist + 1 + Type + String + Value + 0.0.0 + + + LastSnapshotToEmailHeight + + Comment + The height of the last email snapshot, in px + Persist + 1 + Type + S32 + Value + 768 + + LastSnapshotToEmailWidth + + Comment + The width of the last email snapshot, in px + Persist + 1 + Type + S32 + Value + 1024 + + LastSnapshotToDiskHeight + + Comment + The height of the last disk snapshot, in px + Persist + 1 + Type + S32 + Value + 768 + + LastSnapshotToDiskWidth + + Comment + The width of the last disk snapshot, in px + Persist + 1 + Type + S32 + Value + 1024 + + LastSnapshotToInventoryHeight + + Comment + The height of the last texture snapshot, in px + Persist + 1 + Type + S32 + Value + 512 + + LastSnapshotToInventoryWidth + + Comment + The width of the last texture snapshot, in px + Persist + 1 + Type + S32 + Value + 512 + + LastSnapshotType + + Comment + Select this as next type of snapshot to take (0 = postcard, 1 = texture, 2 = local image) + Persist + 1 + Type + S32 + Value + 0 + + LeftClickShowMenu + + Comment + Left click opens pie menu (FALSE = left click touches or grabs object) + Persist + 1 + Type + Boolean + Value + 0 + + LimitDragDistance + + Comment + Limit translation of object via translate tool + Persist + 1 + Type + Boolean + Value + 1 + + LimitSelectDistance + + Comment + Disallow selection of objects beyond max select distance + Persist + 1 + Type + Boolean + Value + 1 + + LipSyncAah + + Comment + Aah (jaw opening) babble loop + Persist + 1 + Type + String + Value + 257998776531013446642343 + + LipSyncAahPowerTransfer + + Comment + Transfer curve for Voice Interface power to aah lip sync amplitude + Persist + 1 + Type + String + Value + 0000123456789 + + LipSyncEnabled + + Comment + 0 disable lip-sync, 1 enable babble loop + Persist + 1 + Type + Boolean + Value + 1 + + LipSyncOoh + + Comment + Ooh (mouth width) babble loop + Persist + 1 + Type + String + Value + 1247898743223344444443200000 + + LipSyncOohAahRate + + Comment + Rate to babble Ooh and Aah (/sec) + Persist + 1 + Type + F32 + Value + 24.0 + + LipSyncOohPowerTransfer + + Comment + Transfer curve for Voice Interface power to ooh lip sync amplitude + Persist + 1 + Type + String + Value + 0012345566778899 + + LocalCacheVersion + + Comment + Version number of cache + Persist + 1 + Type + S32 + Value + 0 + + LocalFileSystemBrowsingEnabled + + Comment + Enable/disable access to the local file system via the file picker + Persist + 1 + Type + Boolean + Value + 1 + + LoginSRVTimeout + + Comment + Duration in seconds of the login SRV request timeout + Persist + 0 + Type + F32 + Value + 10.0 + + LoginSRVPump + + Comment + Name of the message pump that handles SRV request + Persist + 0 + Type + String + Value + LLAres + + LogMessages + + Comment + Log network traffic + Persist + 1 + Type + Boolean + Value + 0 + + LogTextureNetworkTraffic + + Comment + Log network traffic for textures + Persist + 1 + Type + Boolean + Value + 0 + + LoginAsGod + + Comment + Attempt to login with god powers (Linden accounts only) + Persist + 1 + Type + Boolean + Value + 0 + + LoginLocation + + Comment + Login location ('last', 'home') + Persist + 1 + Type + String + Value + last + + LoginPage + + Comment + Login authentication page. + Persist + 1 + Type + String + Value + + + LosslessJ2CUpload + + Comment + Use lossless compression for small image uploads + Persist + 1 + Type + Boolean + Value + 0 + + MainloopTimeoutDefault + + Comment + Timeout duration for mainloop lock detection, in seconds. + Persist + 1 + Type + F32 + Value + 20.0 + + MapOverlayIndex + + Comment + Currently selected world map type + Persist + 1 + Type + S32 + Value + 0 + + MapScale + + Comment + World map zoom level (pixels per region) + Persist + 1 + Type + F32 + Value + 128.0 + + MapServerURL + + Comment + World map URL template for locating map tiles + Persist + 0 + Type + String + Value + http://map.secondlife.com.s3.amazonaws.com/ + + CurrentMapServerURL + + Comment + Current Session World map URL + Persist + 0 + Type + String + Value + + + MapShowEvents + + Comment + Show events on world map + Persist + 1 + Type + Boolean + Value + 1 + + MapShowInfohubs + + Comment + Show infohubs on the world map + Persist + 1 + Type + Boolean + Value + 1 + + MapShowLandForSale + + Comment + Show land for sale on world map + Persist + 1 + Type + Boolean + Value + 0 + + MapShowPeople + + Comment + Show other users on world map + Persist + 1 + Type + Boolean + Value + 1 + + MapShowTelehubs + + Comment + Show telehubs on world map + Persist + 1 + Type + Boolean + Value + 1 + + MiniMapAutoCenter + + Comment + Center the focal point of the minimap. + Persist + 0 + Type + Boolean + Value + 1 + + Marker + + Comment + [NOT USED] + Persist + 1 + Type + String + Value + + + MarketplaceURL + + Comment + URL to the Marketplace + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/ + + MarketplaceURL_objectFemale + + Comment + URL to the Marketplace Attachments Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/attachments + + MarketplaceURL_objectMale + + Comment + URL to the Marketplace Attachments Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/attachments + + MarketplaceURL_clothingFemale + + Comment + URL to the Marketplace Clothing Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/clothing_female_avatar + + MarketplaceURL_clothingMale + + Comment + URL to the Marketplace Clothing Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/clothing_male_avatar + + MarketplaceURL_bodypartFemale + + Comment + URL to the Marketplace Bodyparts Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com + + MarketplaceURL_bodypartMale + + Comment + URL to the Marketplace Bodyparts Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/ + + MarketplaceURL_glovesMale + + Comment + URL to the Marketplace Gloves Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/gloves_both_women_and_men + + MarketplaceURL_glovesFemale + + Comment + URL to the Marketplace Gloves Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/gloves_both_women_and_men + + MarketplaceURL_jacketFemale + + Comment + URL to the Marketplace Jacket Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/jacket_womens + + MarketplaceURL_jacketMale + + Comment + URL to the Marketplace Jacket Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/jacket_mens + + MarketplaceURL_shirtFemale + + Comment + URL to the Marketplace Shirt Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/shirt_womens + + MarketplaceURL_shirtMale + + Comment + URL to the Marketplace Shirt Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/shirt_mens + + MarketplaceURL_undershirtFemale + + Comment + URL to the Marketplace Undershirt Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/undershirt_womens + + MarketplaceURL_undershirtMale + + Comment + URL to the Marketplace Undershirt Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/undershirt_mens + + MarketplaceURL_skirtFemale + + Comment + URL to the Marketplace Skirt Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/skirts_women + + MarketplaceURL_skirtMale + + Comment + URL to the Marketplace Skirt Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/skirts_women + + MarketplaceURL_pantsFemale + + Comment + URL to the Marketplace Pants Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/pants_women + + MarketplaceURL_pantsMale + + Comment + URL to the Marketplace Pants Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/pants_men + + MarketplaceURL_underpantsFemale + + Comment + URL to the Marketplace Underwear Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/underwear_women + + MarketplaceURL_underpantsMale + + Comment + URL to the Marketplace Underwear Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/underwear_men + + MarketplaceURL_shoesFemale + + Comment + URL to the Marketplace Shoes Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/shoes_women + + MarketplaceURL_shoesMale + + Comment + URL to the Marketplace Shoes Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/shoes_men + + MarketplaceURL_socksFemale + + Comment + URL to the Marketplace Socks Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/socks_women + + MarketplaceURL_socksMale + + Comment + URL to the Marketplace Socks Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/socks_women + + MarketplaceURL_tattooMale + + Comment + URL to the Marketplace Tattoo Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/tattoo_both_women_and_men + + MarketplaceURL_tattooFemale + + Comment + URL to the Marketplace Tattoo Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/tattoo_both_women_and_men + + MarketplaceURL_hairFemale + + Comment + URL to the Marketplace Hair Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/womens_hair + + MarketplaceURL_hairMale + + Comment + URL to the Marketplace Hair Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/mens_hair + + MarketplaceURL_eyesFemale + + Comment + URL to the Marketplace Eyes Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/womens_eyes + + MarketplaceURL_eyesMale + + Comment + URL to the Marketplace Eyes Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/mens_eyes + + MarketplaceURL_shapeFemale + + Comment + URL to the Marketplace Shape Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/womens_shape + + MarketplaceURL_shapeMale + + Comment + URL to the Marketplace Shape Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/mens_shape + + MarketplaceURL_skinFemale + + Comment + URL to the Marketplace Skin Female + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/womens_skin + + MarketplaceURL_skinMale + + Comment + URL to the Marketplace Skins Male + Persist + 0 + Type + String + Value + http://marketplace.secondlife.com/trampoline/viewer21/mens_skin + + MaxDragDistance + + Comment + Maximum allowed translation distance in a single operation of translate tool (meters from start point) + Persist + 1 + Type + F32 + Value + 48.0 + + MaxSelectDistance + + Comment + Maximum allowed selection distance (meters from avatar) + Persist + 1 + Type + F32 + Value + 64.0 + + MaxWearableWaitTime + + Comment + Max seconds to wait for wearable assets to fetch. + Persist + 1 + Type + F32 + Value + 60.0 + + MediaControlFadeTime + + Comment + Amount of time (in seconds) that the media control fades + Persist + 1 + Type + F32 + Value + 1.5 + + MediaControlTimeout + + Comment + Amount of time (in seconds) for media controls to fade with no mouse activity + Persist + 1 + Type + F32 + Value + 3.0 + + MediaEnablePopups + + Comment + If true, enable targeted links and javascript in media to open new media browser windows without a prompt. + Persist + 1 + Type + Boolean + Value + 0 + + MediaOnAPrimUI + + Comment + Whether or not to show the "link sharing" UI + Persist + 1 + Type + Boolean + Value + 1 + + MediaPerformanceManagerDebug + + Comment + Whether to show debug data for the media performance manager in the nearby media list. + Persist + 1 + Type + Boolean + Value + 0 + + MediaShowOnOthers + + Comment + Whether or not to show media on other avatars + Persist + 1 + Type + Boolean + Value + 0 + + MediaShowOutsideParcel + + Comment + Whether or not to show media from outside the current parcel + Persist + 1 + Type + Boolean + Value + 1 + + MediaShowWithinParcel + + Comment + Whether or not to show media within the current parcel + Persist + 1 + Type + Boolean + Value + 1 + + MediaTentativeAutoPlay + + Comment + This is a tentative flag that may be temporarily set off by the user, until she teleports + Persist + 0 + Type + Boolean + Value + 1 + + MemoryLogFrequency + + Comment + Seconds between display of Memory in log (0 for never) + Persist + 1 + Type + F32 + Value + 600.0 + + MemProfiling + + Comment + You want to use tcmalloc's memory profiling options. + Persist + 1 + Type + Boolean + Value + 0 + + MenuAccessKeyTime + + Comment + Time (seconds) in which the menu key must be tapped to move focus to the menu bar + Persist + 1 + Type + F32 + Value + 0.25 + + MenuBarHeight + + Comment + + Persist + 0 + Type + S32 + Value + 18 + + MenuBarWidth + + Comment + + Persist + 0 + Type + S32 + Value + 410 + + MePanelOpened + + Comment + Indicates that Me Panel was opened at least once after Viewer was installed + Persist + 1 + Type + Boolean + Value + 0 + + MigrateCacheDirectory + + Comment + Check for old version of disk cache to migrate to current location + Persist + 1 + Type + Boolean + Value + 1 + + MiniMapPrimMaxRadius + + Comment + Radius of the largest prim to show on the MiniMap. Increasing beyond 256 may cause client lag. + Persist + 1 + Type + F32 + Value + 256.0 + + MiniMapRotate + + Comment + Rotate miniature world map to avatar direction + Persist + 1 + Type + Boolean + Value + 1 + + MiniMapScale + + Comment + Miniature world map zoom level (pixels per region) + Persist + 1 + Type + F32 + Value + 128.0 + + MouseSensitivity + + Comment + Controls responsiveness of mouse when in mouselook mode (fraction or multiple of default mouse sensitivity) + Persist + 1 + Type + F32 + Value + 3.0 + + MouseSmooth + + Comment + Smooths out motion of mouse when in mouselook mode. + Persist + 1 + Type + Boolean + Value + 0 + + MouseSun + + Comment + + Persist + 0 + Type + Boolean + Value + 0 + + MuteAmbient + + Comment + Ambient sound effects, such as wind noise, play at 0 volume + Persist + 1 + Type + Boolean + Value + 0 + + MuteAudio + + Comment + All audio plays at 0 volume (streaming audio still takes up bandwidth, for example) + Persist + 1 + Type + Boolean + Value + 0 + + MuteMedia + + Comment + Media plays at 0 volume (streaming audio still takes up bandwidth) + Persist + 1 + Type + Boolean + Value + 0 + + MuteMusic + + Comment + Music plays at 0 volume (streaming audio still takes up bandwidth) + Persist + 1 + Type + Boolean + Value + 0 + + MuteSounds + + Comment + Sound effects play at 0 volume + Persist + 1 + Type + Boolean + Value + 0 + + MuteUI + + Comment + UI sound effects play at 0 volume + Persist + 1 + Type + Boolean + Value + 0 + + MuteVoice + + Comment + Voice plays at 0 volume (streaming audio still takes up bandwidth) + Persist + 1 + Type + Boolean + Value + 0 + + MuteWhenMinimized + + Comment + Mute audio when SL window is minimized + Persist + 1 + Type + Boolean + Value + 0 + + MyOutfitsAutofill + + Comment + Always autofill My Outfits from library when empty (else happens just once). + Persist + 1 + Type + Boolean + Value + 0 + + NearMeRange + + Comment + Search radius for nearby avatars + Persist + 1 + Type + F32 + Value + 130 + + NextOwnerCopy + + Comment + Newly created objects can be copied by next owner + Persist + 1 + Type + Boolean + Value + 0 + + NextOwnerModify + + Comment + Newly created objects can be modified by next owner + Persist + 1 + Type + Boolean + Value + 0 + + NextOwnerTransfer + + Comment + Newly created objects can be resold or given away by next owner + Persist + 1 + Type + Boolean + Value + 1 + + NewCacheLocation + + Comment + Change the location of the local disk cache to this + Persist + 1 + Type + String + Value + + + NewCacheLocationTopFolder + + Comment + Change the top folder location of the local disk cache to this + Persist + 1 + Type + String + Value + + + NextLoginLocation + + Comment + Location to log into by default. + Persist + 1 + Type + String + Value + + + NoAudio + + Comment + Disable audio playback. + Persist + 1 + Type + Boolean + Value + 0 + + NoHardwareProbe + + Comment + Disable hardware probe. + Persist + 1 + Type + Boolean + Value + 0 + + NoInventoryLibrary + + Comment + Do not request inventory library. + Persist + 1 + Type + Boolean + Value + 0 + + NoPreload + + Comment + Disable sound and image preload. + Persist + 1 + Type + Boolean + Value + 0 + + NoVerifySSLCert + + Comment + Do not verify SSL peers. + Persist + 1 + Type + Boolean + Value + 0 + + NotifyBoxHeight + + Comment + Height of notification messages + Persist + 1 + Type + S32 + Value + 200 + + NotifyBoxWidth + + Comment + Width of notification messages + Persist + 1 + Type + S32 + Value + 305 + + NotificationToastLifeTime + + Comment + Number of seconds while a notification toast exists + Persist + 1 + Type + S32 + Value + 5 + + NotificationTipToastLifeTime + + Comment + Number of seconds while a notification tip toast exist + Persist + 1 + Type + S32 + Value + 10 + + ToastFadingTime + + Comment + Number of seconds while a toast is fading + Persist + 1 + Type + S32 + Value + 1 + + NearbyToastFadingTime + + Comment + Number of seconds while a nearby chat toast is fading + Persist + 1 + Type + S32 + Value + 3 + + NearbyToastLifeTime + + Comment + Number of seconds while a nearby chat toast exists + Persist + 1 + Type + S32 + Value + 23 + + StartUpToastLifeTime + + Comment + Number of seconds while a StartUp toast exist + Persist + 1 + Type + S32 + Value + 5 + + ToastGap + + Comment + Gap between toasts on a screen (min. value is 5) + Persist + 1 + Type + S32 + Value + 7 + + ToastButtonWidth + + Comment + Default width of buttons in the toast. + Notes: + If required width will be less then this one, a button will be reshaped to default size , otherwise to required + Change of this parameter will affect the layout of buttons in notification toast. + Persist + 1 + Type + S32 + Value + 90 + + ChannelBottomPanelMargin + + Comment + Space from a lower toast to the Bottom Tray + Persist + 1 + Type + S32 + Value + 35 + + NotificationChannelRightMargin + + Comment + Space between toasts and a right border of an area where they can appear + Persist + 1 + Type + S32 + Value + 5 + + NotificationChannelHeightRatio + + Comment + Notification channel and World View ratio(0.0 - always show 1 notification, 1.0 - max ratio). + Persist + 1 + Type + F32 + Value + 0.5 + + OverflowToastHeight + + Comment + Height of an overflow toast + Persist + 1 + Type + S32 + Value + 72 + + NotifyMoneyChange + + Comment + Pop up notifications for all L$ transactions + Persist + 1 + Type + Boolean + Value + 1 + + NotifyTipDuration + + Comment + Length of time that notification tips stay on screen (seconds) + Persist + 1 + Type + F32 + Value + 4.0 + + NumSessions + + Comment + Number of successful logins to Second Life + Persist + 1 + Type + S32 + Value + 0 + + NumpadControl + + Comment + How numpad keys control your avatar. 0 = Like the normal arrow keys, 1 = Numpad moves avatar when numlock is off, 2 = Numpad moves avatar regardless of numlock (use this if you have no numlock) + Persist + 1 + Type + S32 + Value + 0 + + ObjectCacheEnabled + + Comment + Enable the object cache. + Persist + 1 + Type + Boolean + Value + 1 + + OpenDebugStatAdvanced + + Comment + Expand advanced performance stats display + Persist + 1 + Type + Boolean + Value + 0 + + OpenDebugStatBasic + + Comment + Expand basic performance stats display + Persist + 1 + Type + Boolean + Value + 1 + + OpenDebugStatNet + + Comment + Expand network stats display + Persist + 1 + Type + Boolean + Value + 1 + + OpenDebugStatRender + + Comment + Expand render stats display + Persist + 1 + Type + Boolean + Value + 1 + + OpenDebugStatSim + + Comment + Expand simulator performance stats display + Persist + 1 + Type + Boolean + Value + 1 + + OpenDebugStatTexture + + Comment + Expand Texture performance stats display + Persist + 1 + Type + Boolean + Value + 0 + + OpenDebugStatPhysicsDetails + + Comment + Expand Physics Details performance stats display + Persist + 1 + Type + Boolean + Value + 0 + + OpenDebugStatSimTime + + Comment + Expand Simulator Time performance stats display + Persist + 1 + Type + Boolean + Value + 0 + + OpenDebugStatSimTimeDetails + + Comment + Expand Simulator Time Details performance stats display + Persist + 1 + Type + Boolean + Value + 0 + + OutBandwidth + + Comment + Outgoing bandwidth throttle (bps) + Persist + 1 + Type + F32 + Value + 0.0 + + OverlayTitle + + Comment + Controls watermark text message displayed on screen when "ShowOverlayTitle" is enabled (one word, underscores become spaces) + Persist + 1 + Type + String + Value + Set_via_OverlayTitle_in_settings.xml + + PTTCurrentlyEnabled + + Comment + Use Push to Talk mode + Persist + 0 + Type + Boolean + Value + 1 + + PacketDropPercentage + + Comment + Percentage of packets dropped by the client. + Persist + 1 + Type + F32 + Value + 0.0 + + ParcelMediaAutoPlayEnable + + Comment + Auto play parcel media when available + Persist + 1 + Type + Boolean + Value + 1 + + ParticipantListShowIcons + + Comment + Show/hide people icons in participant list + Persist + 1 + Type + Boolean + Value + 1 + + PerAccountSettingsFile + + Comment + Persisted client settings file name (per user). + Persist + 0 + Type + String + Value + + + PermissionsCautionEnabled + + Comment + When enabled, changes the handling of script permission requests to help avoid accidental granting of certain permissions, such as the debit permission + Persist + 0 + Type + Boolean + Value + 1 + + PermissionsCautionNotifyBoxHeight + + Comment + Height of caution-style notification messages + Persist + 0 + Type + S32 + Value + 344 + + PickerContextOpacity + + Comment + Controls overall opacity of context frustrum connecting color and texture pickers with their swatches + Persist + 1 + Type + F32 + Value + 0.34999999404 + + PicksPerSecondMouseMoving + + Comment + How often to perform hover picks while the mouse is moving (picks per second) + Persist + 1 + Type + F32 + Value + 5.0 + + PicksPerSecondMouseStationary + + Comment + How often to perform hover picks while the mouse is stationary (picks per second) + Persist + 1 + Type + F32 + Value + 0.0 + + PieMenuLineWidth + + Comment + Width of lines in pie menu display (pixels) + Persist + 1 + Type + F32 + Value + 2.5 + + PingInterpolate + + Comment + Extrapolate object position along velocity vector based on ping delay + Persist + 1 + Type + Boolean + Value + 0 + + PitchFromMousePosition + + Comment + Vertical range over which avatar head tracks mouse position (degrees of head rotation from top of window to bottom) + Persist + 1 + Type + F32 + Value + 90.0 + + PlayTypingAnim + + Comment + Your avatar plays the typing animation whenever you type in the chat bar + Persist + 1 + Type + Boolean + Value + 1 + + PluginAttachDebuggerToPlugins + + Comment + If true, attach a debugger session to each plugin process as it's launched. + Persist + 1 + Type + Boolean + Value + 0 + + PluginInstancesCPULimit + + Comment + Amount of total plugin CPU usage before inworld plugins start getting turned down to "slideshow" priority. Set to 0 to disable this check. + Persist + 1 + Type + F32 + Value + 0.9 + + + PlainTextChatHistory + + Comment + Enable/Disable plain text chat history style + Persist + 1 + Type + Boolean + Value + 0 + + + PluginInstancesLow + + Comment + Limit on the number of inworld media plugins that will run at "low" priority + Persist + 1 + Type + U32 + Value + 4 + + PluginInstancesNormal + + Comment + Limit on the number of inworld media plugins that will run at "normal" or higher priority + Persist + 1 + Type + U32 + Value + 2 + + PluginInstancesTotal + + Comment + Hard limit on the number of plugins that will be instantiated at once for inworld media + Persist + 1 + Type + U32 + Value + 8 + + + PluginUseReadThread + + Comment + Use a separate thread to read incoming messages from plugins + Persist + 1 + Type + Boolean + Value + 0 + + + PrecachingDelay + + Comment + Delay when logging in to load world before showing it (seconds) + Persist + 1 + Type + F32 + Value + 6.0 + + PreferredMaturity + + Comment + Setting for the user's preferred maturity level (consts in indra_constants.h) + Persist + 1 + Type + U32 + Value + 13 + + PrimMediaMasterEnabled + + Comment + Whether or not Media on a Prim is enabled. + Persist + 1 + Type + Boolean + Value + 1 + + PrimMediaControlsUseHoverControlSet + + Comment + Whether or not hovering over prim media uses minimal "hover" controls or the authored control set. + Persist + 1 + Type + Boolean + Value + 0 + + PrimMediaDragNDrop + + Comment + Enable drag and drop of URLs onto prim faces + Persist + 1 + Type + Boolean + Value + 1 + + PrimMediaMaxRetries + + Comment + Maximum number of retries for media queries. + Persist + 1 + Type + U32 + Value + 4 + + PrimMediaRequestQueueDelay + + Comment + Timer delay for fetching media from the queue (in seconds). + Persist + 1 + Type + F32 + Value + 1.0 + + PrimMediaRetryTimerDelay + + Comment + Timer delay for retrying on media queries (in seconds). + Persist + 1 + Type + F32 + Value + 5.0 + + PrimMediaMaxSortedQueueSize + + Comment + Maximum number of objects the viewer will load media for initially + Persist + 1 + Type + U32 + Value + 100000 + + PrimMediaMaxRoundRobinQueueSize + + Comment + Maximum number of objects the viewer will continuously update media for + Persist + 1 + Type + U32 + Value + 100000 + + ProbeHardwareOnStartup + + Comment + Query current hardware configuration on application startup + Persist + 1 + Type + Boolean + Value + 1 + + PurgeCacheOnNextStartup + + Comment + Clear local file cache next time viewer is run + Persist + 1 + Type + Boolean + Value + 0 + + PurgeCacheOnStartup + + Comment + Clear local file cache every time viewer is run + Persist + 1 + Type + Boolean + Value + 0 + + PushToTalkButton + + Comment + Which button or keyboard key is used for push-to-talk + Persist + 1 + Type + String + Value + MiddleMouse + + PushToTalkToggle + + Comment + Should the push-to-talk button behave as a toggle + Persist + 1 + Type + Boolean + Value + 1 + + QAMode + + Comment + Enable Testing Features. + Persist + 1 + Type + Boolean + Value + 0 + + QAModeEventHostPort + + Comment + Port on which lleventhost should listen + Persist + 0 + Type + S32 + Value + -1 + + QAModeTermCode + + Comment + On LL_ERRS, terminate with this code instead of OS message box + Persist + 0 + Type + S32 + Value + -1 + + QuietSnapshotsToDisk + + Comment + Take snapshots to disk without playing animation or sound + Persist + 1 + Type + Boolean + Value + 0 + + QuitAfterSeconds + + Comment + The duration allowed before quitting. + Persist + 1 + Type + F32 + Value + 0.0 + + QuitAfterSecondsOfAFK + + Comment + The duration allowed after being AFK before quitting. + Persist + 1 + Type + F32 + Value + 0.0 + + QuitOnLoginActivated + + Comment + Quit if login page is activated (used when auto login is on and users must not be able to login manually) + Persist + 1 + Type + Boolean + Value + 0 + + RadioLandBrushAction + + Comment + Last selected land modification operation (0 = flatten, 1 = raise, 2 = lower, 3 = smooth, 4 = roughen, 5 = revert) + Persist + 1 + Type + S32 + Value + 0 + + RadioLandBrushSize + + Comment + Size of land modification brush (0 = small, 1 = medium, 2 = large) + Persist + 1 + Type + S32 + Value + 0 + + LandBrushForce + + Comment + Multiplier for land modification brush force. + Persist + 1 + Type + F32 + Value + 1.0 + + MediaBrowserWindowLimit + + Comment + Maximum number of media brower windows that can be open at once in the media browser floater (0 for no limit) + Persist + 1 + Type + S32 + Value + 5 + + WebContentWindowLimit + + Comment + Maximum number of web brower windows that can be open at once in the Web content floater (0 for no limit) + Persist + 1 + Type + S32 + Value + 5 + + MediaRollOffRate + + Comment + Multiplier to change rate of media attenuation + Persist + 1 + Type + F32 + Value + 0.125 + + MediaRollOffMin + + Comment + Adjusts the distance at which media attentuation starts + Persist + 1 + Type + F32 + Value + 5.0 + + MediaRollOffMax + + Comment + Distance at which media volume is set to 0 + Persist + 1 + Type + F32 + Value + 30.0 + + RecentItemsSortOrder + + Comment + Specifies sort key for recent inventory items (+0 = name, +1 = date, +2 = folders always by name, +4 = system folders to top) + Persist + 1 + Type + U32 + Value + 1 + + RectangleSelectInclusive + + Comment + Select objects that have at least one vertex inside selection rectangle + Persist + 1 + Type + Boolean + Value + 1 + + RegInClient + + Comment + Experimental: Embed registration in login screen + Persist + 1 + Type + Boolean + Value + 0 + + QuickBuyCurrency + + Comment + Toggle between HTML based currency purchase floater and legacy XUI version + Persist + 1 + Type + Boolean + Value + 0 + + RegionTextureSize + + Comment + Terrain texture dimensions (power of 2) + Persist + 1 + Type + U32 + Value + 256 + + RememberPassword + + Comment + Keep password (in encrypted form) for next login + Persist + 1 + Type + Boolean + Value + 1 + + RenderAnisotropic + + Comment + Render textures using anisotropic filtering + Persist + 1 + Type + Boolean + Value + 0 + + RenderAppleUseMultGL + + Comment + Whether we want to use multi-threaded OpenGL on Apple hardware (requires restart of SL). + Persist + 1 + Type + Boolean + Value + 0 + + RenderAttachedLights + + Comment + Render lighted prims that are attached to avatars + Persist + 1 + Type + Boolean + Value + 1 + + RenderAttachedParticles + + Comment + Render particle systems that are attached to avatars + Persist + 1 + Type + Boolean + Value + 1 + + RenderAvatar + + Comment + Render Avatars + Persist + 0 + Type + Boolean + Value + 1 + + RenderAvatarCloth + + Comment + Controls if avatars use wavy cloth + Persist + 1 + Type + Boolean + Value + 1 + + RenderAvatarLODFactor + + Comment + Controls level of detail of avatars (multiplier for current screen area when calculated level of detail) + Persist + 1 + Type + F32 + Value + 0.5 + + RenderAvatarMaxVisible + + Comment + Maximum number of avatars to display at any one time + Persist + 1 + Type + S32 + Value + 12 + + RenderAvatarVP + + Comment + Use vertex programs to perform hardware skinning of avatar + Persist + 1 + Type + Boolean + Value + 1 + + + RenderShadowNearDist + + Comment + Near clip plane of shadow camera (affects precision of depth shadows). + Persist + 1 + Type + Vector3 + Value + + 256 + 256 + 256 + + + RenderShadowClipPlanes + + Comment + Near clip plane split distances for shadow map frusta. + Persist + 1 + Type + Vector3 + Value + + 1.0 + 12.0 + 32.0 + + + RenderShadowSplitExponent + + Comment + Near clip plane split distances for shadow map frusta (x=perspective, y=ortho, z=transition rate). + Persist + 1 + Type + Vector3 + Value + + 3.0 + 3.0 + 2.0 + + + RenderShadowOrthoClipPlanes + + Comment + Near clip plane split distances for orthographic shadow map frusta. + Persist + 1 + Type + Vector3 + Value + + 4.0 + 8.0 + 24.0 + + + RenderShadowProjOffset + + Comment + Amount to scale distance to virtual origin of shadow perspective projection. + Persist + 1 + Type + F32 + Value + 2.0 + + RenderShadowSlopeThreshold + + Comment + Cutoff slope value for points to affect perspective shadow generation + Persist + 1 + Type + F32 + Value + 0.0 + + RenderShadowProjExponent + + Comment + Exponent applied to transition between ortho and perspective shadow projections based on viewing angle and light vector. + Persist + 1 + Type + F32 + Value + 0.5 + + RenderSSAOScale + + Comment + Scaling factor for the area to sample for occluders (pixels at 1 meter away, inversely varying with distance) + Persist + 1 + Type + F32 + Value + 500.0 + + RenderSSAOMaxScale + + Comment + Maximum screen radius for sampling (pixels) + Persist + 1 + Type + U32 + Value + 200 + + RenderSSAOFactor + + Comment + Occlusion sensitivity factor for ambient occlusion (larger is more) + Persist + 1 + Type + F32 + Value + 0.30 + + RenderSSAOEffect + + Comment + Multiplier for (1) value and (2) saturation (HSV definition), for areas which are totally occluded. Blends with original color for partly-occluded areas. (Third component is unused.) + Persist + 1 + Type + Vector3 + Value + + 0.40 + 1.00 + 0.00 + + + RenderBumpmapMinDistanceSquared + + Comment + Maximum distance at which to render bumpmapped primitives (distance in meters, squared) + Persist + 1 + Type + F32 + Value + 100.0 + + RenderNormalMapScale + + Comment + Scaler applied to height map when generating normal maps + Persist + 1 + Type + F32 + Value + 64 + + RenderCubeMap + + Comment + Whether we can render the cube map or not + Persist + 1 + Type + Boolean + Value + 1 + + RenderDebugAlphaMask + + Comment + Test Alpha Masking Cutoffs. + Persist + 1 + Type + F32 + Value + 0.5 + + RenderDebugGL + + Comment + Enable strict GL debugging. + Persist + 1 + Type + Boolean + Value + 0 + + RenderDebugPipeline + + Comment + Enable strict pipeline debugging. + Persist + 1 + Type + Boolean + Value + 0 + + RenderDebugTextureBind + + Comment + Enable texture bind performance test. + Persist + 1 + Type + Boolean + Value + 0 + + RenderDelayCreation + + Comment + Throttle creation of drawables. + Persist + 1 + Type + Boolean + Value + 0 + + RenderAnimateRes + + Comment + Animate rezing prims. + Persist + 1 + Type + Boolean + Value + 0 + + + RenderAnimateTrees + + Comment + Use GL matrix ops to animate tree branches. + Persist + 1 + Type + Boolean + Value + 0 + + + RenderGIRange + + Comment + Distance to cut off GI effect. + Persist + 1 + Type + F32 + Value + 96 + + + RenderGILuminance + + Comment + Luminance factor of global illumination contribution. + Persist + 1 + Type + F32 + Value + 0.075 + + + RenderGIBrightness + + Comment + Brightness factor of global illumination contribution. + Persist + 1 + Type + F32 + Value + 0.3 + + + RenderGINoise + + Comment + Noise of position sampling for GI photon mapping. + Persist + 1 + Type + F32 + Value + 0.7 + + + RenderGIAttenuation + + Comment + Distance attenuation factor for indirect lighting. + Persist + 1 + Type + F32 + Value + 0.1 + + + RenderGIBlurBrightness + + Comment + Brightness factor of global illumination blur effect. + Persist + 1 + Type + F32 + Value + 1.025 + + + RenderGIBlurEdgeWeight + + Comment + Edge weight for GI soften filter (sharpness). + Persist + 1 + Type + F32 + Value + 0.8 + + + RenderGIBlurIncrement + + Comment + Increment of scale for each pass of global illumination blur effect. + Persist + 1 + Type + F32 + Value + 0.8 + + + RenderLuminanceScale + + Comment + Luminance value scalar for darkening effect. + Persist + 1 + Type + F32 + Value + 1.0 + + + RenderSunLuminanceScale + + Comment + Sun Luminance value scalar for darkening effect. + Persist + 1 + Type + F32 + Value + 1.0 + + + RenderSunLuminanceOffset + + Comment + Sun Luminance value offset for darkening effect. + Persist + 1 + Type + F32 + Value + 0 + + + RenderLuminanceDetail + + Comment + Mipmap level to use for luminance + Persist + 1 + Type + F32 + Value + 16.0 + + + RenderEdgeDepthCutoff + + Comment + Cutoff for depth difference that amounts to an edge. + Persist + 1 + Type + F32 + Value + 0.01 + + RenderEdgeNormCutoff + + Comment + Cutoff for normal difference that amounts to an edge. + Persist + 1 + Type + F32 + Value + 0.25 + + + RenderDeferredAlphaSoften + + Comment + Scalar for softening alpha surfaces (for soft particles). + Persist + 1 + Type + F32 + Value + 0.75 + + RenderDeferredNoise + + Comment + Noise scalar to hide banding in deferred render. + Persist + 1 + Type + F32 + Value + 4 + + RenderDeferredSpotShadowBias + + Comment + Bias value for spot shadows (prevent shadow acne). + Persist + 1 + Type + F32 + Value + -64.0 + + RenderDeferredSpotShadowOffset + + Comment + Offset value for spot shadows (prevent shadow acne). + Persist + 1 + Type + F32 + Value + 0.8 + + + RenderShadowBias + + Comment + Bias value for shadows (prevent shadow acne). + Persist + 1 + Type + F32 + Value + -0.008 + + RenderShadowOffset + + Comment + Offset value for shadows (prevent shadow acne). + Persist + 1 + Type + F32 + Value + 0.01 + + + RenderShadowBiasError + + Comment + Error scale for shadow bias (based on altitude). + Persist + 1 + Type + F32 + Value + 0 + + RenderShadowOffsetError + + Comment + Error scale for shadow offset (based on altitude). + Persist + 1 + Type + F32 + Value + 0 + + + RenderSpotLightsInNondeferred + + Comment + Whether to support projectors as spotlights when Lighting and Shadows is disabled + Persist + 1 + Type + Boolean + Value + 0 + + + RenderSpotShadowBias + + Comment + Bias value for shadows (prevent shadow acne). + Persist + 1 + Type + F32 + Value + 0.0 + + RenderSpotShadowOffset + + Comment + Offset value for shadows (prevent shadow acne). + Persist + 1 + Type + F32 + Value + 0.04 + + + RenderShadowResolutionScale + + Comment + Scale of shadow map resolution vs. screen resolution + Persist + 1 + Type + F32 + Value + 1.0 + + + RenderDeferredTreeShadowBias + + Comment + Bias value for tree shadows (prevent shadow acne). + Persist + 1 + Type + F32 + Value + 1.0 + + RenderDeferredTreeShadowOffset + + Comment + Offset value for tree shadows (prevent shadow acne). + Persist + 1 + Type + F32 + Value + 1.0 + + + RenderHoverGlowEnable + + Comment + Show glow effect when hovering on interactive objects. + Persist + 1 + Type + Boolean + Value + 0 + + + RenderHighlightFadeTime + + Comment + Transition time for mouseover highlights. + Persist + 1 + Type + F32 + Value + 0.1 + + + RenderHighlightBrightness + + Comment + Brightness of mouseover highlights. + Persist + 1 + Type + F32 + Value + 4.0 + + + RenderHighlightThickness + + Comment + Thickness of mouseover highlights. + Persist + 1 + Type + F32 + Value + 0.6 + + + RenderHighlightColor + + Comment + Brightness of mouseover highlights. + Persist + 1 + Type + Color4 + Value + + 0.4 + 0.98 + 0.93 + 1.0 + + + + RenderSpecularResX + + Comment + Spec map resolution. + Persist + 1 + Type + U32 + Value + 128 + + + RenderSpecularResY + + Comment + Spec map resolution. + Persist + 1 + Type + U32 + Value + 128 + + + RenderSpecularExponent + + Comment + Specular exponent for generating spec map + Persist + 1 + Type + F32 + Value + 8 + + + RenderDeferred + + Comment + Use deferred rendering pipeline. + Persist + 1 + Type + Boolean + Value + 0 + + + RenderDeferredGI + + Comment + Enable GI in deferred renderer. + Persist + 1 + Type + Boolean + Value + 0 + + + RenderDeferredSun + + Comment + Execute sunlight shader in deferred renderer. + Persist + 1 + Type + Boolean + Value + 1 + + + RenderDeferredAtmospheric + + Comment + Execute atmospheric shader in deferred renderer. + Persist + 1 + Type + Boolean + Value + 1 + + + RenderDeferredSSAO + + Comment + Execute screen space ambient occlusion shader in deferred renderer. + Persist + 1 + Type + Boolean + Value + 1 + + + RenderDeferredBlurLight + + Comment + Execute shadow softening shader in deferred renderer. + Persist + 1 + Type + Boolean + Value + 1 + + + RenderDeferredLocalLights + + Comment + Execute local lighting shader in deferred renderer. + Persist + 1 + Type + Boolean + Value + 1 + + + RenderDeferredFullscreenLights + + Comment + Execute local lighting shader in deferred renderer. + Persist + 1 + Type + Boolean + Value + 1 + + + RenderDeferredSunWash + + Comment + Amount local lights are washed out by sun. + Persist + 1 + Type + F32 + Value + 0.5 + + RenderShadowNoise + + Comment + Magnitude of noise on shadow samples. + Persist + 1 + Type + F32 + Value + -0.0001 + + RenderShadowErrorCutoff + + Comment + Cutoff error value to use ortho instead of perspective projection. + Persist + 1 + Type + F32 + Value + 5.0 + + RenderShadowFOVCutoff + + Comment + Cutoff FOV to use ortho instead of perspective projection. + Persist + 1 + Type + F32 + Value + 1.1 + + + RenderShadowGaussian + + Comment + Gaussian coefficients for the two shadow/SSAO blurring passes (z component unused). + Persist + 1 + Type + Vector3 + Value + + 3.0 + 2.0 + 0.0 + + + + RenderShadowBlurSize + + Comment + Scale of shadow softening kernel. + Persist + 1 + Type + F32 + Value + 1.4 + + RenderShadowBlurSamples + + Comment + Number of samples to take for each pass of shadow blur (value range 1-16). Actual number of samples is value * 2 - 1. + Persist + 1 + Type + U32 + Value + 4 + + RenderShadowBlurDistFactor + + Comment + Distance scaler for shadow blur. + Persist + 1 + Type + F32 + Value + 0.1 + + + RenderGIAmbiance + + Comment + Ambiance factor of global illumination contribution. + Persist + 1 + Type + F32 + Value + 0.5 + + + RenderGIMinRenderSize + + Comment + Minimum size of objects to put into GI source map. + Persist + 1 + Type + F32 + Value + 0.5 + + + RenderGIBlurColorCurve + + Comment + Color curve for GI softening kernel + Persist + 1 + Type + Vector3 + Value + + 1.0 + 0.6 + 0.02 + + + + RenderGIBlurPasses + + Comment + Scale of GI softening kernel. + Persist + 1 + Type + U32 + Value + 4 + + + RenderGIBlurSize + + Comment + Scale of GI softening kernel. + Persist + 1 + Type + F32 + Value + 4.0 + + RenderGIBlurSamples + + Comment + Number of samples to take for each pass of GI blur (value range 1-16). Actual number of samples is value * 2 - 1. + Persist + 1 + Type + U32 + Value + 16 + + RenderGIBlurDistFactor + + Comment + Distance scaler for GI blur. + Persist + 1 + Type + F32 + Value + 0.0 + + + RenderDynamicLOD + + Comment + Dynamically adjust level of detail. + Persist + 1 + Type + Boolean + Value + 1 + + RenderFSAASamples + + Comment + Number of samples to use for FSAA (0 = no AA). + Persist + 1 + Type + U32 + Value + 0 + + RenderFarClip + + Comment + Distance of far clip plane from camera (meters) + Persist + 1 + Type + F32 + Value + 256.0 + + RenderAutoMaskAlphaNonDeferred + + Comment + Use alpha masks where appropriate, in the non-deferred (non-'Lighting and Shadows') graphics mode + Persist + 1 + Type + Boolean + Value + 0 + + RenderAutoMaskAlphaDeferred + + Comment + Use alpha masks where appropriate, in the deferred ('Lighting and Shadows') graphics mode + Persist + 1 + Type + Boolean + Value + 1 + + RenderFlexTimeFactor + + Comment + Controls level of detail of flexible objects (multiplier for amount of time spent processing flex objects) + Persist + 1 + Type + F32 + Value + 1.0 + + RenderFogRatio + + Comment + Distance from camera where fog reaches maximum density (fraction or multiple of far clip distance) + Persist + 1 + Type + F32 + Value + 4.0 + + RenderGamma + + Comment + Sets gamma exponent for renderer + Persist + 1 + Type + F32 + Value + 0.0 + + RenderGammaFull + + Comment + Use fully controllable gamma correction, instead of faster, hard-coded gamma correction of 2. + Persist + 1 + Type + Boolean + Value + 1.0 + + RenderGlow + + Comment + Render bloom post effect. + Persist + 1 + Type + Boolean + Value + 1 + + RenderGlowIterations + + Comment + Number of times to iterate the glow (higher = wider and smoother but slower) + Persist + 1 + Type + S32 + Value + 2 + + RenderGlowLumWeights + + Comment + Weights for each color channel to be used in calculating luminance (should add up to 1.0) + Persist + 1 + Type + Vector3 + Value + + 0.299 + 0.587 + 0.114 + + + RenderGlowMaxExtractAlpha + + Comment + Max glow alpha value for brightness extraction to auto-glow. + Persist + 1 + Type + F32 + Value + 0.065 + + RenderGlowMinLuminance + + Comment + Min luminance intensity necessary to consider an object bright enough to automatically glow. + Persist + 1 + Type + F32 + Value + 2.5 + + RenderGlowResolutionPow + + Comment + Glow map resolution power of two. + Persist + 1 + Type + S32 + Value + 9 + + RenderGlowStrength + + Comment + Additive strength of glow. + Persist + 1 + Type + F32 + Value + 0.35 + + RenderGlowWarmthAmount + + Comment + Amount of warmth extraction to use (versus luminance extraction). 0 = lum, 1.0 = warmth + Persist + 1 + Type + F32 + Value + 0.0 + + RenderGlowWarmthWeights + + Comment + Weight of each color channel used before finding the max warmth + Persist + 1 + Type + Vector3 + Value + + 1.0 + 0.5 + 0.7 + + + RenderGlowWidth + + Comment + Glow sample size (higher = wider and softer but eventually more pixelated) + Persist + 1 + Type + F32 + Value + 1.3 + + RenderGround + + Comment + Determines whether we can render the ground pool or not + Persist + 1 + Type + Boolean + Value + 1 + + RenderHUDInSnapshot + + Comment + Display HUD attachments in snapshot + Persist + 1 + Type + Boolean + Value + 0 + + RenderHUDParticles + + Comment + Display particle systems in HUD attachments (experimental) + Persist + 1 + Type + Boolean + Value + 0 + + RenderHighlightSelections + + Comment + Show selection outlines on objects + Persist + 0 + Type + Boolean + Value + 1 + + RenderHiddenSelections + + Comment + Show selection lines on objects that are behind other objects + Persist + 1 + Type + Boolean + Value + 1 + + RenderHideGroupTitle + + Comment + Don't show my group title in my name label + Persist + 1 + Type + Boolean + Value + 0 + + NameTagShowGroupTitles + + Comment + Show group titles in name labels + Persist + 1 + Type + Boolean + Value + 0 + + NameTagShowDisplayNames + + Comment + Show display names in name labels + Persist + 1 + Type + Boolean + Value + 1 + + NameTagShowFriends + + Comment + Highlight the name tags of your friends + Persist + 1 + Type + Boolean + Value + 0 + + NameTagShowUsernames + + Comment + Show usernames in avatar name tags + Persist + 1 + Type + Boolean + Value + 1 + + RenderInitError + + Comment + Error occured while initializing GL + Persist + 1 + Type + Boolean + Value + 0 + + RenderLightRadius + + Comment + Render the radius of selected lights + Persist + 1 + Type + Boolean + Value + 0 + + RenderMaxPartCount + + Comment + Maximum number of particles to display on screen + Persist + 1 + Type + S32 + Value + 4096 + + RenderMaxNodeSize + + Comment + Maximum size of a single node's vertex data (in KB). + Persist + 1 + Type + S32 + Value + 8192 + + RenderMaxVBOSize + + Comment + Maximum size of a vertex buffer (in KB). + Persist + 1 + Type + S32 + Value + 512 + + RenderNameFadeDuration + + Comment + Time interval over which to fade avatar names (seconds) + Persist + 1 + Type + F32 + Value + 1.0 + + RenderNameShowSelf + + Comment + Display own name above avatar + Persist + 1 + Type + Boolean + Value + 1 + + RenderNameShowTime + + Comment + Fade avatar names after specified time (seconds) + Persist + 1 + Type + F32 + Value + 10.0 + + RenderObjectBump + + Comment + Show bumpmapping on primitives + Persist + 1 + Type + Boolean + Value + 1 + + RenderQualityPerformance + + Comment + Which graphics settings you've chosen + Persist + 1 + Type + U32 + Value + 1 + + RenderReflectionDetail + + Comment + Detail of reflection render pass. + Persist + 1 + Type + S32 + Value + 2 + + RenderShadowDetail + + Comment + Detail of shadows. + Persist + 1 + Type + S32 + Value + 2 + + + RenderReflectionRes + + Comment + Reflection map resolution. + Persist + 1 + Type + S32 + Value + 64 + + RenderResolutionDivisor + + Comment + Divisor for rendering 3D scene at reduced resolution. + Persist + 1 + Type + U32 + Value + 1 + + RenderShaderLightingMaxLevel + + Comment + Max lighting level to use in the shader (class 3 is default, 2 is less lights, 1 is sun/moon only. Works around shader compiler bugs on certain platforms.) + Persist + 1 + Type + S32 + Value + 3 + + RenderShaderLODThreshold + + Comment + Fraction of draw distance defining the switch to a different shader LOD + Persist + 1 + Type + F32 + Value + 1.0 + + RenderShaderParticleThreshold + + Comment + Fraction of draw distance to not use shader on particles + Persist + 1 + Type + F32 + Value + 0.25 + + RenderSunDynamicRange + + Comment + Defines what percent brighter the sun is than local point lights (1.0 = 100% brighter. Value should not be less than 0. ). + Persist + 1 + Type + F32 + Value + 1.0 + + RenderTerrainDetail + + Comment + Detail applied to terrain texturing (0 = none, 1 or 2 = full) + Persist + 1 + Type + S32 + Value + 2 + + RenderTerrainLODFactor + + Comment + Controls level of detail of terrain (multiplier for current screen area when calculated level of detail) + Persist + 1 + Type + F32 + Value + 1.0 + + RenderTerrainScale + + Comment + Terrain detail texture scale + Persist + 1 + Type + F32 + Value + 12.0 + + RenderTextureMemoryMultiple + + Comment + Multiple of texture memory value to use (should fit: 0 < value <= 1.0) + Persist + 1 + Type + F32 + Value + 1.0 + + RenderTrackerBeacon + + Comment + Display tracking arrow and beacon to target avatar, teleport destination + Persist + 1 + Type + Boolean + Value + 1 + + RenderTransparentWater + + Comment + Render water as transparent. Setting to false renders water as opaque with a simple texture applied. + Persist + 1 + Type + Boolean + Value + 1 + + RenderTreeLODFactor + + Comment + Controls level of detail of vegetation (multiplier for current screen area when calculated level of detail) + Persist + 1 + Type + F32 + Value + 0.5 + + RenderUIInSnapshot + + Comment + Display user interface in snapshot + Persist + 1 + Type + Boolean + Value + 0 + + RenderUIBuffer + + Comment + Cache ui render in a screen aligned buffer. + Persist + 1 + Type + Boolean + Value + 0 + + RenderUnloadedAvatar + + Comment + Show avatars which haven't finished loading + Persist + 1 + Type + Boolean + Value + 0 + + RenderUseFBO + + Comment + Whether we want to use GL_EXT_framebuffer_objects. + Persist + 1 + Type + Boolean + Value + 1 + + RenderUseTriStrips + + Comment + Use triangle strips for rendering prims. + Persist + 1 + Type + Boolean + Value + 0 + + RenderUseFarClip + + Comment + If false, frustum culling will ignore far clip plane. + Persist + 1 + Type + Boolean + Value + 1 + + RenderUseImpostors + + Comment + Whether we want to use impostors for far away avatars. + Persist + 1 + Type + Boolean + Value + 1 + + RenderUseShaderLOD + + Comment + Whether we want to have different shaders for LOD + Persist + 1 + Type + Boolean + Value + 1 + + RenderUseShaderNearParticles + + Comment + Whether we want to use shaders on near particles + Persist + 1 + Type + Boolean + Value + 0 + + RenderVBOEnable + + Comment + Use GL Vertex Buffer Objects + Persist + 1 + Type + Boolean + Value + 1 + + RenderVBOMappingDisable + + Comment + Disable VBO glMapBufferARB + Persist + 1 + Type + Boolean + Value + 1 + + RenderUseStreamVBO + + Comment + Use VBO's for stream buffers + Persist + 1 + Type + Boolean + Value + 1 + + RenderVolumeLODFactor + + Comment + Controls level of detail of primitives (multiplier for current screen area when calculated level of detail) + Persist + 1 + Type + F32 + Value + 1.0 + + RenderWater + + Comment + Display water + Persist + 1 + Type + Boolean + Value + 1 + + RenderWaterMipNormal + + Comment + Use mip maps for water normal map. + Persist + 1 + Type + Boolean + Value + 1 + + RenderWaterRefResolution + + Comment + Water planar reflection resolution. + Persist + 1 + Type + S32 + Value + 512 + + RenderParcelSelection + + Comment + Display selected parcel outline + Persist + 1 + Type + Boolean + Value + 1 + + RotateRight + + Comment + Make the agent rotate to its right. + Persist + 1 + Type + Boolean + Value + 0 + + RotationStep + + Comment + All rotations via rotation tool are constrained to multiples of this unit (degrees) + Persist + 1 + Type + F32 + Value + 1.0 + + SafeMode + + Comment + Reset preferences, run in safe mode. + Persist + 1 + Type + Boolean + Value + 0 + + SaveMinidump + + Comment + Save minidump for developer debugging on crash + Persist + 1 + Type + Boolean + Value + 1 + + ScaleShowAxes + + Comment + Show indicator of selected scale axis when scaling + Persist + 1 + Type + Boolean + Value + 0 + + ScaleStretchTextures + + Comment + Stretch textures along with object when scaling + Persist + 1 + Type + Boolean + Value + 1 + + ScaleUniform + + Comment + Scale selected objects evenly about center of selection + Persist + 1 + Type + Boolean + Value + 0 + + ScriptHelpFollowsCursor + + Comment + Scripting help window updates contents based on script editor contents under text cursor + Persist + 1 + Type + Boolean + Value + 0 + + ScriptsCanShowUI + + Comment + Allow LSL calls (such as LLMapDestination) to spawn viewer UI + Persist + 1 + Type + Boolean + Value + 1 + + SecondLifeEnterprise + + Comment + Enables Second Life Enterprise features + Persist + 1 + Type + Boolean + Value + 0 + + SelectMovableOnly + + Comment + Select only objects you can move + Persist + 1 + Type + Boolean + Value + 0 + + SelectOwnedOnly + + Comment + Select only objects you own + Persist + 1 + Type + Boolean + Value + 0 + + SelectionHighlightAlpha + + Comment + Opacity of selection highlight (0.0 = completely transparent, 1.0 = completely opaque) + Persist + 1 + Type + F32 + Value + 0.40000000596 + + SelectionHighlightAlphaTest + + Comment + Alpha value below which pixels are displayed on selection highlight line (0.0 = show all pixels, 1.0 = show now pixels) + Persist + 1 + Type + F32 + Value + 0.1 + + SelectionHighlightThickness + + Comment + Thickness of selection highlight line (fraction of view distance) + Persist + 1 + Type + F32 + Value + 0.00999999977648 + + SelectionHighlightUAnim + + Comment + Rate at which texture animates along U direction in selection highlight line (fraction of texture per second) + Persist + 1 + Type + F32 + Value + 0.0 + + SelectionHighlightUScale + + Comment + Scale of texture display on selection highlight line (fraction of texture size) + Persist + 1 + Type + F32 + Value + 0.1 + + SelectionHighlightVAnim + + Comment + Rate at which texture animates along V direction in selection highlight line (fraction of texture per second) + Persist + 1 + Type + F32 + Value + 0.5 + + SelectionHighlightVScale + + Comment + Scale of texture display on selection highlight line (fraction of texture size) + Persist + 1 + Type + F32 + Value + 1.0 + + ServerChoice + + Comment + [DO NOT MODIFY] Controls which grid you connect to + Persist + 1 + Type + S32 + Value + 0 + + ShareWithGroup + + Comment + Newly created objects are shared with the currently active group + Persist + 1 + Type + Boolean + Value + 0 + + ShowAdvancedGraphicsSettings + + Comment + Show advanced graphics settings + Persist + 1 + Type + Boolean + Value + 0 + + ShowAllObjectHoverTip + + Comment + Show descriptive tooltip when mouse hovers over non-interactive and interactive objects. + Persist + 1 + Type + Boolean + Value + 0 + + AvatarNameTagMode + + Comment + Select Avatar Name Tag Mode + Persist + 1 + Type + S32 + Value + 1 + + ShowAxes + + Comment + Render coordinate frame at your position + Persist + 1 + Type + Boolean + Value + 0 + + ShowBanLines + + Comment + Show in-world ban/access borders + Persist + 1 + Type + Boolean + Value + 1 + + ShowBuildButton + + Comment + Shows/Hides Build button in the bottom tray. + Persist + 1 + Type + Boolean + Value + 0 + + ShowCameraButton + + Comment + Show/Hide View button in the bottom tray. + Persist + 1 + Type + Boolean + Value + 1 + + ShowConsoleWindow + + Comment + Show log in separate OS window + Persist + 1 + Type + Boolean + Value + 0 + + NavBarShowCoordinates + + Comment + Show coordinates in navigation bar + Persist + 1 + Type + Boolean + Value + 0 + + NavBarShowParcelProperties + + Comment + Show parcel property icons in navigation bar + Persist + 1 + Type + Boolean + Value + 1 + + ShowBetaGrids + + Comment + Display the beta grids in the grid selection control. + Persist + 1 + Type + Boolean + Value + 1 + + ShowCrosshairs + + Comment + Display crosshairs when in mouselook mode + Persist + 1 + Type + Boolean + Value + 1 + + ShowDebugConsole + + Comment + Show log in SL window + Persist + 1 + Type + Boolean + Value + 0 + + ShowEmptyFoldersWhenSearching + + Comment + Shows folders that do not have any visible contents when applying a filter to inventory + Persist + 1 + Type + Boolean + Value + 0 + + ShowGestureButton + + Comment + Shows/Hides Gesture button in the bottom tray. + Persist + 1 + Type + Boolean + Value + 1 + + ShowHoverTips + + Comment + Show descriptive tooltip when mouse hovers over items in world + Persist + 1 + Type + Boolean + Value + 1 + + ShowLandHoverTip + + Comment + Show descriptive tooltip when mouse hovers over land + Persist + 1 + Type + Boolean + Value + 0 + + ShowMiniMapButton + + Comment + Shows/Hides Mini-Map button in the bottom tray. + Persist + 1 + Type + Boolean + Value + 0 + + ShowMoveButton + + Comment + Shows/Hides Move button in the bottom tray. + Persist + 1 + Type + Boolean + Value + 1 + + ShowScriptErrors + + Comment + Show script errors + Persist + 1 + Type + Boolean + Value + 1 + + ShowScriptErrorsLocation + + Comment + Show script error in chat or window + Persist + 1 + Type + S32 + Value + 1 + + ShowSearchButton + + Comment + Shows/Hides Search button in the bottom tray. + Persist + 1 + Type + Boolean + Value + 0 + + ShowSnapshotButton + + Comment + Shows/Hides Snapshot button button in the bottom tray. + Persist + 1 + Type + Boolean + Value + 1 + + ShowObjectRenderingCost + + Comment + Show the object rendering cost in build tools + Persist + 1 + Type + Boolean + Value + 1 + + ShowNavbarFavoritesPanel + + Comment + Show/Hide Navigation Bar Favorites Panel + Persist + 1 + Type + Boolean + Value + 1 + + ShowNavbarNavigationPanel + + Comment + Show/Hide Navigation Bar Navigation Panel + Persist + 1 + Type + Boolean + Value + 1 + + ShowWorldMapButton + + Comment + Shows/Hides Map button in the bottom tray. + Persist + 1 + Type + Boolean + Value + 0 + + ShowMiniLocationPanel + + Comment + Show/Hide Mini-Location Panel + Persist + 1 + Type + Boolean + Value + 0 + + SidebarCameraMovement + + Comment + Reflects world rect changing while changing sidebar visibility. + Persist + 1 + Type + Boolean + Value + 0 + + GroupListShowIcons + + Comment + Show/hide group icons in the group list + Persist + 1 + Type + Boolean + Value + 1 + + FriendsListShowIcons + + Comment + Show/hide online and all friends icons in the friend list + Persist + 1 + Type + Boolean + Value + 1 + + FriendsListShowPermissions + + Comment + Show/hide permission icons in the friend list + Persist + 1 + Type + Boolean + Value + 1 + + NearbyListShowIcons + + Comment + Show/hide people icons in nearby list + Persist + 1 + Type + Boolean + Value + 1 + + RecentListShowIcons + + Comment + Show/hide people icons in recent list + Persist + 1 + Type + Boolean + Value + 1 + + FriendsSortOrder + + Comment + Specifies sort order for friends (0 = by name, 1 = by online status) + Persist + 1 + Type + U32 + Value + 0 + + NearbyPeopleSortOrder + + Comment + Specifies sort order for nearby people (0 = by name, 3 = by distance, 4 = by most recent) + Persist + 1 + Type + U32 + Value + 4 + + RecentPeopleSortOrder + + Comment + Specifies sort order for recent people (0 = by name, 2 = by most recent) + Persist + 1 + Type + U32 + Value + 2 + + ShowPGSearchAll + + Comment + Display results of search All that are flagged as general + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 1 + + ShowMatureSearchAll + + Comment + Display results of search All that are flagged as moderate + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 0 + + ShowAdultSearchAll + + Comment + Display results of search All that are flagged as adult + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 0 + + ShowPGGroups + + Comment + Display results of find groups that are flagged as general + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 1 + + ShowMatureGroups + + Comment + Display results of find groups that are flagged as moderate + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 0 + + ShowAdultGroups + + Comment + Display results of find groups that are flagged as adult + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 0 + + ShowPGClassifieds + + Comment + Display results of find classifieds that are flagged as general + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 1 + + ShowMatureClassifieds + + Comment + Display results of find classifieds that are flagged as moderate + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 0 + + ShowAdultClassifieds + + Comment + Display results of find classifieds that are flagged as adult + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 0 + + ShowPGEvents + + Comment + Display results of find events that are flagged as general + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 1 + + ShowMatureEvents + + Comment + Display results of find events that are flagged as moderate + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 0 + + ShowAdultEvents + + Comment + Display results of find events that are flagged as adult + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 0 + + ShowPGLand + + Comment + Display results of find land sales that are flagged as general + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 1 + + ShowMatureLand + + Comment + Display results of find land sales that are flagged as moderate + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 0 + + ShowAdultLand + + Comment + Display results of find land sales that are flagged as adult + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 0 + + ShowPGSims + + Comment + Display results of find places or find popular that are in general sims + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 1 + + ShowMatureSims + + Comment + Display results of find places or find popular that are in moderate sims + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 0 + + ShowAdultSims + + Comment + Display results of find places or find popular that are in adult sims + Persist + 1 + HideFromEditor + 1 + Type + Boolean + Value + 0 + + ShowNearClip + + Comment + + Persist + 0 + Type + Boolean + Value + 0 + + ShowNewInventory + + Comment + Automatically views new notecards/textures/landmarks + Persist + 1 + Type + Boolean + Value + 1 + + ShowInInventory + + Comment + Automatically opens inventory to show accepted objects + Persist + 1 + Type + Boolean + Value + 1 + + ShowObjectUpdates + + Comment + Show when update messages are received for individual objects + Persist + 0 + Type + Boolean + Value + 0 + + ShowOverlayTitle + + Comment + Prints watermark text message on screen + Persist + 1 + Type + Boolean + Value + 0 + + ShowParcelOwners + + Comment + + Persist + 0 + Type + Boolean + Value + 0 + + ShowPermissions + + Comment + + Persist + 0 + Type + Boolean + Value + 0 + + ShowPropertyLines + + Comment + Show line overlay demarking property boundaries + Persist + 1 + Type + Boolean + Value + 0 + + ShowNetStats + + Comment + Show the Search Bar in the Status Overlay + Persist + 1 + Type + Boolean + Value + 0 + + ShowSelectionBeam + + Comment + Show selection particle beam when selecting or interacting with objects. + Persist + 1 + Type + Boolean + Value + 1 + + ShowStartLocation + + Comment + Display starting location menu on login screen + Persist + 1 + Type + Boolean + Value + 0 + + ShowTangentBasis + + Comment + Render normal and binormal (debugging bump mapping) + Persist + 1 + Type + Boolean + Value + 0 + + ShowToolBar + + Comment + Show toolbar at bottom of screen + Persist + 1 + Type + Boolean + Value + 1 + + ShowTutorial + + Comment + Show tutorial window on login + Persist + 1 + Type + Boolean + Value + 0 + + ShowVoiceVisualizersInCalls + + Comment + Enables in-world voice visualizers, voice gestures and lip-sync while in group or P2P calls. + Persist + 1 + Type + Boolean + Value + 0 + + SkinCurrent + + Comment + The currently selected skin. + Persist + 1 + Type + String + Value + default + + SkinningSettingsFile + + Comment + Client skin color setting file name (per install). + Persist + 0 + Type + String + Value + + + SkyAmbientScale + + Comment + Controls strength of ambient, or non-directional light from the sun and moon (fraction or multiple of default ambient level) + Persist + 1 + Type + F32 + Value + 0.300000011921 + + SkyEditPresets + + Comment + Whether to be able to edit the sky defaults or not + Persist + 1 + Type + Boolean + Value + 0 + + SkyNightColorShift + + Comment + Controls moonlight color (base color applied to moon as light source) + Persist + 1 + Type + Color3 + Value + + 0.67 + 0.67 + 1.0 + + + SkyOverrideSimSunPosition + + Comment + + Persist + 0 + Type + Boolean + Value + 0 + + SkySunDefaultPosition + + Comment + Default position of sun in sky (direction in world coordinates) + Persist + 1 + Type + Vector3 + Value + + 1.0 + 0.0 + 0.1 + + + SkyUseClassicClouds + + Comment + Whether to use the old Second Life particle clouds or not + Persist + 1 + Type + Boolean + Value + 1 + + SnapEnabled + + Comment + Enable snapping to grid + Persist + 1 + Type + Boolean + Value + 1 + + SnapMargin + + Comment + Controls maximum distance between windows before they auto-snap together (pixels) + Persist + 1 + Type + S32 + Value + 10 + + SnapToMouseCursor + + Comment + When snapping to grid, center object on nearest grid point to mouse cursor + Persist + 1 + Type + Boolean + Value + 0 + + SnapshotFormat + + Comment + Save snapshots in this format (0 = PNG, 1 = JPEG, 2 = BMP) + Persist + 1 + Type + S32 + Value + 0 + + SnapshotLocalLastResolution + + Comment + Take next local snapshot at this resolution + Persist + 1 + Type + S32 + Value + 0 + + SnapshotPostcardLastResolution + + Comment + Take next postcard snapshot at this resolution + Persist + 1 + Type + S32 + Value + 0 + + SnapshotQuality + + Comment + Quality setting of postcard JPEGs (0 = worst, 100 = best) + Persist + 1 + Type + S32 + Value + 75 + + SnapshotSharingEnabled + + Comment + Enable uploading of snapshots to a web service. + Persist + 1 + Type + Boolean + Value + 0 + + SnapshotConfigURL + + Comment + URL to fetch Snapshot Sharing configuration data from. + Persist + 1 + Type + String + Value + http://photos.apps.staging.avatarsunited.com/viewer_config + + SnapshotTextureLastResolution + + Comment + Take next texture snapshot at this resolution + Persist + 1 + Type + S32 + Value + 0 + + SpeedTest + + Comment + Performance testing mode, no network + Persist + 1 + Type + Boolean + Value + 0 + + StatsAutoRun + + Comment + Play back autopilot + Persist + 1 + Type + Boolean + Value + 0 + + StatsFile + + Comment + Filename for stats logging output + Persist + 1 + Type + String + Value + fs.txt + + StatsNumRuns + + Comment + Loop autopilot playback this number of times + Persist + 1 + Type + S32 + Value + -1 + + StatsPilotFile + + Comment + Filename for stats logging autopilot path + Persist + 1 + Type + String + Value + pilot.txt + + StatsQuitAfterRuns + + Comment + Quit application after this number of autopilot playback runs + Persist + 1 + Type + Boolean + Value + 0 + + StatsSessionTrackFrameStats + + Comment + Track rendering and network statistics + Persist + 1 + Type + Boolean + Value + 0 + + StatsSummaryFile + + Comment + Filename for stats logging summary + Persist + 1 + Type + String + Value + fss.txt + + SystemLanguage + + Comment + Language indicated by system settings (for UI) + Persist + 1 + Type + String + Value + en + + TabToTextFieldsOnly + + Comment + TAB key takes you to next text entry field, instead of next widget + Persist + 1 + Type + Boolean + Value + 0 + + TerrainColorHeightRange + + Comment + Altitude range over which a given terrain texture has effect (meters) + Persist + 1 + Type + F32 + Value + 60.0 + + TerrainColorStartHeight + + Comment + Starting altitude for terrain texturing (meters) + Persist + 1 + Type + F32 + Value + 20.0 + + TextureDecodeDisabled + + Comment + If TRUE, do not fetch and decode any textures + Persist + 1 + Type + Boolean + Value + 0 + + TextureDisable + + Comment + If TRUE, do not load textures for in-world content + Persist + 1 + Type + Boolean + Value + 0 + + TextureDiscardLevel + + Comment + Specify texture resolution (0 = highest, 5 = lowest) + Persist + 1 + Type + U32 + Value + 0 + + TextureLoadFullRes + + Comment + If TRUE, always load textures at full resolution (discard = 0) + Persist + 1 + Type + Boolean + Value + 0 + + TextureMemory + + Comment + Amount of memory to use for textures in MB (0 = autodetect) + Persist + 1 + Type + S32 + Value + 0 + + TexturePickerShowFolders + + Comment + Show folders with no texures in texture picker + Persist + 1 + Type + Boolean + Value + 1 + + TexturePickerSortOrder + + Comment + Specifies sort key for textures in texture picker (+0 = name, +1 = date, +2 = folders always by name, +4 = system folders to top) + Persist + 1 + Type + U32 + Value + 2 + + ThrottleBandwidthKBPS + + Comment + Maximum allowable downstream bandwidth (kilo bits per second) + Persist + 1 + Type + F32 + Value + 500.0 + + UpdaterMaximumBandwidth + + Comment + Maximum allowable downstream bandwidth for updater service (kilo bits per second) + Persist + 1 + Type + F32 + Value + 500.0 + + ToolTipDelay + + Comment + Seconds before displaying tooltip when mouse stops over UI element + Persist + 1 + Type + F32 + Value + 0.699999988079 + + ToolTipFadeTime + + Comment + Seconds over which tooltip fades away + Persist + 1 + Type + F32 + Value + 0.2 + + ToolTipVisibleTimeFar + + Comment + Fade tooltip after after time passes (seconds) while mouse not near tooltip + Persist + 1 + Type + F32 + Value + 1.0 + + ToolTipVisibleTimeNear + + Comment + Fade tooltip after after time passes (seconds) while mouse near tooltip or original position + Persist + 1 + Type + F32 + Value + 10.0 + + ToolTipVisibleTimeOver + + Comment + Fade tooltip after after time passes (seconds) while mouse over tooltip + Persist + 1 + Type + F32 + Value + 1000.0 + + ToolboxAutoMove + + Comment + [NOT USED] + Persist + 1 + Type + Boolean + Value + 0 + + TrackFocusObject + + Comment + Camera tracks last object zoomed on + Persist + 1 + Type + Boolean + Value + 1 + + TranslateLanguage + + Comment + Translate Language specifier + Persist + 1 + Type + String + Value + default + + TranslateChat + + Comment + Translate incoming chat messages + Persist + 1 + Type + Boolean + Value + 0 + + TutorialURL + + Comment + URL for tutorial menu item, set automatically during login + Persist + 0 + Type + String + Value + + + TypeAheadTimeout + + Comment + Time delay before clearing type-ahead buffer in lists (seconds) + Persist + 1 + Type + F32 + Value + 1.5 + + UIAutoScale + + Comment + Keep UI scale consistent across different resolutions + Persist + 1 + Type + Boolean + Value + 1 + + UIAvatariconctrlSymbolHPad + + Comment + UI Avatar Icon Control Symbol Horizontal Pad + Persist + 1 + Type + S32 + Value + 2 + + UIAvatariconctrlSymbolVPad + + Comment + UI Avatar Icon Control Symbol Vertical Pad + Persist + 1 + Type + S32 + Value + 2 + + UIAvatariconctrlSymbolSize + + Comment + UI Avatar Icon Control Symbol Size + Persist + 1 + Type + S32 + Value + 5 + + UIAvatariconctrlSymbolPosition + + Comment + UI Avatar Icon Control Symbol Position (TopLeft|TopRight|BottomLeft|BottomRight) + Persist + 1 + Type + String + Value + BottomRight + + UIButtonOrigHPad + + Comment + UI Button Original Horizontal Pad + Persist + 1 + Type + S32 + Value + 6 + + UICheckboxctrlBtnSize + + Comment + UI Checkbox Control Button Size + Persist + 1 + Type + S32 + Value + 13 + + UICheckboxctrlHeight + + Comment + UI Checkbox Control Height + Persist + 1 + Type + S32 + Value + 16 + + UICheckboxctrlHPad + + Comment + UI Checkbox Control Horizontal Pad + Persist + 1 + Type + S32 + Value + 2 + + UICheckboxctrlSpacing + + Comment + UI Checkbox Control Spacing + Persist + 1 + Type + S32 + Value + 5 + + UICheckboxctrlVPad + + Comment + UI Checkbox Control Vertical Pad + Persist + 1 + Type + S32 + Value + 2 + + UICloseBoxFromTop + + Comment + Distance from top of floater to top of close box icon, pixels + Persist + 1 + Type + S32 + Value + 5 + + UIExtraTriangleHeight + + Comment + UI extra triangle height + Persist + 1 + Type + S32 + Value + -2 + + UIExtraTriangleWidth + + Comment + UI extra triangle width + Persist + 1 + Type + S32 + Value + 2 + + UIFloaterCloseBoxSize + + Comment + Size of UI floater close box size + Persist + 1 + Type + S32 + Value + 16 + + UIFloaterHPad + + Comment + Size of UI floater horizontal pad + Persist + 1 + Type + S32 + Value + 6 + + UIFloaterTestBool + + Comment + Example saved setting for the test floater + Persist + 1 + Type + Boolean + Value + 0 + + UIFloaterTitleVPad + + Comment + Distance from top of floater to top of title string, pixels + Persist + 1 + Type + S32 + Value + 7 + + UIImgDefaultEyesUUID + + Comment + + Persist + 0 + Type + String + Value + 6522e74d-1660-4e7f-b601-6f48c1659a77 + + UIImgDefaultGlovesUUID + + Comment + + Persist + 0 + Type + String + Value + 5748decc-f629-461c-9a36-a35a221fe21f + + UIImgDefaultHairUUID + + Comment + + Persist + 0 + Type + String + Value + 7ca39b4c-bd19-4699-aff7-f93fd03d3e7b + + UIImgDefaultJacketUUID + + Comment + + Persist + 0 + Type + String + Value + 5748decc-f629-461c-9a36-a35a221fe21f + + UIImgDefaultPantsUUID + + Comment + + Persist + 0 + Type + String + Value + 5748decc-f629-461c-9a36-a35a221fe21f + + UIImgDefaultShirtUUID + + Comment + + Persist + 0 + Type + String + Value + 5748decc-f629-461c-9a36-a35a221fe21f + + UIImgDefaultShoesUUID + + Comment + + Persist + 0 + Type + String + Value + 5748decc-f629-461c-9a36-a35a221fe21f + + UIImgDefaultSkirtUUID + + Comment + + Persist + 0 + Type + String + Value + 5748decc-f629-461c-9a36-a35a221fe21f + + UIImgDefaultSocksUUID + + Comment + + Persist + 0 + Type + String + Value + 5748decc-f629-461c-9a36-a35a221fe21f + + UIImgDefaultAlphaUUID + + Comment + + Persist + 0 + Type + String + Value + 5748decc-f629-461c-9a36-a35a221fe21f + + UIImgDefaultUnderwearUUID + + Comment + + Persist + 0 + Type + String + Value + 5748decc-f629-461c-9a36-a35a221fe21f + + StartUpChannelUUID + + Comment + + Persist + 0 + Type + String + Value + B56AF90D-6684-48E4-B1E4-722D3DEB2CB6 + + NearByChatChannelUUID + + Comment + + Persist + 0 + Type + String + Value + E1158BD6-661C-4981-9DAD-4DCBFF062502 + + NotificationChannelUUID + + Comment + + Persist + 0 + Type + String + Value + AEED3193-8709-4693-8558-7452CCA97AE5 + + AlertChannelUUID + + Comment + + Persist + 0 + Type + String + Value + F3E07BC8-A973-476D-8C7F-F3B7293975D1 + + UIImgWhiteUUID + + Comment + + Persist + 0 + Type + String + Value + 5748decc-f629-461c-9a36-a35a221fe21f + + UILineEditorCursorThickness + + Comment + UI Line Editor Cursor Thickness + Persist + 1 + Type + S32 + Value + 2 + + UIMaxComboWidth + + Comment + Maximum width of combo box + Persist + 1 + Type + S32 + Value + 500 + + UIMinimizedWidth + + Comment + Size of UI floater minimized width + Persist + 1 + Type + S32 + Value + 160 + + UIMultiSliderctrlSpacing + + Comment + UI multi slider ctrl spacing + Persist + 1 + Type + S32 + Value + 4 + + UIMultiTrackHeight + + Comment + UI multi track height + Persist + 1 + Type + S32 + Value + 6 + + UIPreeditMarkerBrightness + + Comment + UI Preedit Marker Brightness + Persist + 1 + Type + F32 + Value + 0.4 + + UIPreeditMarkerGap + + Comment + UI Preedit Marker Gap + Persist + 1 + Type + S32 + Value + 1 + + UIPreeditMarkerPosition + + Comment + UI Preedit Marker Position + Persist + 1 + Type + S32 + Value + 4 + + UIPreeditMarkerThickness + + Comment + UI Preedit Marker Thickness + Persist + 1 + Type + S32 + Value + 1 + + UIPreeditStandoutBrightness + + Comment + UI Preedit Standout Brightness + Persist + 1 + Type + F32 + Value + 0.6 + + UIPreeditStandoutGap + + Comment + UI Preedit Standout Gap + Persist + 1 + Type + S32 + Value + 1 + + UIPreeditStandoutPosition + + Comment + UI Preedit Standout Position + Persist + 1 + Type + S32 + Value + 4 + + UIPreeditStandoutThickness + + Comment + UI Preedit Standout Thickness + Persist + 1 + Type + S32 + Value + 2 + + UIResizeBarHeight + + Comment + Size of UI resize bar height + Persist + 1 + Type + S32 + Value + 3 + + UIResizeBarOverlap + + Comment + Size of UI resize bar overlap + Persist + 1 + Type + S32 + Value + 1 + + UIScaleFactor + + Comment + Size of UI relative to default layout on 1024x768 screen + Persist + 1 + Type + F32 + Value + 1.0 + + UIScrollbarSize + + Comment + UI scrollbar size + Persist + 1 + Type + S32 + Value + 15 + + UISliderctrlHeight + + Comment + UI slider ctrl height + Persist + 1 + Type + S32 + Value + 16 + + UISliderctrlSpacing + + Comment + UI slider ctrl spacing + Persist + 1 + Type + S32 + Value + 4 + + UISndAlert + + Comment + Sound file for alerts (uuid for sound asset) + Persist + 1 + Type + String + Value + ed124764-705d-d497-167a-182cd9fa2e6c + + UISndBadKeystroke + + Comment + Sound file for invalid keystroke (uuid for sound asset) + Persist + 1 + Type + String + Value + 2ca849ba-2885-4bc3-90ef-d4987a5b983a + + UISndClick + + Comment + Sound file for mouse click (uuid for sound asset) + Persist + 1 + Type + String + Value + 4c8c3c77-de8d-bde2-b9b8-32635e0fd4a6 + + UISndClickRelease + + Comment + Sound file for mouse button release (uuid for sound asset) + Persist + 1 + Type + String + Value + 4c8c3c77-de8d-bde2-b9b8-32635e0fd4a6 + + UISndDebugSpamToggle + + Comment + Log UI sound effects as they are played + Persist + 1 + Type + Boolean + Value + 0 + + UISndHealthReductionF + + Comment + Sound file for female pain (uuid for sound asset) + Persist + 1 + Type + String + Value + 219c5d93-6c09-31c5-fb3f-c5fe7495c115 + + UISndHealthReductionM + + Comment + Sound file for male pain (uuid for sound asset) + Persist + 1 + Type + String + Value + e057c244-5768-1056-c37e-1537454eeb62 + + UISndHealthReductionThreshold + + Comment + Amount of health reduction required to trigger "pain" sound + Persist + 1 + Type + F32 + Value + 10.0 + + UISndInvalidOp + + Comment + Sound file for invalid operations (uuid for sound asset) + Persist + 1 + Type + String + Value + 4174f859-0d3d-c517-c424-72923dc21f65 + + UISndMoneyChangeDown + + Comment + Sound file for L$ balance increase (uuid for sound asset) + Persist + 1 + Type + String + Value + 104974e3-dfda-428b-99ee-b0d4e748d3a3 + + UISndMoneyChangeThreshold + + Comment + Amount of change in L$ balance required to trigger "money" sound + Persist + 1 + Type + F32 + Value + 50.0 + + UISndMoneyChangeUp + + Comment + Sound file for L$ balance decrease(uuid for sound asset) + Persist + 1 + Type + String + Value + 77a018af-098e-c037-51a6-178f05877c6f + + UISndNewIncomingIMSession + + Comment + Sound file for new instant message session(uuid for sound asset) + Persist + 1 + Type + String + Value + 67cc2844-00f3-2b3c-b991-6418d01e1bb7 + + UISndObjectCreate + + Comment + Sound file for object creation (uuid for sound asset) + Persist + 1 + Type + String + Value + f4a0660f-5446-dea2-80b7-6482a082803c + + UISndObjectDelete + + Comment + Sound file for object deletion (uuid for sound asset) + Persist + 1 + Type + String + Value + 0cb7b00a-4c10-6948-84de-a93c09af2ba9 + + UISndObjectRezIn + + Comment + Sound file for rezzing objects (uuid for sound asset) + Persist + 1 + Type + String + Value + 3c8fc726-1fd6-862d-fa01-16c5b2568db6 + + UISndObjectRezOut + + Comment + Sound file for derezzing objects (uuid for sound asset) + Persist + 1 + Type + String + Value + 00000000-0000-0000-0000-000000000000 + + UISndSnapshot + + Comment + Sound file for taking a snapshot (uuid for sound asset) + Persist + 1 + Type + String + Value + 3d09f582-3851-c0e0-f5ba-277ac5c73fb4 + + UISndStartIM + + Comment + Sound file for starting a new IM session (uuid for sound asset) + Persist + 1 + Type + String + Value + c825dfbc-9827-7e02-6507-3713d18916c1 + + UISndTeleportOut + + Comment + Sound file for teleporting (uuid for sound asset) + Persist + 1 + Type + String + Value + d7a9a565-a013-2a69-797d-5332baa1a947 + + UISndTyping + + Comment + Sound file for starting to type a chat message (uuid for sound asset) + Persist + 1 + Type + String + Value + 5e191c7b-8996-9ced-a177-b2ac32bfea06 + + UISndWindowClose + + Comment + Sound file for closing a window (uuid for sound asset) + Persist + 1 + Type + String + Value + 2c346eda-b60c-ab33-1119-b8941916a499 + + UISndWindowOpen + + Comment + Sound file for opening a window (uuid for sound asset) + Persist + 1 + Type + String + Value + c80260ba-41fd-8a46-768a-6bf236360e3a + + UISpinctrlBtnHeight + + Comment + UI spin control button height + Persist + 1 + Type + S32 + Value + 11 + + UISpinctrlBtnWidth + + Comment + UI spin control button width + Persist + 1 + Type + S32 + Value + 16 + + UISpinctrlDefaultLabelWidth + + Comment + UI spin control default label width + Persist + 1 + Type + S32 + Value + 10 + + UISpinctrlSpacing + + Comment + UI spin control spacing + Persist + 1 + Type + S32 + Value + 2 + + UITabCntrArrowBtnSize + + Comment + UI Tab Container Arrow Button Size + Persist + 1 + Type + S32 + Value + 16 + + UITabCntrvArrowBtnSize + + Comment + UI Tab Container V Arrow Button Size + Persist + 1 + Type + S32 + Value + 16 + + UITabCntrvPad + + Comment + UI Tab Container V Pad + Persist + 1 + Type + S32 + Value + 0 + + UITabCntrButtonPanelOverlap + + Comment + UI Tab Container Button Panel Overlap + Persist + 1 + Type + S32 + Value + 1 + + UITabCntrCloseBtnSize + + Comment + UI Tab Container Close Button Size + Persist + 1 + Type + S32 + Value + 16 + + UITabCntrTabHPad + + Comment + UI Tab Container Tab Horizontal Pad + Persist + 1 + Type + S32 + Value + 4 + + UITabCntrTabPartialWidth + + Comment + UI Tab Container Tab Partial Width + Persist + 1 + Type + S32 + Value + 12 + + UITabCntrVertTabMinWidth + + Comment + UI Tab Container Vertical Tab Minimum Width + Persist + 1 + Type + S32 + Value + 100 + + UITabPadding + + Comment + UI Tab Padding + Persist + 1 + Type + S32 + Value + 15 + + UpdaterServiceSetting + + Comment + Configure updater service. + Persist + 1 + Type + U32 + Value + 3 + + UpdaterServiceCheckPeriod + + Comment + Default period between update checking. + Persist + 1 + Type + U32 + Value + 3600 + + UpdaterServiceURL + + Comment + Default location for the updater service. + Persist + 0 + Type + String + Value + https://update.secondlife.com + + UpdaterServicePath + + Comment + Path on the update server host. + Persist + 0 + Type + String + Value + update + + UpdaterServiceProtocolVersion + + Comment + The update protocol version to use. + Persist + 0 + Type + String + Value + v1.0 + + UploadBakedTexOld + + Comment + Forces the baked texture pipeline to upload using the old method. + Persist + 1 + Type + Boolean + Value + 0 + + UseAltKeyForMenus + + Comment + Access menus via keyboard by tapping Alt + Persist + 1 + Type + Boolean + Value + 0 + + UseChatBubbles + + Comment + Show chat above avatars head in chat bubbles + Persist + 1 + Type + Boolean + Value + 0 + + UseCircuitCodeMaxRetries + + Comment + Max timeout count for the initial UseCircuitCode message + Persist + 0 + Type + S32 + Value + 3 + + UseCircuitCodeTimeout + + Comment + Timeout duration in seconds for the initial UseCircuitCode message + Persist + 0 + Type + F32 + Value + 5.0 + + UseDebugLogin + + Comment + Provides extra control over which grid to connect to + Persist + 1 + Type + Boolean + Value + 0 + + UseDebugMenus + + Comment + Turns on "Debug" menu + Persist + 1 + Type + Boolean + Value + 0 + + UseDefaultColorPicker + + Comment + Use color picker supplied by operating system + Persist + 1 + Type + Boolean + Value + 0 + + UseDisplayNames + + Comment + Use new, changeable, unicode names + Persist + 1 + Type + Boolean + Value + 1 + + UseEnergy + + Comment + + Persist + 0 + Type + Boolean + Value + 1 + + UseExternalBrowser + + Comment + Use default browser when opening web pages instead of in-world browser. + Persist + 1 + Type + Boolean + Value + 1 + + UseFreezeFrame + + Comment + Freeze time when taking snapshots. + Persist + 1 + Type + Boolean + Value + 0 + + UseOcclusion + + Comment + Enable object culling based on occlusion (coverage) by other objects + Persist + 1 + Type + Boolean + Value + 1 + + RenderDelayVBUpdate + + Comment + Delay vertex buffer updates until just before rendering + Persist + 1 + Type + Boolean + Value + 1 + + SpeakerParticipantDefaultOrder + + Comment + Order for displaying speakers in voice controls. 0 = alphabetical. 1 = recent. + Persist + 1 + Type + U32 + Value + 1 + + SpeakerParticipantRemoveDelay + + Comment + Timeout to remove participants who is not in channel before removed from list of active speakers (text/voice chat) + Persist + 1 + Type + F32 + Value + 10.0 + + UseNewWalkRun + + Comment + Replace standard walk/run animations with new ones. + Persist + 1 + Type + Boolean + Value + 1 + + UseStartScreen + + Comment + Whether to load a start screen image or not. + Persist + 1 + Type + Boolean + Value + 1 + + UseWebPagesOnPrims + + Comment + [NOT USED] + Persist + 1 + Type + Boolean + Value + 0 + + UserConnectionPort + + Comment + Port that this client transmits on. + Persist + 1 + Type + U32 + Value + 0 + + UserLogFile + + Comment + User specified log file name. + Persist + 1 + Type + String + Value + + + UserLoginInfo + + Comment + Users loging data. + Persist + 1 + Type + LLSD + Value + + VFSOldSize + + Comment + [DO NOT MODIFY] Controls resizing of local file cache + Persist + 1 + Type + U32 + Value + 0 + + VFSSalt + + Comment + [DO NOT MODIFY] Controls local file caching behavior + Persist + 1 + Type + U32 + Value + 1 + + VectorizeEnable + + Comment + Enable general vector operations and data alignment. + Persist + 1 + Type + Boolean + Value + 0 + + VectorizePerfTest + + Comment + Test SSE/vectorization performance and choose fastest version. + Persist + 1 + Type + Boolean + Value + 1 + + VectorizeProcessor + + Comment + 0=Compiler Default, 1=SSE, 2=SSE2, autodetected + Persist + 0 + Type + U32 + Value + 0 + + VectorizeSkin + + Comment + Enable vector operations for avatar skinning. + Persist + 1 + Type + Boolean + Value + 1 + + VelocityInterpolate + + Comment + Extrapolate object motion from last packet based on received velocity + Persist + 1 + Type + Boolean + Value + 1 + + InterpolationTime + + Comment + How long to extrapolate object motion after last packet received + Persist + 1 + Type + F32 + Value + 3 + + InterpolationPhaseOut + + Comment + Seconds to phase out interpolated motion + Persist + 1 + Type + F32 + Value + 1 + + VerboseLogs + + Comment + Display source file and line number for each log item for debugging purposes + Persist + 1 + Type + Boolean + Value + 0 + + VertexShaderEnable + + Comment + Enable/disable all GLSL shaders (debug) + Persist + 1 + Type + Boolean + Value + 0 + + VivoxAutoPostCrashDumps + + Comment + If true, SLVoice will automatically send crash dumps directly to Vivox. + Persist + 1 + Type + Boolean + Value + 0 + + VivoxDebugLevel + + Comment + Logging level to use when launching the vivox daemon + Persist + 1 + Type + String + Value + -1 + + VivoxDebugSIPURIHostName + + Comment + Hostname portion of vivox SIP URIs (empty string for the default). + Persist + 1 + Type + String + Value + + + VivoxDebugVoiceAccountServerURI + + Comment + URI to the vivox account management server (empty string for the default). + Persist + 1 + Type + String + Value + + + VivoxVoiceHost + + Comment + Client SLVoice host to connect to + Persist + 1 + Type + String + Value + 127.0.0.1 + + VivoxVoicePort + + Comment + Client SLVoice port to connect to + Persist + 1 + Type + U32 + Value + 44125 + + VoiceCallsFriendsOnly + + Comment + Only accept voice calls from residents on your friends list + Persist + 1 + Type + Boolean + Value + 0 + + VoiceCallsRejectAll + + Comment + Silently reject all incoming voice calls. + Persist + 1 + Type + Boolean + Value + 0 + + VoiceDisableMic + + Comment + Completely disable the ability to open the mic. + Persist + 1 + Type + Boolean + Value + 0 + + VoiceEffectExpiryWarningTime + + Comment + How much notice to give of Voice Morph subscriptions expiry, in seconds. + Persist + 1 + Type + S32 + Value + 259200 + + VoiceMorphingEnabled + + Comment + Whether or not to enable Voice Morphs and show the UI. + Persist + 0 + Type + Boolean + Value + 1 + + AutoDisengageMic + + Comment + Automatically turn off the microphone when ending IM calls. + Persist + 1 + Type + Boolean + Value + 1 + + VoiceEarLocation + + Comment + Location of the virtual ear for voice + Persist + 1 + Type + S32 + Value + 0 + + VoiceHost + + Comment + Client SLVoice host to connect to + Persist + 1 + Type + String + Value + 127.0.0.1 + + VoiceImageLevel0 + + Comment + Texture UUID for voice image level 0 + Persist + 1 + Type + String + Value + 041ee5a0-cb6a-9ac5-6e49-41e9320507d5 + + VoiceImageLevel1 + + Comment + Texture UUID for voice image level 1 + Persist + 1 + Type + String + Value + 29de489d-0491-fb00-7dab-f9e686d31e83 + + VoiceImageLevel2 + + Comment + Texture UUID for voice image level 2 + Persist + 1 + Type + String + Value + 29de489d-0491-fb00-7dab-f9e686d31e83 + + VoiceImageLevel3 + + Comment + Texture UUID for voice image level 3 + Persist + 1 + Type + String + Value + 29de489d-0491-fb00-7dab-f9e686d31e83 + + VoiceImageLevel4 + + Comment + Texture UUID for voice image level 4 + Persist + 1 + Type + String + Value + 29de489d-0491-fb00-7dab-f9e686d31e83 + + VoiceImageLevel5 + + Comment + Texture UUID for voice image level 5 + Persist + 1 + Type + String + Value + 29de489d-0491-fb00-7dab-f9e686d31e83 + + VoiceImageLevel6 + + Comment + Texture UUID for voice image level 6 + Persist + 1 + Type + String + Value + 29de489d-0491-fb00-7dab-f9e686d31e83 + + VoiceInputAudioDevice + + Comment + Audio input device to use for voice + Persist + 1 + Type + String + Value + Default + + VoiceLogFile + + Comment + Log file to use when launching the voice daemon + Persist + 1 + Type + String + Value + + + VoiceOutputAudioDevice + + Comment + Audio output device to use for voice + Persist + 1 + Type + String + Value + Default + + VoiceParticipantLeftRemoveDelay + + Comment + Timeout to remove participants who has left Voice chat from the list in Voice Controls Panel + Persist + 1 + Type + S32 + Value + 10 + + VoicePort + + Comment + Client SLVoice port to connect to + Persist + 1 + Type + U32 + Value + 44125 + + WarningsAsChat + + Comment + Display warning messages in chat history + Persist + 1 + Type + Boolean + Value + 0 + + VoiceServerType + + Comment + The type of voice server to connect to. + Persist + 0 + Type + String + Value + vivox + + WLSkyDetail + + Comment + Controls vertex detail on the WindLight sky. Lower numbers will give better performance and uglier skies. + Persist + 1 + Type + U32 + Value + 64 + + WatchdogEnabled + + Comment + Controls whether the thread watchdog timer is activated. + Persist + 0 + Type + S32 + Value + -1 + + WaterEditPresets + + Comment + Whether to be able to edit the water defaults or not + Persist + 1 + Type + Boolean + Value + 0 + + WaterGLFogDensityScale + + Comment + Maps shader water fog density to gl fog density + Persist + 1 + Type + F32 + Value + 0.02 + + WaterGLFogDepthFloor + + Comment + Controls how dark water gl fog can get + Persist + 1 + Type + F32 + Value + 0.25 + + WaterGLFogDepthScale + + Comment + Controls how quickly gl fog gets dark under water + Persist + 1 + Type + F32 + Value + 50.0 + + WellIconFlashCount + + Comment + Number of flashes of IM Well and Notification Well icons after which flashing buttons stay lit up. Requires restart. + Persist + 1 + Type + S32 + Value + 3 + + WellIconFlashPeriod + + Comment + Period at which IM Well and Notification Well icons flash (seconds). Requires restart. + Persist + 1 + Type + F32 + Value + 0.25 + + WindLightUseAtmosShaders + + Comment + Whether to enable or disable WindLight atmospheric shaders. + Persist + 1 + Type + Boolean + Value + 1 + + WindowFullScreen + + Comment + SL viewer window full screen + Persist + 1 + Type + Boolean + Value + 0 + + WindowHeight + + Comment + SL viewer window height + Persist + 1 + Type + S32 + Value + 738 + + WindowMaximized + + Comment + SL viewer window maximized on login + Persist + 1 + Type + Boolean + Value + 0 + + WindowWidth + + Comment + SL viewer window width + Persist + 1 + Type + S32 + Value + 1024 + + WindowX + + Comment + X coordinate of lower left corner of SL viewer window, relative to primary display (pixels) + Persist + 1 + Type + S32 + Value + 10 + + WindowY + + Comment + Y coordinate of lower left corner of SL viewer window, relative to primary display (pixels) + Persist + 1 + Type + S32 + Value + 10 + + XferThrottle + + Comment + Maximum allowable downstream bandwidth for asset transfers (bits per second) + Persist + 1 + Type + F32 + Value + 150000.0 + + ExternalEditor + + Comment + Path to program used to edit LSL scripts and XUI files, e.g.: /usr/bin/gedit --new-window "%s" + Persist + 1 + Type + String + Value + + + YawFromMousePosition + + Comment + Horizontal range over which avatar head tracks mouse position (degrees of head rotation from left of window to right) + Persist + 1 + Type + F32 + Value + 90.0 + + YouAreHereDistance + + Comment + Radius of distance for banner that indicates if the resident is "on" the Place.(meters from avatar to requested place) + Persist + 1 + Type + F32 + Value + 10.0 + + YieldTime + + Comment + Yield some time to the local host. + Persist + 1 + Type + S32 + Value + -1 + + ZoomDirect + + Comment + Map Joystick zoom axis directly to camera zoom. + Persist + 1 + Type + Boolean + Value + 0 + + ZoomTime + + Comment + Time of transition between different camera modes (seconds) + Persist + 1 + Type + F32 + Value + 0.40000000596 + + particlesbeacon + + Comment + Beacon / Highlight particle generators + Persist + 1 + Type + Boolean + Value + 0 + + physicalbeacon + + Comment + Beacon / Highlight physical objects + Persist + 1 + Type + Boolean + Value + 1 + + renderbeacons + + Comment + Beacon / Highlight particle generators + Persist + 1 + Type + Boolean + Value + 0 + + renderhighlights + + Comment + Beacon / Highlight scripted objects with touch function + Persist + 1 + Type + Boolean + Value + 1 + + scriptsbeacon + + Comment + Beacon / Highlight scripted objects + Persist + 1 + Type + Boolean + Value + 0 + + scripttouchbeacon + + Comment + Beacon / Highlight scripted objects with touch function + Persist + 1 + Type + Boolean + Value + 1 + + ShowDeviceSettings + + Comment + Show device settings + Persist + 1 + Type + Boolean + Value + 0 + + SLURLDragNDrop + + Comment + Enable drag and drop of SLURLs onto the viewer + Persist + 1 + Type + Boolean + Value + 1 + + soundsbeacon + + Comment + Beacon / Highlight sound generators + Persist + 1 + Type + Boolean + Value + 0 + + LogTextureDownloadsToViewerLog + + Comment + Send texture download details to the viewer log + Persist + 1 + Type + Boolean + Value + 0 + + LogTextureDownloadsToSimulator + + Comment + Send a digest of texture info to the sim + Persist + 1 + Type + Boolean + Value + 0 + + TextureLoggingThreshold + + Comment + Specifies the byte threshold at which texture download data should be sent to the sim. + Persist + 1 + Type + U32 + Value + 1 + + + + + ShowVoiceChannelPopup + + Comment + Controls visibility of the current voice channel popup above the voice tab + Persist + 1 + Type + Boolean + Value + 0 + + ShowVolumeSettingsPopup + + Comment + Show individual volume slider for voice, sound effects, etc + Persist + 1 + Type + Boolean + Value + 0 + + max_texture_dimension_X + + Comment + Maximum texture width for user uploaded textures + Persist + 1 + Type + S32 + Value + 2048 + + max_texture_dimension_Y + + Comment + Maximum texture height for user uploaded textures + Persist + 1 + Type + S32 + Value + 2048 + + + teleport_offer_invitation_max_length + + Comment + Maximum length of teleport offer invitation line editor. 254 - max_location_url_length(76) = 178 + Persist + 1 + Type + S32 + Value + 178 + + always_showable_floaters + + Comment + Floaters that can be shown despite mouselook mode + Persist + 1 + Type + LLSD + Value + + snapshot + postcard + mini_map + + + LandmarksSortedByDate + + Comment + Reflects landmarks panel sorting order. + Persist + 1 + Type + Boolean + Value + 1 + + OutfitOperationsTimeout + + Comment + Timeout for outfit related operations. + Persist + 1 + Type + S32 + Value + 180 + + HeightUnits + + Comment + Determines which metric units are used: 1(TRUE) for meter and 0(FALSE) for foot. + Persist + 1 + Type + Boolean + Value + 1 + + TipToastMessageLineCount + + Comment + Max line count of text message on tip toast. + Persist + 1 + Type + S32 + Value + 10 + + NotMovingHintTimeout + + Comment + Number of seconds to wait for resident to move before displaying move hint. + Persist + 1 + Type + F32 + Value + 120.0 + + DestinationGuideHintTimeout + + Comment + Number of seconds to wait before telling resident about destination guide. + Persist + 1 + Type + F32 + Value + 1200.0 + + SidePanelHintTimeout + + Comment + Number of seconds to wait before telling resident about side panel. + Persist + 1 + Type + F32 + Value + 300.0 + + GroupMembersSortOrder + + Comment + The order by which group members will be sorted (name|donated|online) + Persist + 1 + Type + String + Value + name + + SessionSettingsFile + + Comment + Settings that are a applied per session (not saved). + Persist + 1 + Type + String + Value + + + UserSessionSettingsFile + + Comment + User settings that are a applied per session (not saved). + Persist + 1 + Type + String + Value + + + OpenSidePanelsInFloaters + + Comment + If true, will always open side panel contents in a floater. + Persist + 1 + Type + Boolean + Value + 0 + + AvatarInspectorTooltipDelay + + Comment + Seconds before displaying avatar inspector tooltip + Persist + 1 + Type + F32 + Value + 0.35 + + ObjectInspectorTooltipDelay + + Comment + Seconds before displaying object inspector tooltip + Persist + 1 + Type + F32 + Value + 0.35 + + SLURLTeleportDirectly + + Comment + Clicking on a slurl will teleport you directly instead of opening places panel + Persist + 1 + Type + Boolean + Value + 0 + + EnableClassifieds + + Comment + Enable creation of new classified ads from web link + Persist + 1 + Type + Boolean + Value + 1 + + EnableGroupInfo + + Comment + Enable viewing and editing of group info from web link + Persist + 1 + Type + Boolean + Value + 1 + + EnablePicks + + Comment + Enable editing of picks from web link + Persist + 1 + Type + Boolean + Value + 1 + + EnableWorldMap + + Comment + Enable opening world map from web link + Persist + 1 + Type + Boolean + Value + 1 + + SearchFromAddressBar + + Comment + Can enter search queries into navigation address bar + Persist + 1 + Type + Boolean + Value + 1 + + LogInventoryDecline + + Comment + Log in system chat whenever an inventory offer is declined + Persist + 1 + Type + Boolean + Value + 1 + + UseHTTPInventory + + Comment + Allow use of http inventory transfers instead of UDP + Persist + 1 + Type + Boolean + Value + 1 + + + diff --git a/indra/newview/generate_breakpad_symbols.py b/indra/newview/generate_breakpad_symbols.py index 4fd04d780e..5ebec1563e 100644 --- a/indra/newview/generate_breakpad_symbols.py +++ b/indra/newview/generate_breakpad_symbols.py @@ -1,29 +1,31 @@ #!/usr/bin/env python -# @file generate_breakpad_symbols.py -# @author Brad Kittenbrink -# @brief Simple tool for generating google_breakpad symbol information -# for the crash reporter. -# -# $LicenseInfo:firstyear=2010&license=viewerlgpl$ -# Second Life Viewer Source Code -# Copyright (C) 2010, Linden Research, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; -# version 2.1 of the License only. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -# -# Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA -# $/LicenseInfo$ +"""\ +@file generate_breakpad_symbols.py +@author Brad Kittenbrink +@brief Simple tool for generating google_breakpad symbol information + for the crash reporter. + +$LicenseInfo:firstyear=2010&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2010-2011, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" import collections diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index c8b7895d3f..6d61be84ef 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1,5137 +1,5137 @@ - /** - * @file llappviewer.cpp - * @brief The LLAppViewer class definitions - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llappviewer.h" - -// Viewer includes -#include "llversioninfo.h" -#include "llversionviewer.h" -#include "llfeaturemanager.h" -#include "lluictrlfactory.h" -#include "lltexteditor.h" -#include "llerrorcontrol.h" -#include "lleventtimer.h" -#include "llviewertexturelist.h" -#include "llgroupmgr.h" -#include "llagent.h" -#include "llagentcamera.h" -#include "llagentlanguage.h" -#include "llagentwearables.h" -#include "llwindow.h" -#include "llviewerstats.h" -#include "llviewerstatsrecorder.h" -#include "llmd5.h" -#include "llpumpio.h" -#include "llmimetypes.h" -#include "llslurl.h" -#include "llstartup.h" -#include "llfocusmgr.h" -#include "llviewerjoystick.h" -#include "llallocator.h" -#include "llares.h" -#include "llcurl.h" -#include "lltexturestats.h" -#include "lltexturestats.h" -#include "llviewerwindow.h" -#include "llviewerdisplay.h" -#include "llviewermedia.h" -#include "llviewerparcelmedia.h" -#include "llviewermediafocus.h" -#include "llviewermessage.h" -#include "llviewerobjectlist.h" -#include "llworldmap.h" -#include "llmutelist.h" -#include "llviewerhelp.h" -#include "lluicolortable.h" -#include "llurldispatcher.h" -#include "llurlhistory.h" -//#include "llfirstuse.h" -#include "llrender.h" -#include "llteleporthistory.h" -#include "lllocationhistory.h" -#include "llfasttimerview.h" -#include "llvoicechannel.h" -#include "llvoavatarself.h" -#include "llsidetray.h" -#include "llfeaturemanager.h" -#include "llurlmatch.h" -#include "lltextutil.h" -#include "lllogininstance.h" -#include "llprogressview.h" - -#include "llweb.h" -#include "llsecondlifeurls.h" -#include "llupdaterservice.h" - -// Linden library includes -#include "llavatarnamecache.h" -#include "llimagej2c.h" -#include "llmemory.h" -#include "llprimitive.h" -#include "llurlaction.h" -#include "llurlentry.h" -#include "llvfile.h" -#include "llvfsthread.h" -#include "llvolumemgr.h" -#include "llxfermanager.h" - -#include "llnotificationmanager.h" -#include "llnotifications.h" -#include "llnotificationsutil.h" - -// Third party library includes -#include - - -#if LL_WINDOWS -# include // For _SH_DENYWR in initMarkerFile -#else -# include // For initMarkerFile support -#endif - -#include "llapr.h" -#include "apr_dso.h" -#include - -#include "llviewerkeyboard.h" -#include "lllfsthread.h" -#include "llworkerthread.h" -#include "lltexturecache.h" -#include "lltexturefetch.h" -#include "llimageworker.h" -#include "llevents.h" - -// The files below handle dependencies from cleanup. -#include "llkeyframemotion.h" -#include "llworldmap.h" -#include "llhudmanager.h" -#include "lltoolmgr.h" -#include "llassetstorage.h" -#include "llpolymesh.h" -#include "llcachename.h" -#include "llaudioengine.h" -#include "llstreamingaudio.h" -#include "llviewermenu.h" -#include "llselectmgr.h" -#include "lltrans.h" -#include "lltransutil.h" -#include "lltracker.h" -#include "llviewerparcelmgr.h" -#include "llworldmapview.h" -#include "llpostprocess.h" -#include "llwlparammanager.h" -#include "llwaterparammanager.h" - -#include "lldebugview.h" -#include "llconsole.h" -#include "llcontainerview.h" -#include "lltooltip.h" - -#include "llsdserialize.h" - -#include "llworld.h" -#include "llhudeffecttrail.h" -#include "llvectorperfoptions.h" -#include "llslurl.h" -#include "llwatchdog.h" - -// Included so that constants/settings might be initialized -// in save_settings_to_globals() -#include "llbutton.h" -#include "llstatusbar.h" -#include "llsurface.h" -#include "llvosky.h" -#include "llvotree.h" -#include "llvoavatar.h" -#include "llfolderview.h" -#include "llagentpilot.h" -#include "llvovolume.h" -#include "llflexibleobject.h" -#include "llvosurfacepatch.h" -#include "llviewerfloaterreg.h" -#include "llcommandlineparser.h" -#include "llfloatermemleak.h" -#include "llfloaterreg.h" -#include "llfloatersnapshot.h" -#include "llfloaterinventory.h" - -// includes for idle() idleShutdown() -#include "llviewercontrol.h" -#include "lleventnotifier.h" -#include "llcallbacklist.h" -#include "pipeline.h" -#include "llgesturemgr.h" -#include "llsky.h" -#include "llvlmanager.h" -#include "llviewercamera.h" -#include "lldrawpoolbump.h" -#include "llvieweraudio.h" -#include "llimview.h" -#include "llviewerthrottle.h" -#include "llparcel.h" -#include "llavatariconctrl.h" -#include "llgroupiconctrl.h" -#include "llviewerassetstats.h" - -// Include for security api initialization -#include "llsecapi.h" -#include "llmachineid.h" - -#include "llmainlooprepeater.h" - -// *FIX: These extern globals should be cleaned up. -// The globals either represent state/config/resource-storage of either -// this app, or another 'component' of the viewer. App globals should be -// moved into the app class, where as the other globals should be -// moved out of here. -// If a global symbol reference seems valid, it will be included -// via header files above. - -//---------------------------------------------------------------------------- -// llviewernetwork.h -#include "llviewernetwork.h" -// define a self-registering event API object -#include "llappviewerlistener.h" - -#if (LL_LINUX || LL_SOLARIS) && LL_GTK -#include "glib.h" -#endif // (LL_LINUX || LL_SOLARIS) && LL_GTK - -#if LL_MSVC -// disable boost::lexical_cast warning -#pragma warning (disable:4702) -#endif - -static LLAppViewerListener sAppViewerListener(LLAppViewer::instance); - -////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor -// -//---------------------------------------------------------------------------- -// viewer.cpp - these are only used in viewer, should be easily moved. - -#if LL_DARWIN -extern void init_apple_menu(const char* product); -#endif // LL_DARWIN - -extern BOOL gRandomizeFramerate; -extern BOOL gPeriodicSlowFrame; -extern BOOL gDebugGL; - -//////////////////////////////////////////////////////////// -// All from the last globals push... -const F32 DEFAULT_AFK_TIMEOUT = 5.f * 60.f; // time with no input before user flagged as Away From Keyboard - -F32 gSimLastTime; // Used in LLAppViewer::init and send_stats() -F32 gSimFrames; - -BOOL gShowObjectUpdates = FALSE; -BOOL gUseQuickTime = TRUE; - -eLastExecEvent gLastExecEvent = LAST_EXEC_NORMAL; - -LLSD gDebugInfo; - -U32 gFrameCount = 0; -U32 gForegroundFrameCount = 0; // number of frames that app window was in foreground -LLPumpIO* gServicePump = NULL; - -U64 gFrameTime = 0; -F32 gFrameTimeSeconds = 0.f; -F32 gFrameIntervalSeconds = 0.f; -F32 gFPSClamped = 10.f; // Pretend we start at target rate. -F32 gFrameDTClamped = 0.f; // Time between adjacent checks to network for packets -U64 gStartTime = 0; // gStartTime is "private", used only to calculate gFrameTimeSeconds -U32 gFrameStalls = 0; -const F64 FRAME_STALL_THRESHOLD = 1.0; - -LLTimer gRenderStartTime; -LLFrameTimer gForegroundTime; -LLFrameTimer gLoggedInTime; -LLTimer gLogoutTimer; -static const F32 LOGOUT_REQUEST_TIME = 6.f; // this will be cut short by the LogoutReply msg. -F32 gLogoutMaxTime = LOGOUT_REQUEST_TIME; - -BOOL gDisconnected = FALSE; - -// used to restore texture state after a mode switch -LLFrameTimer gRestoreGLTimer; -BOOL gRestoreGL = FALSE; -BOOL gUseWireframe = FALSE; - -// VFS globals - see llappviewer.h -LLVFS* gStaticVFS = NULL; - -LLMemoryInfo gSysMemory; -U64 gMemoryAllocated = 0; // updated in display_stats() in llviewerdisplay.cpp - -std::string gLastVersionChannel; - -LLVector3 gWindVec(3.0, 3.0, 0.0); -LLVector3 gRelativeWindVec(0.0, 0.0, 0.0); - -U32 gPacketsIn = 0; - -BOOL gPrintMessagesThisFrame = FALSE; - -BOOL gRandomizeFramerate = FALSE; -BOOL gPeriodicSlowFrame = FALSE; - -BOOL gCrashOnStartup = FALSE; -BOOL gLLErrorActivated = FALSE; -BOOL gLogoutInProgress = FALSE; - -//////////////////////////////////////////////////////////// -// Internal globals... that should be removed. -static std::string gArgs; - -const std::string MARKER_FILE_NAME("SecondLife.exec_marker"); -const std::string ERROR_MARKER_FILE_NAME("SecondLife.error_marker"); -const std::string LLERROR_MARKER_FILE_NAME("SecondLife.llerror_marker"); -const std::string LOGOUT_MARKER_FILE_NAME("SecondLife.logout_marker"); -static BOOL gDoDisconnect = FALSE; -static std::string gLaunchFileOnQuit; - -// Used on Win32 for other apps to identify our window (eg, win_setup) -const char* const VIEWER_WINDOW_CLASSNAME = "Second Life"; - -//---------------------------------------------------------------------------- - -// List of entries from strings.xml to always replace -static std::set default_trans_args; -void init_default_trans_args() -{ - default_trans_args.insert("SECOND_LIFE"); // World - default_trans_args.insert("APP_NAME"); - default_trans_args.insert("CAPITALIZED_APP_NAME"); - default_trans_args.insert("SECOND_LIFE_GRID"); - default_trans_args.insert("SUPPORT_SITE"); -} - -//---------------------------------------------------------------------------- -// File scope definitons -const char *VFS_DATA_FILE_BASE = "data.db2.x."; -const char *VFS_INDEX_FILE_BASE = "index.db2.x."; - - -struct SettingsFile : public LLInitParam::Block -{ - Mandatory name; - Optional file_name; - Optional required, - persistent; - Optional file_name_setting; - - SettingsFile() - : name("name"), - file_name("file_name"), - required("required", false), - persistent("persistent", true), - file_name_setting("file_name_setting") - {} -}; - -struct SettingsGroup : public LLInitParam::Block -{ - Mandatory name; - Mandatory path_index; - Multiple files; - - SettingsGroup() - : name("name"), - path_index("path_index"), - files("file") - {} -}; - -struct SettingsFiles : public LLInitParam::Block -{ - Multiple groups; - - SettingsFiles() - : groups("group") - {} -}; - -static std::string gWindowTitle; - -LLAppViewer::LLUpdaterInfo *LLAppViewer::sUpdaterInfo = NULL ; - -//---------------------------------------------------------------------------- -// Metrics logging control constants -//---------------------------------------------------------------------------- -static const F32 METRICS_INTERVAL_DEFAULT = 600.0; -static const F32 METRICS_INTERVAL_QA = 30.0; -static F32 app_metrics_interval = METRICS_INTERVAL_DEFAULT; -static bool app_metrics_qa_mode = false; - -void idle_afk_check() -{ - // check idle timers - if (gSavedSettings.getS32("AFKTimeout") && (gAwayTriggerTimer.getElapsedTimeF32() > gSavedSettings.getS32("AFKTimeout"))) - { - gAgent.setAFK(); - } -} - -// A callback set in LLAppViewer::init() -static void ui_audio_callback(const LLUUID& uuid) -{ - if (gAudiop) - { - gAudiop->triggerSound(uuid, gAgent.getID(), 1.0f, LLAudioEngine::AUDIO_TYPE_UI); - } -} - -bool create_text_segment_icon_from_url_match(LLUrlMatch* match,LLTextBase* base) -{ - if(!match || !base || base->getPlainText()) - return false; - - LLUUID match_id = match->getID(); - - LLIconCtrl* icon; - - if(gAgent.isInGroup(match_id, TRUE)) - { - LLGroupIconCtrl::Params icon_params; - icon_params.group_id = match_id; - icon_params.rect = LLRect(0, 16, 16, 0); - icon_params.visible = true; - icon = LLUICtrlFactory::instance().create(icon_params); - } - else - { - LLAvatarIconCtrl::Params icon_params; - icon_params.avatar_id = match_id; - icon_params.rect = LLRect(0, 16, 16, 0); - icon_params.visible = true; - icon = LLUICtrlFactory::instance().create(icon_params); - } - - LLInlineViewSegment::Params params; - params.force_newline = false; - params.view = icon; - params.left_pad = 4; - params.right_pad = 4; - params.top_pad = -2; - params.bottom_pad = 2; - - base->appendWidget(params," ",false); - - return true; -} - -void request_initial_instant_messages() -{ - static BOOL requested = FALSE; - if (!requested - && gMessageSystem - && LLMuteList::getInstance()->isLoaded() - && isAgentAvatarValid()) - { - // Auto-accepted inventory items may require the avatar object - // to build a correct name. Likewise, inventory offers from - // muted avatars require the mute list to properly mute. - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_RetrieveInstantMessages); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gAgent.sendReliableMessage(); - requested = TRUE; - } -} - -// A settings system callback for CrashSubmitBehavior -bool handleCrashSubmitBehaviorChanged(const LLSD& newvalue) -{ - S32 cb = newvalue.asInteger(); - const S32 NEVER_SUBMIT_REPORT = 2; - if(cb == NEVER_SUBMIT_REPORT) - { - LLAppViewer::instance()->destroyMainloopTimeout(); - } - return true; -} - -// Use these strictly for things that are constructed at startup, -// or for things that are performance critical. JC -static void settings_to_globals() -{ - LLBUTTON_H_PAD = gSavedSettings.getS32("ButtonHPad"); - BTN_HEIGHT_SMALL = gSavedSettings.getS32("ButtonHeightSmall"); - BTN_HEIGHT = gSavedSettings.getS32("ButtonHeight"); - - MENU_BAR_HEIGHT = gSavedSettings.getS32("MenuBarHeight"); - MENU_BAR_WIDTH = gSavedSettings.getS32("MenuBarWidth"); - - LLSurface::setTextureSize(gSavedSettings.getU32("RegionTextureSize")); - - LLImageGL::sGlobalUseAnisotropic = gSavedSettings.getBOOL("RenderAnisotropic"); - LLVOVolume::sLODFactor = gSavedSettings.getF32("RenderVolumeLODFactor"); - LLVOVolume::sDistanceFactor = 1.f-LLVOVolume::sLODFactor * 0.1f; - LLVolumeImplFlexible::sUpdateFactor = gSavedSettings.getF32("RenderFlexTimeFactor"); - LLVOTree::sTreeFactor = gSavedSettings.getF32("RenderTreeLODFactor"); - LLVOAvatar::sLODFactor = gSavedSettings.getF32("RenderAvatarLODFactor"); - LLVOAvatar::sMaxVisible = (U32)gSavedSettings.getS32("RenderAvatarMaxVisible"); - LLVOAvatar::sVisibleInFirstPerson = gSavedSettings.getBOOL("FirstPersonAvatarVisible"); - // clamp auto-open time to some minimum usable value - LLFolderView::sAutoOpenTime = llmax(0.25f, gSavedSettings.getF32("FolderAutoOpenDelay")); - LLSelectMgr::sRectSelectInclusive = gSavedSettings.getBOOL("RectangleSelectInclusive"); - LLSelectMgr::sRenderHiddenSelections = gSavedSettings.getBOOL("RenderHiddenSelections"); - LLSelectMgr::sRenderLightRadius = gSavedSettings.getBOOL("RenderLightRadius"); - - gAgentPilot.mNumRuns = gSavedSettings.getS32("StatsNumRuns"); - gAgentPilot.mQuitAfterRuns = gSavedSettings.getBOOL("StatsQuitAfterRuns"); - gAgent.setHideGroupTitle(gSavedSettings.getBOOL("RenderHideGroupTitle")); - - gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc"); - gShowObjectUpdates = gSavedSettings.getBOOL("ShowObjectUpdates"); - LLWorldMapView::sMapScale = gSavedSettings.getF32("MapScale"); -} - -static void settings_modify() -{ - LLRenderTarget::sUseFBO = gSavedSettings.getBOOL("RenderUseFBO"); - LLVOAvatar::sUseImpostors = gSavedSettings.getBOOL("RenderUseImpostors"); - LLVOSurfacePatch::sLODFactor = gSavedSettings.getF32("RenderTerrainLODFactor"); - LLVOSurfacePatch::sLODFactor *= LLVOSurfacePatch::sLODFactor; //square lod factor to get exponential range of [1,4] - gDebugGL = gSavedSettings.getBOOL("RenderDebugGL") || gDebugSession; - gDebugPipeline = gSavedSettings.getBOOL("RenderDebugPipeline"); - gAuditTexture = gSavedSettings.getBOOL("AuditTexture"); -#if LL_VECTORIZE - if (gSysCPU.hasAltivec()) - { - gSavedSettings.setBOOL("VectorizeEnable", TRUE ); - gSavedSettings.setU32("VectorizeProcessor", 0 ); - } - else - if (gSysCPU.hasSSE2()) - { - gSavedSettings.setBOOL("VectorizeEnable", TRUE ); - gSavedSettings.setU32("VectorizeProcessor", 2 ); - } - else - if (gSysCPU.hasSSE()) - { - gSavedSettings.setBOOL("VectorizeEnable", TRUE ); - gSavedSettings.setU32("VectorizeProcessor", 1 ); - } - else - { - // Don't bother testing or running if CPU doesn't support it. JC - gSavedSettings.setBOOL("VectorizePerfTest", FALSE ); - gSavedSettings.setBOOL("VectorizeEnable", FALSE ); - gSavedSettings.setU32("VectorizeProcessor", 0 ); - gSavedSettings.setBOOL("VectorizeSkin", FALSE); - } -#else - // This build target doesn't support SSE, don't test/run. - gSavedSettings.setBOOL("VectorizePerfTest", FALSE ); - gSavedSettings.setBOOL("VectorizeEnable", FALSE ); - gSavedSettings.setU32("VectorizeProcessor", 0 ); - gSavedSettings.setBOOL("VectorizeSkin", FALSE); - - // disable fullscreen mode, unsupported - gSavedSettings.setBOOL("WindowFullScreen", FALSE); -#endif -} - -class LLFastTimerLogThread : public LLThread -{ -public: - std::string mFile; - - LLFastTimerLogThread(std::string& test_name) : LLThread("fast timer log") - { - std::string file_name = test_name + std::string(".slp"); - mFile = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, file_name); - } - - void run() - { - std::ofstream os(mFile.c_str()); - - while (!LLAppViewer::instance()->isQuitting()) - { - LLFastTimer::writeLog(os); - os.flush(); - ms_sleep(32); - } - - os.close(); - } - -}; - -//virtual -bool LLAppViewer::initSLURLHandler() -{ - // does nothing unless subclassed - return false; -} - -//virtual -bool LLAppViewer::sendURLToOtherInstance(const std::string& url) -{ - // does nothing unless subclassed - return false; -} - -//---------------------------------------------------------------------------- -// LLAppViewer definition - -// Static members. -// The single viewer app. -LLAppViewer* LLAppViewer::sInstance = NULL; - -const std::string LLAppViewer::sGlobalSettingsName = "Global"; - -LLTextureCache* LLAppViewer::sTextureCache = NULL; -LLImageDecodeThread* LLAppViewer::sImageDecodeThread = NULL; -LLTextureFetch* LLAppViewer::sTextureFetch = NULL; - -LLAppViewer::LLAppViewer() : - mMarkerFile(), - mLogoutMarkerFile(NULL), - mReportedCrash(false), - mNumSessions(0), - mPurgeCache(false), - mPurgeOnExit(false), - mSecondInstance(false), - mSavedFinalSnapshot(false), - mForceGraphicsDetail(false), - mQuitRequested(false), - mLogoutRequestSent(false), - mYieldTime(-1), - mMainloopTimeout(NULL), - mAgentRegionLastAlive(false), - mRandomizeFramerate(LLCachedControl(gSavedSettings,"Randomize Framerate", FALSE)), - mPeriodicSlowFrame(LLCachedControl(gSavedSettings,"Periodic Slow Frame", FALSE)), - mFastTimerLogThread(NULL), - mUpdater(new LLUpdaterService()), - mSettingsLocationList(NULL) -{ - if(NULL != sInstance) - { - llerrs << "Oh no! An instance of LLAppViewer already exists! LLAppViewer is sort of like a singleton." << llendl; - } - - setupErrorHandling(); - sInstance = this; - gLoggedInTime.stop(); - - LLLoginInstance::instance().setUpdaterService(mUpdater.get()); -} - -LLAppViewer::~LLAppViewer() -{ - delete mSettingsLocationList; - - LLLoginInstance::instance().setUpdaterService(0); - - destroyMainloopTimeout(); - - // If we got to this destructor somehow, the app didn't hang. - removeMarkerFile(); -} - -bool LLAppViewer::init() -{ - // - // Start of the application - // - // IMPORTANT! Do NOT put anything that will write - // into the log files during normal startup until AFTER - // we run the "program crashed last time" error handler below. - // - LLFastTimer::reset(); - - // Need to do this initialization before we do anything else, since anything - // that touches files should really go through the lldir API - gDirUtilp->initAppDirs("SecondLife"); - // set skin search path to default, will be overridden later - // this allows simple skinned file lookups to work - gDirUtilp->setSkinFolder("default"); - - initLogging(); - - // - // OK to write stuff to logs now, we've now crash reported if necessary - // - - init_default_trans_args(); - - if (!initConfiguration()) - return false; - - // write Google Breakpad minidump files to our log directory - std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); - logdir += gDirUtilp->getDirDelimiter(); - setMiniDumpDir(logdir); - - // Although initLogging() is the right place to mess with - // setFatalFunction(), we can't query gSavedSettings until after - // initConfiguration(). - S32 rc(gSavedSettings.getS32("QAModeTermCode")); - if (rc >= 0) - { - // QAModeTermCode set, terminate with that rc on LL_ERRS. Use _exit() - // rather than exit() because normal cleanup depends too much on - // successful startup! - LLError::setFatalFunction(boost::bind(_exit, rc)); - } - - mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling")); - -#if LL_RECORD_VIEWER_STATS - LLViewerStatsRecorder::initClass(); -#endif - - // *NOTE:Mani - LLCurl::initClass is not thread safe. - // Called before threads are created. - LLCurl::initClass(); - LLMachineID::init(); - - { - // Viewer metrics initialization - static LLCachedControl metrics_submode(gSavedSettings, - "QAModeMetrics", - false, - "Enables QA features (logging, faster cycling) for metrics collector"); - - if (metrics_submode) - { - app_metrics_qa_mode = true; - app_metrics_interval = METRICS_INTERVAL_QA; - } - LLViewerAssetStatsFF::init(); - } - - initThreads(); - writeSystemInfo(); - - // Initialize updater service (now that we have an io pump) - initUpdater(); - if(isQuitting()) - { - // Early out here because updater set the quitting flag. - return true; - } - - ////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////// - // *FIX: The following code isn't grouped into functions yet. - - // Statistics / debug timer initialization - init_statistics(); - - // - // Various introspection concerning the libs we're using - particularly - // the libs involved in getting to a full login screen. - // - LL_INFOS("InitInfo") << "J2C Engine is: " << LLImageJ2C::getEngineInfo() << LL_ENDL; - LL_INFOS("InitInfo") << "libcurl version is: " << LLCurl::getVersionString() << LL_ENDL; - - // Get the single value from the crash settings file, if it exists - std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); - gCrashSettings.loadFromFile(crash_settings_filename); - if(gSavedSettings.getBOOL("IgnoreAllNotifications")) - { - gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ALWAYS_SEND); - gCrashSettings.saveToFile(crash_settings_filename, FALSE); - } - - ///////////////////////////////////////////////// - // OS-specific login dialogs - ///////////////////////////////////////////////// - - //test_cached_control(); - - // track number of times that app has run - mNumSessions = gSavedSettings.getS32("NumSessions"); - mNumSessions++; - gSavedSettings.setS32("NumSessions", mNumSessions); - - if (gSavedSettings.getBOOL("VerboseLogs")) - { - LLError::setPrintLocation(true); - } - - // Widget construction depends on LLUI being initialized - LLUI::settings_map_t settings_map; - settings_map["config"] = &gSavedSettings; - settings_map["ignores"] = &gWarningSettings; - settings_map["floater"] = &gSavedSettings; // *TODO: New settings file - settings_map["account"] = &gSavedPerAccountSettings; - - LLUI::initClass(settings_map, - LLUIImageList::getInstance(), - ui_audio_callback, - &LLUI::sGLScaleFactor); - - // Setup paths and LLTrans after LLUI::initClass has been called - LLUI::setupPaths(); - LLTransUtil::parseStrings("strings.xml", default_trans_args); - LLTransUtil::parseLanguageStrings("language_settings.xml"); - - // LLKeyboard relies on LLUI to know what some accelerator keys are called. - LLKeyboard::setStringTranslatorFunc( LLTrans::getKeyboardString ); - - LLWeb::initClass(); // do this after LLUI - - // Provide the text fields with callbacks for opening Urls - LLUrlAction::setOpenURLCallback(&LLWeb::loadURL); - LLUrlAction::setOpenURLInternalCallback(&LLWeb::loadURLInternal); - LLUrlAction::setOpenURLExternalCallback(&LLWeb::loadURLExternal); - LLUrlAction::setExecuteSLURLCallback(&LLURLDispatcher::dispatchFromTextEditor); - - // Let code in llui access the viewer help floater - LLUI::sHelpImpl = LLViewerHelp::getInstance(); - - // Load translations for tooltips - LLFloater::initClass(); - - ///////////////////////////////////////////////// - - LLToolMgr::getInstance(); // Initialize tool manager if not already instantiated - - LLViewerFloaterReg::registerFloaters(); - - ///////////////////////////////////////////////// - // - // Load settings files - // - // - LLGroupMgr::parseRoleActions("role_actions.xml"); - - LLAgent::parseTeleportMessages("teleport_strings.xml"); - - LLViewerJointMesh::updateVectorize(); - - // load MIME type -> media impl mappings - std::string mime_types_name; -#if LL_DARWIN - mime_types_name = "mime_types_mac.xml"; -#elif LL_LINUX - mime_types_name = "mime_types_linux.xml"; -#else - mime_types_name = "mime_types.xml"; -#endif - LLMIMETypes::parseMIMETypes( mime_types_name ); - - // Copy settings to globals. *TODO: Remove or move to appropriage class initializers - settings_to_globals(); - // Setup settings listeners - settings_setup_listeners(); - // Modify settings based on system configuration and compile options - settings_modify(); - - // Find partition serial number (Windows) or hardware serial (Mac) - mSerialNumber = generateSerialNumber(); - - // do any necessary set-up for accepting incoming SLURLs from apps - initSLURLHandler(); - - if(false == initHardwareTest()) - { - // Early out from user choice. - return false; - } - - // Prepare for out-of-memory situations, during which we will crash on - // purpose and save a dump. -#if LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP - MemSetErrorHandler(first_mem_error_handler); -#endif // LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP - - // *Note: this is where gViewerStats used to be created. - - // - // Initialize the VFS, and gracefully handle initialization errors - // - - if (!initCache()) - { - std::ostringstream msg; - msg << LLTrans::getString("MBUnableToAccessFile"); - OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); - return 1; - } - - // Initialize the repeater service. - LLMainLoopRepeater::instance().start(); - - // - // Initialize the window - // - gGLActive = TRUE; - initWindow(); - - // initWindow also initializes the Feature List, so now we can initialize this global. - LLCubeMap::sUseCubeMaps = LLFeatureManager::getInstance()->isFeatureAvailable("RenderCubeMap"); - - // call all self-registered classes - LLInitClassList::instance().fireCallbacks(); - - LLFolderViewItem::initClass(); // SJB: Needs to happen after initWindow(), not sure why but related to fonts - - gGLManager.getGLInfo(gDebugInfo); - gGLManager.printGLInfoString(); - - // Load Default bindings - std::string key_bindings_file = gDirUtilp->findFile("keys.xml", - gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), - gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); - - - if (!gViewerKeyboard.loadBindingsXML(key_bindings_file)) - { - std::string key_bindings_file = gDirUtilp->findFile("keys.ini", - gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), - gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); - if (!gViewerKeyboard.loadBindings(key_bindings_file)) - { - LL_ERRS("InitInfo") << "Unable to open keys.ini" << LL_ENDL; - } - } - - // If we don't have the right GL requirements, exit. - if (!gGLManager.mHasRequirements && !gNoRender) - { - // can't use an alert here since we're exiting and - // all hell breaks lose. - OSMessageBox( - LLNotifications::instance().getGlobalString("UnsupportedGLRequirements"), - LLStringUtil::null, - OSMB_OK); - return 0; - } - - // alert the user if they are using unsupported hardware - if(!gSavedSettings.getBOOL("AlertedUnsupportedHardware")) - { - bool unsupported = false; - LLSD args; - std::string minSpecs; - - // get cpu data from xml - std::stringstream minCPUString(LLNotifications::instance().getGlobalString("UnsupportedCPUAmount")); - S32 minCPU = 0; - minCPUString >> minCPU; - - // get RAM data from XML - std::stringstream minRAMString(LLNotifications::instance().getGlobalString("UnsupportedRAMAmount")); - U64 minRAM = 0; - minRAMString >> minRAM; - minRAM = minRAM * 1024 * 1024; - - if(!LLFeatureManager::getInstance()->isGPUSupported() && LLFeatureManager::getInstance()->getGPUClass() != GPU_CLASS_UNKNOWN) - { - minSpecs += LLNotifications::instance().getGlobalString("UnsupportedGPU"); - minSpecs += "\n"; - unsupported = true; - } - if(gSysCPU.getMHz() < minCPU) - { - minSpecs += LLNotifications::instance().getGlobalString("UnsupportedCPU"); - minSpecs += "\n"; - unsupported = true; - } - if(gSysMemory.getPhysicalMemoryClamped() < minRAM) - { - minSpecs += LLNotifications::instance().getGlobalString("UnsupportedRAM"); - minSpecs += "\n"; - unsupported = true; - } - - if (LLFeatureManager::getInstance()->getGPUClass() == GPU_CLASS_UNKNOWN) - { - LLNotificationsUtil::add("UnknownGPU"); - } - - if(unsupported) - { - if(!gSavedSettings.controlExists("WarnUnsupportedHardware") - || gSavedSettings.getBOOL("WarnUnsupportedHardware")) - { - args["MINSPECS"] = minSpecs; - LLNotificationsUtil::add("UnsupportedHardware", args ); - } - - } - } - - - // save the graphics card - gDebugInfo["GraphicsCard"] = LLFeatureManager::getInstance()->getGPUString(); - - // Save the current version to the prefs file - gSavedSettings.setString("LastRunVersion", - LLVersionInfo::getChannelAndVersion()); - - gSimLastTime = gRenderStartTime.getElapsedTimeF32(); - gSimFrames = (F32)gFrameCount; - - LLViewerJoystick::getInstance()->init(false); - - try { - initializeSecHandler(); - } - catch (LLProtectedDataException ex) - { - LLNotificationsUtil::add("CorruptedProtectedDataStore"); - } - LLHTTPClient::setCertVerifyCallback(secapiSSLCertVerifyCallback); - - - gGLActive = FALSE; - if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0) - { - loadEventHostModule(gSavedSettings.getS32("QAModeEventHostPort")); - } - - LLViewerMedia::initClass(); - LLTextUtil::TextHelpers::iconCallbackCreationFunction = create_text_segment_icon_from_url_match; - - //EXT-7013 - On windows for some locale (Japanese) standard - //datetime formatting functions didn't support some parameters such as "weekday". - //Names for days and months localized in xml are also useful for Polish locale(STORM-107). - std::string language = LLControlGroup::getInstance(sGlobalSettingsName)->getString("Language"); - if(language == "ja" || language == "pl") - { - LLStringOps::setupWeekDaysNames(LLTrans::getString("dateTimeWeekdaysNames")); - LLStringOps::setupWeekDaysShortNames(LLTrans::getString("dateTimeWeekdaysShortNames")); - LLStringOps::setupMonthNames(LLTrans::getString("dateTimeMonthNames")); - LLStringOps::setupMonthShortNames(LLTrans::getString("dateTimeMonthShortNames")); - LLStringOps::setupDayFormat(LLTrans::getString("dateTimeDayFormat")); - - LLStringOps::sAM = LLTrans::getString("dateTimeAM"); - LLStringOps::sPM = LLTrans::getString("dateTimePM"); - } - - LLAgentLanguage::init(); - - - - return true; -} - -static LLFastTimer::DeclareTimer FTM_MESSAGES("System Messages"); -static LLFastTimer::DeclareTimer FTM_SLEEP("Sleep"); -static LLFastTimer::DeclareTimer FTM_TEXTURE_CACHE("Texture Cache"); -static LLFastTimer::DeclareTimer FTM_DECODE("Image Decode"); -static LLFastTimer::DeclareTimer FTM_VFS("VFS Thread"); -static LLFastTimer::DeclareTimer FTM_LFS("LFS Thread"); -static LLFastTimer::DeclareTimer FTM_PAUSE_THREADS("Pause Threads"); -static LLFastTimer::DeclareTimer FTM_IDLE("Idle"); -static LLFastTimer::DeclareTimer FTM_PUMP("Pump"); -static LLFastTimer::DeclareTimer FTM_PUMP_ARES("Ares"); -static LLFastTimer::DeclareTimer FTM_PUMP_SERVICE("Service"); -static LLFastTimer::DeclareTimer FTM_SERVICE_CALLBACK("Callback"); -static LLFastTimer::DeclareTimer FTM_AGENT_AUTOPILOT("Autopilot"); -static LLFastTimer::DeclareTimer FTM_AGENT_UPDATE("Update"); - -bool LLAppViewer::mainLoop() -{ - LLMemType mt1(LLMemType::MTYPE_MAIN); - mMainloopTimeout = new LLWatchdogTimeout(); - - //------------------------------------------- - // Run main loop until time to quit - //------------------------------------------- - - // Create IO Pump to use for HTTP Requests. - gServicePump = new LLPumpIO(gAPRPoolp); - LLHTTPClient::setPump(*gServicePump); - LLCurl::setCAFile(gDirUtilp->getCAFile()); - - // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated. - - LLVoiceChannel::initClass(); - LLVoiceClient::getInstance()->init(gServicePump); - LLTimer frameTimer,idleTimer; - LLTimer debugTime; - LLFrameTimer memCheckTimer; - LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); - joystick->setNeedsReset(true); - - LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); - // As we do not (yet) send data on the mainloop LLEventPump that varies - // with each frame, no need to instantiate a new LLSD event object each - // time. Obviously, if that changes, just instantiate the LLSD at the - // point of posting. - LLSD newFrame; - - const F32 memory_check_interval = 1.0f ; //second - - // Handle messages - while (!LLApp::isExiting()) - { - LLFastTimer::nextFrame(); // Should be outside of any timer instances - - //clear call stack records - llclearcallstacks; - - //check memory availability information - { - if(memory_check_interval < memCheckTimer.getElapsedTimeF32()) - { - memCheckTimer.reset() ; - - //update the availability of memory - LLMemoryInfo::getAvailableMemoryKB(mAvailPhysicalMemInKB, mAvailVirtualMemInKB) ; - } - llcallstacks << "Available physical mem(KB): " << mAvailPhysicalMemInKB << llcallstacksendl ; - llcallstacks << "Available virtual mem(KB): " << mAvailVirtualMemInKB << llcallstacksendl ; - } - - try - { - pingMainloopTimeout("Main:MiscNativeWindowEvents"); - - if (gViewerWindow) - { - LLFastTimer t2(FTM_MESSAGES); - gViewerWindow->mWindow->processMiscNativeEvents(); - } - - pingMainloopTimeout("Main:GatherInput"); - - if (gViewerWindow) - { - LLFastTimer t2(FTM_MESSAGES); - if (!restoreErrorTrap()) - { - llwarns << " Someone took over my signal/exception handler (post messagehandling)!" << llendl; - } - - gViewerWindow->mWindow->gatherInput(); - } - -#if 1 && !LL_RELEASE_FOR_DOWNLOAD - // once per second debug info - if (debugTime.getElapsedTimeF32() > 1.f) - { - debugTime.reset(); - } - -#endif - //memory leaking simulation - LLFloaterMemLeak* mem_leak_instance = - LLFloaterReg::findTypedInstance("mem_leaking"); - if(mem_leak_instance) - { - mem_leak_instance->idle() ; - } - - // canonical per-frame event - mainloop.post(newFrame); - - if (!LLApp::isExiting()) - { - pingMainloopTimeout("Main:JoystickKeyboard"); - - // Scan keyboard for movement keys. Command keys and typing - // are handled by windows callbacks. Don't do this until we're - // done initializing. JC - if (gViewerWindow->mWindow->getVisible() - && gViewerWindow->getActive() - && !gViewerWindow->mWindow->getMinimized() - && LLStartUp::getStartupState() == STATE_STARTED - && !gViewerWindow->getShowProgress() - && !gFocusMgr.focusLocked()) - { - LLMemType mjk(LLMemType::MTYPE_JOY_KEY); - joystick->scanJoystick(); - gKeyboard->scanKeyboard(); - } - - // Update state based on messages, user input, object idle. - { - pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds! - - LLFastTimer t3(FTM_IDLE); - idle(); - - if (gAres != NULL && gAres->isInitialized()) - { - LLMemType mt_ip(LLMemType::MTYPE_IDLE_PUMP); - pingMainloopTimeout("Main:ServicePump"); - LLFastTimer t4(FTM_PUMP); - { - LLFastTimer t(FTM_PUMP_ARES); - gAres->process(); - } - { - LLFastTimer t(FTM_PUMP_SERVICE); - // this pump is necessary to make the login screen show up - gServicePump->pump(); - - { - LLFastTimer t(FTM_SERVICE_CALLBACK); - gServicePump->callback(); - } - } - } - - resumeMainloopTimeout(); - } - - if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED)) - { - pauseMainloopTimeout(); - saveFinalSnapshot(); - disconnectViewer(); - resumeMainloopTimeout(); - } - - // Render scene. - if (!LLApp::isExiting()) - { - pingMainloopTimeout("Main:Display"); - gGLActive = TRUE; - display(); - pingMainloopTimeout("Main:Snapshot"); - LLFloaterSnapshot::update(); // take snapshots - gGLActive = FALSE; - } - - } - - pingMainloopTimeout("Main:Sleep"); - - pauseMainloopTimeout(); - - // Sleep and run background threads - { - LLMemType mt_sleep(LLMemType::MTYPE_SLEEP); - LLFastTimer t2(FTM_SLEEP); - - // yield some time to the os based on command line option - if(mYieldTime >= 0) - { - ms_sleep(mYieldTime); - } - - // yield cooperatively when not running as foreground window - if ( gNoRender - || (gViewerWindow && !gViewerWindow->mWindow->getVisible()) - || !gFocusMgr.getAppHasFocus()) - { - // Sleep if we're not rendering, or the window is minimized. - S32 milliseconds_to_sleep = llclamp(gSavedSettings.getS32("BackgroundYieldTime"), 0, 1000); - // don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads - // of equal priority on Windows - if (milliseconds_to_sleep > 0) - { - ms_sleep(milliseconds_to_sleep); - // also pause worker threads during this wait period - LLAppViewer::getTextureCache()->pause(); - LLAppViewer::getImageDecodeThread()->pause(); - } - } - - if (mRandomizeFramerate) - { - ms_sleep(rand() % 200); - } - - if (mPeriodicSlowFrame - && (gFrameCount % 10 == 0)) - { - llinfos << "Periodic slow frame - sleeping 500 ms" << llendl; - ms_sleep(500); - } - - static const F64 FRAME_SLOW_THRESHOLD = 0.5; //2 frames per seconds - const F64 max_idle_time = llmin(.005*10.0*gFrameTimeSeconds, 0.005); // 5 ms a second - idleTimer.reset(); - bool is_slow = (frameTimer.getElapsedTimeF64() > FRAME_SLOW_THRESHOLD) ; - S32 total_work_pending = 0; - S32 total_io_pending = 0; - while(!is_slow)//do not unpause threads if the frame rates are very low. - { - S32 work_pending = 0; - S32 io_pending = 0; - { - LLFastTimer ftm(FTM_TEXTURE_CACHE); - work_pending += LLAppViewer::getTextureCache()->update(1); // unpauses the texture cache thread - } - { - LLFastTimer ftm(FTM_DECODE); - work_pending += LLAppViewer::getImageDecodeThread()->update(1); // unpauses the image thread - } - { - LLFastTimer ftm(FTM_DECODE); - work_pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread - } - - { - LLFastTimer ftm(FTM_VFS); - io_pending += LLVFSThread::updateClass(1); - } - { - LLFastTimer ftm(FTM_LFS); - io_pending += LLLFSThread::updateClass(1); - } - - if (io_pending > 1000) - { - ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up - } - - total_work_pending += work_pending ; - total_io_pending += io_pending ; - - if (!work_pending || idleTimer.getElapsedTimeF64() >= max_idle_time) - { - break; - } - } - - if(!total_work_pending) //pause texture fetching threads if nothing to process. - { - LLAppViewer::getTextureCache()->pause(); - LLAppViewer::getImageDecodeThread()->pause(); - LLAppViewer::getTextureFetch()->pause(); - } - if(!total_io_pending) //pause file threads if nothing to process. - { - LLVFSThread::sLocal->pause(); - LLLFSThread::sLocal->pause(); - } - - if ((LLStartUp::getStartupState() >= STATE_CLEANUP) && - (frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD)) - { - gFrameStalls++; - } - frameTimer.reset(); - - resumeMainloopTimeout(); - - pingMainloopTimeout("Main:End"); - } - } - catch(std::bad_alloc) - { - { - llinfos << "Availabe physical memory(KB) at the beginning of the frame: " << mAvailPhysicalMemInKB << llendl ; - llinfos << "Availabe virtual memory(KB) at the beginning of the frame: " << mAvailVirtualMemInKB << llendl ; - - LLMemoryInfo::getAvailableMemoryKB(mAvailPhysicalMemInKB, mAvailVirtualMemInKB) ; - - llinfos << "Current availabe physical memory(KB): " << mAvailPhysicalMemInKB << llendl ; - llinfos << "Current availabe virtual memory(KB): " << mAvailVirtualMemInKB << llendl ; - } - - //stop memory leaking simulation - LLFloaterMemLeak* mem_leak_instance = - LLFloaterReg::findTypedInstance("mem_leaking"); - if(mem_leak_instance) - { - mem_leak_instance->stop() ; - llwarns << "Bad memory allocation in LLAppViewer::mainLoop()!" << llendl ; - } - else - { - //output possible call stacks to log file. - LLError::LLCallStacks::print() ; - - llerrs << "Bad memory allocation in LLAppViewer::mainLoop()!" << llendl ; - } - } - } - - // Save snapshot for next time, if we made it through initialization - if (STATE_STARTED == LLStartUp::getStartupState()) - { - try - { - saveFinalSnapshot(); - } - catch(std::bad_alloc) - { - llwarns << "Bad memory allocation when saveFinalSnapshot() is called!" << llendl ; - - //stop memory leaking simulation - LLFloaterMemLeak* mem_leak_instance = - LLFloaterReg::findTypedInstance("mem_leaking"); - if(mem_leak_instance) - { - mem_leak_instance->stop() ; - } - } - } - - delete gServicePump; - - destroyMainloopTimeout(); - - llinfos << "Exiting main_loop" << llendflush; - - return true; -} - -void LLAppViewer::flushVFSIO() -{ - while (1) - { - S32 pending = LLVFSThread::updateClass(0); - pending += LLLFSThread::updateClass(0); - if (!pending) - { - break; - } - llinfos << "Waiting for pending IO to finish: " << pending << llendflush; - ms_sleep(100); - } -} - -bool LLAppViewer::cleanup() -{ - // workaround for DEV-35406 crash on shutdown - LLEventPumps::instance().reset(); - - // remove any old breakpad minidump files from the log directory - if (! isError()) - { - std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); - logdir += gDirUtilp->getDirDelimiter(); - gDirUtilp->deleteFilesInDir(logdir, "*-*-*-*-*.dmp"); - } - - // *TODO - generalize this and move DSO wrangling to a helper class -brad - std::set::const_iterator i; - for(i = mPlugins.begin(); i != mPlugins.end(); ++i) - { - int (*ll_plugin_stop_func)(void) = NULL; - apr_status_t rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll_plugin_stop_func, *i, "ll_plugin_stop"); - ll_plugin_stop_func(); - - rv = apr_dso_unload(*i); - } - mPlugins.clear(); - - //flag all elements as needing to be destroyed immediately - // to ensure shutdown order - LLMortician::setZealous(TRUE); - - LLVoiceClient::getInstance()->terminate(); - - disconnectViewer(); - - llinfos << "Viewer disconnected" << llendflush; - - display_cleanup(); - - release_start_screen(); // just in case - - LLError::logToFixedBuffer(NULL); - - llinfos << "Cleaning Up" << llendflush; - - // Must clean up texture references before viewer window is destroyed. - if(LLHUDManager::instanceExists()) - { - LLHUDManager::getInstance()->updateEffects(); - LLHUDObject::updateAll(); - LLHUDManager::getInstance()->cleanupEffects(); - LLHUDObject::cleanupHUDObjects(); - llinfos << "HUD Objects cleaned up" << llendflush; - } - - LLKeyframeDataCache::clear(); - - // End TransferManager before deleting systems it depends on (Audio, VFS, AssetStorage) -#if 0 // this seems to get us stuck in an infinite loop... - gTransferManager.cleanup(); -#endif - - // Note: this is where gWorldMap used to be deleted. - - // Note: this is where gHUDManager used to be deleted. - if(LLHUDManager::instanceExists()) - { - LLHUDManager::getInstance()->shutdownClass(); - } - - delete gAssetStorage; - gAssetStorage = NULL; - - LLPolyMesh::freeAllMeshes(); - - LLStartUp::cleanupNameCache(); - - // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be deleted. - - LLWorldMap::getInstance()->reset(); // release any images - - llinfos << "Global stuff deleted" << llendflush; - - if (gAudiop) - { - // shut down the streaming audio sub-subsystem first, in case it relies on not outliving the general audio subsystem. - - LLStreamingAudioInterface *sai = gAudiop->getStreamingAudioImpl(); - delete sai; - gAudiop->setStreamingAudioImpl(NULL); - - // shut down the audio subsystem - - bool want_longname = false; - if (gAudiop->getDriverName(want_longname) == "FMOD") - { - // This hack exists because fmod likes to occasionally - // crash or hang forever when shutting down, for no - // apparent reason. - llwarns << "Hack, skipping FMOD audio engine cleanup" << llendflush; - } - else - { - gAudiop->shutdown(); - } - - delete gAudiop; - gAudiop = NULL; - } - - // Note: this is where LLFeatureManager::getInstance()-> used to be deleted. - - // Patch up settings for next time - // Must do this before we delete the viewer window, - // such that we can suck rectangle information out of - // it. - cleanupSavedSettings(); - llinfos << "Settings patched up" << llendflush; - - // delete some of the files left around in the cache. - removeCacheFiles("*.wav"); - removeCacheFiles("*.tmp"); - removeCacheFiles("*.lso"); - removeCacheFiles("*.out"); - removeCacheFiles("*.dsf"); - removeCacheFiles("*.bodypart"); - removeCacheFiles("*.clothing"); - - llinfos << "Cache files removed" << llendflush; - - // Wait for any pending VFS IO - flushVFSIO(); - llinfos << "Shutting down Views" << llendflush; - - // Destroy the UI - if( gViewerWindow) - gViewerWindow->shutdownViews(); - - llinfos << "Cleaning up Inventory" << llendflush; - - // Cleanup Inventory after the UI since it will delete any remaining observers - // (Deleted observers should have already removed themselves) - gInventory.cleanupInventory(); - - llinfos << "Cleaning up Selections" << llendflush; - - // Clean up selection managers after UI is destroyed, as UI may be observing them. - // Clean up before GL is shut down because we might be holding on to objects with texture references - LLSelectMgr::cleanupGlobals(); - - llinfos << "Shutting down OpenGL" << llendflush; - - // Shut down OpenGL - if( gViewerWindow) - { - gViewerWindow->shutdownGL(); - - // Destroy window, and make sure we're not fullscreen - // This may generate window reshape and activation events. - // Therefore must do this before destroying the message system. - delete gViewerWindow; - gViewerWindow = NULL; - llinfos << "ViewerWindow deleted" << llendflush; - } - - llinfos << "Cleaning up Keyboard & Joystick" << llendflush; - - // viewer UI relies on keyboard so keep it aound until viewer UI isa gone - delete gKeyboard; - gKeyboard = NULL; - - // Turn off Space Navigator and similar devices - LLViewerJoystick::getInstance()->terminate(); - - llinfos << "Cleaning up Objects" << llendflush; - - LLViewerObject::cleanupVOClasses(); - - LLWaterParamManager::cleanupClass(); - LLWLParamManager::cleanupClass(); - LLPostProcess::cleanupClass(); - - LLTracker::cleanupInstance(); - - // *FIX: This is handled in LLAppViewerWin32::cleanup(). - // I'm keeping the comment to remember its order in cleanup, - // in case of unforseen dependency. - //#if LL_WINDOWS - // gDXHardware.cleanup(); - //#endif // LL_WINDOWS - - LLVolumeMgr* volume_manager = LLPrimitive::getVolumeManager(); - if (!volume_manager->cleanup()) - { - llwarns << "Remaining references in the volume manager!" << llendflush; - } - LLPrimitive::cleanupVolumeManager(); - - llinfos << "Additional Cleanup..." << llendflush; - - LLViewerParcelMgr::cleanupGlobals(); - - // *Note: this is where gViewerStats used to be deleted. - - //end_messaging_system(); - - LLFollowCamMgr::cleanupClass(); - //LLVolumeMgr::cleanupClass(); - LLPrimitive::cleanupVolumeManager(); - LLWorldMapView::cleanupClass(); - LLFolderViewItem::cleanupClass(); - LLUI::cleanupClass(); - - // - // Shut down the VFS's AFTER the decode manager cleans up (since it cleans up vfiles). - // Also after viewerwindow is deleted, since it may have image pointers (which have vfiles) - // Also after shutting down the messaging system since it has VFS dependencies - - // - llinfos << "Cleaning up VFS" << llendflush; - LLVFile::cleanupClass(); - - llinfos << "Saving Data" << llendflush; - - // Store the time of our current logoff - gSavedPerAccountSettings.setU32("LastLogoff", time_corrected()); - - // Must do this after all panels have been deleted because panels that have persistent rects - // save their rects on delete. - gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE); - - LLUIColorTable::instance().saveUserSettings(); - - // PerAccountSettingsFile should be empty if no user has been logged on. - // *FIX:Mani This should get really saved in a "logoff" mode. - if (gSavedSettings.getString("PerAccountSettingsFile").empty()) - { - llinfos << "Not saving per-account settings; don't know the account name yet." << llendl; - } - else - { - gSavedPerAccountSettings.saveToFile(gSavedSettings.getString("PerAccountSettingsFile"), TRUE); - llinfos << "Saved settings" << llendflush; - } - - std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); - // save all settings, even if equals defaults - gCrashSettings.saveToFile(crash_settings_filename, FALSE); - - std::string warnings_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Warnings")); - gWarningSettings.saveToFile(warnings_settings_filename, TRUE); - - // Save URL history file - LLURLHistory::saveFile("url_history.xml"); - - // save mute list. gMuteList used to also be deleted here too. - LLMuteList::getInstance()->cache(gAgent.getID()); - - if (mPurgeOnExit) - { - llinfos << "Purging all cache files on exit" << llendflush; - std::string mask = gDirUtilp->getDirDelimiter() + "*.*"; - gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask); - } - - removeMarkerFile(); // Any crashes from here on we'll just have to ignore - - writeDebugInfo(); - - LLLocationHistory::getInstance()->save(); - - LLAvatarIconIDCache::getInstance()->save(); - - LLViewerMedia::saveCookieFile(); - - // Stop the plugin read thread if it's running. - LLPluginProcessParent::setUseReadThread(false); - - llinfos << "Shutting down Threads" << llendflush; - - // Let threads finish - LLTimer idleTimer; - idleTimer.reset(); - const F64 max_idle_time = 5.f; // 5 seconds - while(1) - { - S32 pending = 0; - pending += LLAppViewer::getTextureCache()->update(1); // unpauses the worker thread - pending += LLAppViewer::getImageDecodeThread()->update(1); // unpauses the image thread - pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread - pending += LLVFSThread::updateClass(0); - pending += LLLFSThread::updateClass(0); - F64 idle_time = idleTimer.getElapsedTimeF64(); - if(!pending) - { - break ; //done - } - else if(idle_time >= max_idle_time) - { - llwarns << "Quitting with pending background tasks." << llendl; - break; - } - } - - // Delete workers first - // shotdown all worker threads before deleting them in case of co-dependencies - sTextureCache->shutdown(); - sTextureFetch->shutdown(); - sImageDecodeThread->shutdown(); - - sTextureFetch->shutDownTextureCacheThread() ; - sTextureFetch->shutDownImageDecodeThread() ; - - delete sTextureCache; - sTextureCache = NULL; - delete sTextureFetch; - sTextureFetch = NULL; - delete sImageDecodeThread; - sImageDecodeThread = NULL; - delete mFastTimerLogThread; - mFastTimerLogThread = NULL; - - if (LLFastTimerView::sAnalyzePerformance) - { - llinfos << "Analyzing performance" << llendl; - - std::string baseline_name = LLFastTimer::sLogName + "_baseline.slp"; - std::string current_name = LLFastTimer::sLogName + ".slp"; - std::string report_name = LLFastTimer::sLogName + "_report.csv"; - - LLFastTimerView::doAnalysis( - gDirUtilp->getExpandedFilename(LL_PATH_LOGS, baseline_name), - gDirUtilp->getExpandedFilename(LL_PATH_LOGS, current_name), - gDirUtilp->getExpandedFilename(LL_PATH_LOGS, report_name)); - } - LLMetricPerformanceTesterBasic::cleanClass() ; - -#if LL_RECORD_VIEWER_STATS - LLViewerStatsRecorder::cleanupClass(); -#endif - - llinfos << "Cleaning up Media and Textures" << llendflush; - - //Note: - //LLViewerMedia::cleanupClass() has to be put before gTextureList.shutdown() - //because some new image might be generated during cleaning up media. --bao - LLViewerMedia::cleanupClass(); - LLViewerParcelMedia::cleanupClass(); - gTextureList.shutdown(); // shutdown again in case a callback added something - LLUIImageList::getInstance()->cleanUp(); - - // This should eventually be done in LLAppViewer - LLImage::cleanupClass(); - LLVFSThread::cleanupClass(); - LLLFSThread::cleanupClass(); - -#ifndef LL_RELEASE_FOR_DOWNLOAD - llinfos << "Auditing VFS" << llendl; - if(gVFS) - { - gVFS->audit(); - } -#endif - - llinfos << "Misc Cleanup" << llendflush; - - // For safety, the LLVFS has to be deleted *after* LLVFSThread. This should be cleaned up. - // (LLVFS doesn't know about LLVFSThread so can't kill pending requests) -Steve - delete gStaticVFS; - gStaticVFS = NULL; - delete gVFS; - gVFS = NULL; - - gSavedSettings.cleanup(); - LLUIColorTable::instance().clear(); - gCrashSettings.cleanup(); - - LLWatchdog::getInstance()->cleanup(); - - LLViewerAssetStatsFF::cleanup(); - - llinfos << "Shutting down message system" << llendflush; - end_messaging_system(); - - // *NOTE:Mani - The following call is not thread safe. - LLCurl::cleanupClass(); - - // If we're exiting to launch an URL, do that here so the screen - // is at the right resolution before we launch IE. - if (!gLaunchFileOnQuit.empty()) - { - llinfos << "Launch file on quit." << llendflush; -#if LL_WINDOWS - // Indicate an application is starting. - SetCursor(LoadCursor(NULL, IDC_WAIT)); -#endif - - // HACK: Attempt to wait until the screen res. switch is complete. - ms_sleep(1000); - - LLWeb::loadURLExternal( gLaunchFileOnQuit, false ); - llinfos << "File launched." << llendflush; - } - - LLMainLoopRepeater::instance().stop(); - - ll_close_fail_log(); - - MEM_TRACK_RELEASE - - llinfos << "Goodbye!" << llendflush; - - // return 0; - return true; -} - -// A callback for llerrs to call during the watchdog error. -void watchdog_llerrs_callback(const std::string &error_string) -{ - gLLErrorActivated = true; - -#ifdef LL_WINDOWS - RaiseException(0,0,0,0); -#else - raise(SIGQUIT); -#endif -} - -// A callback for the watchdog to call. -void watchdog_killer_callback() -{ - LLError::setFatalFunction(watchdog_llerrs_callback); - llerrs << "Watchdog killer event" << llendl; -} - -bool LLAppViewer::initThreads() -{ -#if MEM_TRACK_MEM - static const bool enable_threads = false; -#else - static const bool enable_threads = true; -#endif - - LLVFSThread::initClass(enable_threads && false); - LLLFSThread::initClass(enable_threads && false); - - // Image decoding - LLAppViewer::sImageDecodeThread = new LLImageDecodeThread(enable_threads && true); - LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true); - LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), - sImageDecodeThread, - enable_threads && true, - app_metrics_qa_mode); - LLImage::initClass(); - - if (LLFastTimer::sLog || LLFastTimer::sMetricLog) - { - LLFastTimer::sLogLock = new LLMutex(NULL); - mFastTimerLogThread = new LLFastTimerLogThread(LLFastTimer::sLogName); - mFastTimerLogThread->start(); - } - - // *FIX: no error handling here! - return true; -} - -void errorCallback(const std::string &error_string) -{ -#ifndef LL_RELEASE_FOR_DOWNLOAD - OSMessageBox(error_string, LLTrans::getString("MBFatalError"), OSMB_OK); -#endif - - //Set the ErrorActivated global so we know to create a marker file - gLLErrorActivated = true; - - LLError::crashAndLoop(error_string); -} - -bool LLAppViewer::initLogging() -{ - // - // Set up logging defaults for the viewer - // - LLError::initForApplication( - gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); - LLError::setFatalFunction(errorCallback); - - // Remove the last ".old" log file. - std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, - "SecondLife.old"); - LLFile::remove(old_log_file); - - // Rename current log file to ".old" - std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, - "SecondLife.log"); - LLFile::rename(log_file, old_log_file); - - // Set the log file to SecondLife.log - - LLError::logToFile(log_file); - - // *FIX:Mani no error handling here! - return true; -} - -bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key, - bool set_defaults) -{ - if (!mSettingsLocationList) - { - llerrs << "Invalid settings location list" << llendl; - } - - LLControlGroup* global_settings = LLControlGroup::getInstance(sGlobalSettingsName); - for(LLInitParam::ParamIterator::const_iterator it = mSettingsLocationList->groups.begin(), end_it = mSettingsLocationList->groups.end(); - it != end_it; - ++it) - { - // skip settings groups that aren't the one we requested - if (it->name() != location_key) continue; - - ELLPath path_index = (ELLPath)it->path_index(); - if(path_index <= LL_PATH_NONE || path_index >= LL_PATH_LAST) - { - llerrs << "Out of range path index in app_settings/settings_files.xml" << llendl; - return false; - } - - LLInitParam::ParamIterator::const_iterator file_it, end_file_it; - for (file_it = it->files.begin(), end_file_it = it->files.end(); - file_it != end_file_it; - ++file_it) - { - llinfos << "Attempting to load settings for the group " << file_it->name() - << " - from location " << location_key << llendl; - - LLControlGroup* settings_group = LLControlGroup::getInstance(file_it->name); - if(!settings_group) - { - llwarns << "No matching settings group for name " << file_it->name() << llendl; - continue; - } - - std::string full_settings_path; - - if (file_it->file_name_setting.isProvided() - && global_settings->controlExists(file_it->file_name_setting)) - { - // try to find filename stored in file_name_setting control - full_settings_path = global_settings->getString(file_it->file_name_setting); - if (!gDirUtilp->fileExists(full_settings_path)) - { - // search in default path - full_settings_path = gDirUtilp->getExpandedFilename((ELLPath)path_index, full_settings_path); - } - } - else - { - // by default, use specified file name - full_settings_path = gDirUtilp->getExpandedFilename((ELLPath)path_index, file_it->file_name()); - } - - if(settings_group->loadFromFile(full_settings_path, set_defaults, file_it->persistent)) - { // success! - llinfos << "Loaded settings file " << full_settings_path << llendl; - } - else - { // failed to load - if(file_it->required) - { - llerrs << "Error: Cannot load required settings file from: " << full_settings_path << llendl; - return false; - } - else - { - // only complain if we actually have a filename at this point - if (!full_settings_path.empty()) - { - llinfos << "Cannot load " << full_settings_path << " - No settings found." << llendl; - } - } - } - } - } - - return true; -} - -std::string LLAppViewer::getSettingsFilename(const std::string& location_key, - const std::string& file) -{ - for(LLInitParam::ParamIterator::const_iterator it = mSettingsLocationList->groups.begin(), end_it = mSettingsLocationList->groups.end(); - it != end_it; - ++it) - { - if (it->name() == location_key) - { - LLInitParam::ParamIterator::const_iterator file_it, end_file_it; - for (file_it = it->files.begin(), end_file_it = it->files.end(); - file_it != end_file_it; - ++file_it) - { - if (file_it->name() == file) - { - return file_it->file_name; - } - } - } - } - - return std::string(); -} - -void LLAppViewer::loadColorSettings() -{ - LLUIColorTable::instance().loadFromSettings(); -} - -bool LLAppViewer::initConfiguration() -{ - //Load settings files list - std::string settings_file_list = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "settings_files.xml"); - //LLControlGroup settings_control("SettingsFiles"); - //llinfos << "Loading settings file list " << settings_file_list << llendl; - //if (0 == settings_control.loadFromFile(settings_file_list)) - //{ - // llerrs << "Cannot load default configuration file " << settings_file_list << llendl; - //} - - LLXMLNodePtr root; - BOOL success = LLXMLNode::parseFile(settings_file_list, root, NULL); - if (!success) - { - llerrs << "Cannot load default configuration file " << settings_file_list << llendl; - } - - mSettingsLocationList = new SettingsFiles(); - - LLXUIParser parser; - parser.readXUI(root, *mSettingsLocationList, settings_file_list); - - if (!mSettingsLocationList->validateBlock()) - { - llerrs << "Invalid settings file list " << settings_file_list << llendl; - } - - // The settings and command line parsing have a fragile - // order-of-operation: - // - load defaults from app_settings - // - set procedural settings values - // - read command line settings - // - selectively apply settings needed to load user settings. - // - load overrides from user_settings - // - apply command line settings (to override the overrides) - // - load per account settings (happens in llstartup - - // - load defaults - bool set_defaults = true; - if(!loadSettingsFromDirectory("Default", set_defaults)) - { - std::ostringstream msg; - msg << "Unable to load default settings file. The installation may be corrupted."; - OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); - return false; - } - - LLUI::setupPaths(); // setup paths for LLTrans based on settings files only - LLTransUtil::parseStrings("strings.xml", default_trans_args); - LLTransUtil::parseLanguageStrings("language_settings.xml"); - // - set procedural settings - // Note: can't use LL_PATH_PER_SL_ACCOUNT for any of these since we haven't logged in yet - gSavedSettings.setString("ClientSettingsFile", - gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global"))); - -#ifndef LL_RELEASE_FOR_DOWNLOAD - // provide developer build only overrides for these control variables that are not - // persisted to settings.xml - LLControlVariable* c = gSavedSettings.getControl("ShowConsoleWindow"); - if (c) - { - c->setValue(true, false); - } - c = gSavedSettings.getControl("AllowMultipleViewers"); - if (c) - { - c->setValue(true, false); - } -#endif - -#ifndef LL_RELEASE_FOR_DOWNLOAD - gSavedSettings.setBOOL("QAMode", TRUE ); - gSavedSettings.setS32("WatchdogEnabled", 0); -#endif - - gCrashSettings.getControl(CRASH_BEHAVIOR_SETTING)->getSignal()->connect(boost::bind(&handleCrashSubmitBehaviorChanged, _2)); - - // These are warnings that appear on the first experience of that condition. - // They are already set in the settings_default.xml file, but still need to be added to LLFirstUse - // for disable/reset ability -// LLFirstUse::addConfigVariable("FirstBalanceIncrease"); -// LLFirstUse::addConfigVariable("FirstBalanceDecrease"); -// LLFirstUse::addConfigVariable("FirstSit"); -// LLFirstUse::addConfigVariable("FirstMap"); -// LLFirstUse::addConfigVariable("FirstGoTo"); -// LLFirstUse::addConfigVariable("FirstBuild"); -// LLFirstUse::addConfigVariable("FirstLeftClickNoHit"); -// LLFirstUse::addConfigVariable("FirstTeleport"); -// LLFirstUse::addConfigVariable("FirstOverrideKeys"); -// LLFirstUse::addConfigVariable("FirstAttach"); -// LLFirstUse::addConfigVariable("FirstAppearance"); -// LLFirstUse::addConfigVariable("FirstInventory"); -// LLFirstUse::addConfigVariable("FirstSandbox"); -// LLFirstUse::addConfigVariable("FirstFlexible"); -// LLFirstUse::addConfigVariable("FirstDebugMenus"); -// LLFirstUse::addConfigVariable("FirstSculptedPrim"); -// LLFirstUse::addConfigVariable("FirstVoice"); -// LLFirstUse::addConfigVariable("FirstMedia"); - - // - read command line settings. - LLControlGroupCLP clp; - std::string cmd_line_config = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, - "cmd_line.xml"); - - clp.configure(cmd_line_config, &gSavedSettings); - - if(!initParseCommandLine(clp)) - { - llwarns << "Error parsing command line options. Command Line options ignored." << llendl; - - llinfos << "Command line usage:\n" << clp << llendl; - - std::ostringstream msg; - msg << LLTrans::getString("MBCmdLineError") << clp.getErrorMessage(); - OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); - return false; - } - - // - selectively apply settings - - // If the user has specified a alternate settings file name. - // Load it now before loading the user_settings/settings.xml - if(clp.hasOption("settings")) - { - std::string user_settings_filename = - gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, - clp.getOption("settings")[0]); - gSavedSettings.setString("ClientSettingsFile", user_settings_filename); - llinfos << "Using command line specified settings filename: " - << user_settings_filename << llendl; - } - - // - load overrides from user_settings - loadSettingsFromDirectory("User"); - - if (gSavedSettings.getBOOL("FirstRunThisInstall")) - { - gSavedSettings.setString("SessionSettingsFile", "settings_minimal.xml"); - } - - if (clp.hasOption("sessionsettings")) - { - std::string session_settings_filename = clp.getOption("sessionsettings")[0]; - gSavedSettings.setString("SessionSettingsFile", session_settings_filename); - llinfos << "Using session settings filename: " - << session_settings_filename << llendl; - } - loadSettingsFromDirectory("Session"); - - if (clp.hasOption("usersessionsettings")) - { - std::string user_session_settings_filename = clp.getOption("usersessionsettings")[0]; - gSavedSettings.setString("UserSessionSettingsFile", user_session_settings_filename); - llinfos << "Using user session settings filename: " - << user_session_settings_filename << llendl; - - } - loadSettingsFromDirectory("UserSession"); - - // - apply command line settings - clp.notify(); - - // Register the core crash option as soon as we can - // if we want gdb post-mortem on cores we need to be up and running - // ASAP or we might miss init issue etc. - if(clp.hasOption("disablecrashlogger")) - { - llwarns << "Crashes will be handled by system, stack trace logs and crash logger are both disabled" << llendl; - LLAppViewer::instance()->disableCrashlogger(); - } - - // Handle initialization from settings. - // Start up the debugging console before handling other options. - if (gSavedSettings.getBOOL("ShowConsoleWindow")) - { - initConsole(); - } - - if(clp.hasOption("help")) - { - std::ostringstream msg; - msg << LLTrans::getString("MBCmdLineUsg") << "\n" << clp; - llinfos << msg.str() << llendl; - - OSMessageBox( - msg.str().c_str(), - LLStringUtil::null, - OSMB_OK); - - return false; - } - - if(clp.hasOption("set")) - { - const LLCommandLineParser::token_vector_t& set_values = clp.getOption("set"); - if(0x1 & set_values.size()) - { - llwarns << "Invalid '--set' parameter count." << llendl; - } - else - { - LLCommandLineParser::token_vector_t::const_iterator itr = set_values.begin(); - for(; itr != set_values.end(); ++itr) - { - const std::string& name = *itr; - const std::string& value = *(++itr); - LLControlVariable* c = LLControlGroup::getInstance(sGlobalSettingsName)->getControl(name); - if(c) - { - c->setValue(value, false); - } - else - { - llwarns << "'--set' specified with unknown setting: '" - << name << "'." << llendl; - } - } - } - } - - if(clp.hasOption("channel")) - { - LLVersionInfo::resetChannel(clp.getOption("channel")[0]); - } - - - // If we have specified crash on startup, set the global so we'll trigger the crash at the right time - if(clp.hasOption("crashonstartup")) - { - gCrashOnStartup = TRUE; - } - - if (clp.hasOption("logperformance")) - { - LLFastTimer::sLog = TRUE; - LLFastTimer::sLogName = std::string("performance"); - } - - if (clp.hasOption("logmetrics")) - { - LLFastTimer::sMetricLog = TRUE ; - // '--logmetrics' can be specified with a named test metric argument so the data gathering is done only on that test - // In the absence of argument, every metric is gathered (makes for a rather slow run and hard to decipher report...) - std::string test_name = clp.getOption("logmetrics")[0]; - llinfos << "'--logmetrics' argument : " << test_name << llendl; - if (test_name == "") - { - llwarns << "No '--logmetrics' argument given, will output all metrics to " << DEFAULT_METRIC_NAME << llendl; - LLFastTimer::sLogName = DEFAULT_METRIC_NAME; - } - else - { - LLFastTimer::sLogName = test_name; - } - } - - if (clp.hasOption("graphicslevel")) - { - const LLCommandLineParser::token_vector_t& value = clp.getOption("graphicslevel"); - if(value.size() != 1) - { - llwarns << "Usage: -graphicslevel <0-3>" << llendl; - } - else - { - std::string detail = value.front(); - mForceGraphicsDetail = TRUE; - - switch (detail.c_str()[0]) - { - case '0': - gSavedSettings.setU32("RenderQualityPerformance", 0); - break; - case '1': - gSavedSettings.setU32("RenderQualityPerformance", 1); - break; - case '2': - gSavedSettings.setU32("RenderQualityPerformance", 2); - break; - case '3': - gSavedSettings.setU32("RenderQualityPerformance", 3); - break; - default: - mForceGraphicsDetail = FALSE; - llwarns << "Usage: -graphicslevel <0-3>" << llendl; - break; - } - } - } - - if (clp.hasOption("analyzeperformance")) - { - LLFastTimerView::sAnalyzePerformance = TRUE; - } - - if (clp.hasOption("replaysession")) - { - LLAgentPilot::sReplaySession = TRUE; - } - - if (clp.hasOption("nonotifications")) - { - gSavedSettings.getControl("IgnoreAllNotifications")->setValue(true, false); - } - - if (clp.hasOption("debugsession")) - { - gDebugSession = TRUE; - gDebugGL = TRUE; - - ll_init_fail_log(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "test_failures.log")); - } - - // Handle slurl use. NOTE: Don't let SL-55321 reappear. - - // *FIX: This init code should be made more robust to prevent - // the issue SL-55321 from returning. One thought is to allow - // only select options to be set from command line when a slurl - // is specified. More work on the settings system is needed to - // achieve this. For now... - - // *NOTE:Mani The command line parser parses tokens and is - // setup to bail after parsing the '--url' option or the - // first option specified without a '--option' flag (or - // any other option that uses the 'last_option' setting - - // see LLControlGroupCLP::configure()) - - // What can happen is that someone can use IE (or potentially - // other browsers) and do the rough equivalent of command - // injection and steal passwords. Phoenix. SL-55321 - if(clp.hasOption("url")) - { - LLStartUp::setStartSLURL(LLSLURL(clp.getOption("url")[0])); - if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION) - { - LLGridManager::getInstance()->setGridChoice(LLStartUp::getStartSLURL().getGrid()); - - } - } - else if(clp.hasOption("slurl")) - { - LLSLURL start_slurl(clp.getOption("slurl")[0]); - LLStartUp::setStartSLURL(start_slurl); - } - - const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); - if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) - { - // hack to force the skin to default. - gDirUtilp->setSkinFolder(skinfolder->getValue().asString()); - //gDirUtilp->setSkinFolder("default"); - } - - mYieldTime = gSavedSettings.getS32("YieldTime"); - - // Read skin/branding settings if specified. - //if (! gDirUtilp->getSkinDir().empty() ) - //{ - // std::string skin_def_file = gDirUtilp->findSkinnedFilename("skin.xml"); - // LLXmlTree skin_def_tree; - - // if (!skin_def_tree.parseFile(skin_def_file)) - // { - // llerrs << "Failed to parse skin definition." << llendl; - // } - - //} - -#if LL_DARWIN - // Initialize apple menubar and various callbacks - init_apple_menu(LLTrans::getString("APP_NAME").c_str()); - -#if __ppc__ - // If the CPU doesn't have Altivec (i.e. it's not at least a G4), don't go any further. - // Only test PowerPC - all Intel Macs have SSE. - if(!gSysCPU.hasAltivec()) - { - std::ostringstream msg; - msg << LLTrans::getString("MBRequiresAltiVec"); - OSMessageBox( - msg.str(), - LLStringUtil::null, - OSMB_OK); - removeMarkerFile(); - return false; - } -#endif - -#endif // LL_DARWIN - - // Display splash screen. Must be after above check for previous - // crash as this dialog is always frontmost. - std::string splash_msg; - LLStringUtil::format_map_t args; - args["[APP_NAME]"] = LLTrans::getString("SECOND_LIFE"); - splash_msg = LLTrans::getString("StartupLoading", args); - LLSplashScreen::show(); - LLSplashScreen::update(splash_msg); - - //LLVolumeMgr::initClass(); - LLVolumeMgr* volume_manager = new LLVolumeMgr(); - volume_manager->useMutex(); // LLApp and LLMutex magic must be manually enabled - LLPrimitive::setVolumeManager(volume_manager); - - // Note: this is where we used to initialize gFeatureManagerp. - - gStartTime = totalTime(); - - // - // Set the name of the window - // - gWindowTitle = LLTrans::getString("APP_NAME"); -#if LL_DEBUG - gWindowTitle += std::string(" [DEBUG] ") + gArgs; -#else - gWindowTitle += std::string(" ") + gArgs; -#endif - LLStringUtil::truncate(gWindowTitle, 255); - - //RN: if we received a URL, hand it off to the existing instance. - // don't call anotherInstanceRunning() when doing URL handoff, as - // it relies on checking a marker file which will not work when running - // out of different directories - - if (LLStartUp::getStartSLURL().isValid()) - { - if (sendURLToOtherInstance(LLStartUp::getStartSLURL().getSLURLString())) - { - // successfully handed off URL to existing instance, exit - return false; - } - } - - if (!gSavedSettings.getBOOL("AllowMultipleViewers")) - { - // - // Check for another instance of the app running - // - - mSecondInstance = anotherInstanceRunning(); - - if (mSecondInstance) - { - std::ostringstream msg; - msg << LLTrans::getString("MBAlreadyRunning"); - OSMessageBox( - msg.str(), - LLStringUtil::null, - OSMB_OK); - return false; - } - - initMarkerFile(); - - checkForCrash(); - } - else - { - mSecondInstance = anotherInstanceRunning(); - - if (mSecondInstance) - { - // This is the second instance of SL. Turn off voice support, - // but make sure the setting is *not* persisted. - LLControlVariable* disable_voice = gSavedSettings.getControl("CmdLineDisableVoice"); - if(disable_voice) - { - const BOOL DO_NOT_PERSIST = FALSE; - disable_voice->setValue(LLSD(TRUE), DO_NOT_PERSIST); - } - } - - initMarkerFile(); - - if(!mSecondInstance) - { - checkForCrash(); - } - } - - // need to do this here - need to have initialized global settings first - std::string nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" ); - if ( !nextLoginLocation.empty() ) - { - LLStartUp::setStartSLURL(LLSLURL(nextLoginLocation)); - }; - - gLastRunVersion = gSavedSettings.getString("LastRunVersion"); - - loadColorSettings(); - - return true; // Config was successful. -} - -namespace { - // *TODO - decide if there's a better place for these functions. - // do we need a file llupdaterui.cpp or something? -brad - - void apply_update_callback(LLSD const & notification, LLSD const & response) - { - lldebugs << "LLUpdate user response: " << response << llendl; - if(response["OK_okcancelbuttons"].asBoolean()) - { - llinfos << "LLUpdate restarting viewer" << llendl; - static const bool install_if_ready = true; - // *HACK - this lets us launch the installer immediately for now - LLUpdaterService().startChecking(install_if_ready); - } - } - - void apply_update_ok_callback(LLSD const & notification, LLSD const & response) - { - llinfos << "LLUpdate restarting viewer" << llendl; - static const bool install_if_ready = true; - // *HACK - this lets us launch the installer immediately for now - LLUpdaterService().startChecking(install_if_ready); - } - - void on_update_downloaded(LLSD const & data) - { - std::string notification_name; - void (*apply_callback)(LLSD const &, LLSD const &) = NULL; - - if(data["required"].asBoolean()) - { - if(LLStartUp::getStartupState() <= STATE_LOGIN_WAIT) - { - // The user never saw the progress bar. - apply_callback = &apply_update_ok_callback; - notification_name = "RequiredUpdateDownloadedVerboseDialog"; - } - else if(LLStartUp::getStartupState() < STATE_WORLD_INIT) - { - // The user is logging in but blocked. - apply_callback = &apply_update_ok_callback; - notification_name = "RequiredUpdateDownloadedDialog"; - } - else - { - // The user is already logged in; treat like an optional update. - apply_callback = &apply_update_callback; - notification_name = "DownloadBackgroundTip"; - } - } - else - { - apply_callback = &apply_update_callback; - if(LLStartUp::getStartupState() < STATE_STARTED) - { - // CHOP-262 we need to use a different notification - // method prior to login. - notification_name = "DownloadBackgroundDialog"; - } - else - { - notification_name = "DownloadBackgroundTip"; - } - } - - LLSD substitutions; - substitutions["VERSION"] = data["version"]; - - // truncate version at the rightmost '.' - std::string version_short(data["version"]); - size_t short_length = version_short.rfind('.'); - if (short_length != std::string::npos) - { - version_short.resize(short_length); - } - - LLUIString relnotes_url("[RELEASE_NOTES_BASE_URL][CHANNEL_URL]/[VERSION_SHORT]"); - relnotes_url.setArg("[VERSION_SHORT]", version_short); - - // *TODO thread the update service's response through to this point - std::string const & channel = LLVersionInfo::getChannel(); - boost::shared_ptr channel_escaped(curl_escape(channel.c_str(), channel.size()), &curl_free); - - relnotes_url.setArg("[CHANNEL_URL]", channel_escaped.get()); - relnotes_url.setArg("[RELEASE_NOTES_BASE_URL]", LLTrans::getString("RELEASE_NOTES_BASE_URL")); - substitutions["RELEASE_NOTES_FULL_URL"] = relnotes_url.getString(); - - LLNotificationsUtil::add(notification_name, substitutions, LLSD(), apply_callback); - } - - void install_error_callback(LLSD const & notification, LLSD const & response) - { - LLAppViewer::instance()->forceQuit(); - } - - bool notify_update(LLSD const & evt) - { - std::string notification_name; - switch (evt["type"].asInteger()) - { - case LLUpdaterService::DOWNLOAD_COMPLETE: - on_update_downloaded(evt); - break; - case LLUpdaterService::INSTALL_ERROR: - if(evt["required"].asBoolean()) { - LLNotificationsUtil::add("FailedRequiredUpdateInstall", LLSD(), LLSD(), &install_error_callback); - } else { - LLNotificationsUtil::add("FailedUpdateInstall"); - } - break; - default: - break; - } - - // let others also handle this event by default - return false; - } - - bool on_bandwidth_throttle(LLUpdaterService * updater, LLSD const & evt) - { - updater->setBandwidthLimit(evt.asInteger() * (1024/8)); - return false; // Let others receive this event. - }; -}; - -void LLAppViewer::initUpdater() -{ - // Initialize the updater service. - // Generate URL to the udpater service - // Get Channel - // Get Version - std::string url = gSavedSettings.getString("UpdaterServiceURL"); - std::string channel = LLVersionInfo::getChannel(); - std::string version = LLVersionInfo::getVersion(); - std::string protocol_version = gSavedSettings.getString("UpdaterServiceProtocolVersion"); - std::string service_path = gSavedSettings.getString("UpdaterServicePath"); - U32 check_period = gSavedSettings.getU32("UpdaterServiceCheckPeriod"); - - mUpdater->setAppExitCallback(boost::bind(&LLAppViewer::forceQuit, this)); - mUpdater->initialize(protocol_version, - url, - service_path, - channel, - version); - mUpdater->setCheckPeriod(check_period); - mUpdater->setBandwidthLimit((int)gSavedSettings.getF32("UpdaterMaximumBandwidth") * (1024/8)); - gSavedSettings.getControl("UpdaterMaximumBandwidth")->getSignal()-> - connect(boost::bind(&on_bandwidth_throttle, mUpdater.get(), _2)); - if(gSavedSettings.getU32("UpdaterServiceSetting")) - { - bool install_if_ready = true; - mUpdater->startChecking(install_if_ready); - } - - LLEventPump & updater_pump = LLEventPumps::instance().obtain(LLUpdaterService::pumpName()); - updater_pump.listen("notify_update", ¬ify_update); -} - -void LLAppViewer::checkForCrash(void) -{ - -#if LL_SEND_CRASH_REPORTS - if (gLastExecEvent == LAST_EXEC_FROZE) - { - llinfos << "Last execution froze, requesting to send crash report." << llendl; - // - // Pop up a freeze or crash warning dialog - // - S32 choice; - if(gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) == CRASH_BEHAVIOR_ASK) - { - std::ostringstream msg; - msg << LLTrans::getString("MBFrozenCrashed"); - std::string alert = LLTrans::getString("APP_NAME") + " " + LLTrans::getString("MBAlert"); - choice = OSMessageBox(msg.str(), - alert, - OSMB_YESNO); - } - else if(gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) == CRASH_BEHAVIOR_NEVER_SEND) - { - choice = OSBTN_NO; - } - else - { - choice = OSBTN_YES; - } - - if (OSBTN_YES == choice) - { - llinfos << "Sending crash report." << llendl; - - bool report_freeze = true; - handleCrashReporting(report_freeze); - } - else - { - llinfos << "Not sending crash report." << llendl; - } - } -#endif // LL_SEND_CRASH_REPORTS - -} - -bool LLAppViewer::initWindow() -{ - LL_INFOS("AppInit") << "Initializing window..." << LL_ENDL; - - // store setting in a global for easy access and modification - gNoRender = gSavedSettings.getBOOL("DisableRendering"); - - // always start windowed - BOOL ignorePixelDepth = gSavedSettings.getBOOL("IgnorePixelDepth"); - gViewerWindow = new LLViewerWindow(gWindowTitle, - VIEWER_WINDOW_CLASSNAME, - gSavedSettings.getS32("WindowX"), gSavedSettings.getS32("WindowY"), - gSavedSettings.getS32("WindowWidth"), gSavedSettings.getS32("WindowHeight"), - gSavedSettings.getBOOL("WindowFullScreen"), ignorePixelDepth); - - // Need to load feature table before cheking to start watchdog. - const S32 NEVER_SUBMIT_REPORT = 2; - bool use_watchdog = false; - int watchdog_enabled_setting = gSavedSettings.getS32("WatchdogEnabled"); - if(watchdog_enabled_setting == -1){ - use_watchdog = !LLFeatureManager::getInstance()->isFeatureAvailable("WatchdogDisabled"); - } - else - { - // The user has explicitly set this setting; always use that value. - use_watchdog = bool(watchdog_enabled_setting); - } - - bool send_reports = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) != NEVER_SUBMIT_REPORT; - if(use_watchdog && send_reports) - { - LLWatchdog::getInstance()->init(watchdog_killer_callback); - } - - LLNotificationsUI::LLNotificationManager::getInstance(); - - if (gSavedSettings.getBOOL("WindowMaximized")) - { - gViewerWindow->mWindow->maximize(); - } - - if (!gNoRender) - { - // - // Initialize GL stuff - // - - if (mForceGraphicsDetail) - { - LLFeatureManager::getInstance()->setGraphicsLevel(gSavedSettings.getU32("RenderQualityPerformance"), false); - } - - // Set this flag in case we crash while initializing GL - gSavedSettings.setBOOL("RenderInitError", TRUE); - gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); - - gPipeline.init(); - stop_glerror(); - gViewerWindow->initGLDefaults(); - - gSavedSettings.setBOOL("RenderInitError", FALSE); - gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); - } - - //If we have a startup crash, it's usually near GL initialization, so simulate that. - if(gCrashOnStartup) - { - LLAppViewer::instance()->forceErrorLLError(); - } - - LLUI::sWindow = gViewerWindow->getWindow(); - - // Show watch cursor - gViewerWindow->setCursor(UI_CURSOR_WAIT); - - // Finish view initialization - gViewerWindow->initBase(); - - // show viewer window - //gViewerWindow->mWindow->show(); - - - return true; -} - -void LLAppViewer::writeDebugInfo() -{ - std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log"); - llinfos << "Opening debug file " << debug_filename << llendl; - llofstream out_file(debug_filename); - LLSDSerialize::toPrettyXML(gDebugInfo, out_file); - out_file.close(); -} - -void LLAppViewer::cleanupSavedSettings() -{ - gSavedSettings.setBOOL("MouseSun", FALSE); - - gSavedSettings.setBOOL("UseEnergy", TRUE); // force toggle to turn off, since sends message to simulator - - gSavedSettings.setBOOL("DebugWindowProc", gDebugWindowProc); - - gSavedSettings.setBOOL("ShowObjectUpdates", gShowObjectUpdates); - - if (!gNoRender) - { - if (gDebugView) - { - gSavedSettings.setBOOL("ShowDebugConsole", gDebugView->mDebugConsolep->getVisible()); - } - } - - // save window position if not maximized - // as we don't track it in callbacks - if(NULL != gViewerWindow) - { - BOOL maximized = gViewerWindow->mWindow->getMaximized(); - if (!maximized) - { - LLCoordScreen window_pos; - - if (gViewerWindow->mWindow->getPosition(&window_pos)) - { - gSavedSettings.setS32("WindowX", window_pos.mX); - gSavedSettings.setS32("WindowY", window_pos.mY); - } - } - } - - gSavedSettings.setF32("MapScale", LLWorldMapView::sMapScale ); - - // Some things are cached in LLAgent. - if (gAgent.isInitialized()) - { - gSavedSettings.setF32("RenderFarClip", gAgentCamera.mDrawDistance); - } -} - -void LLAppViewer::removeCacheFiles(const std::string& file_mask) -{ - std::string mask = gDirUtilp->getDirDelimiter() + file_mask; - gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), mask); -} - -void LLAppViewer::writeSystemInfo() -{ - gDebugInfo["SLLog"] = LLError::logFileName(); - - gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel(); - gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor(); - gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); - gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch(); - gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::getBuild(); - - gDebugInfo["CAFilename"] = gDirUtilp->getCAFile(); - - gDebugInfo["CPUInfo"]["CPUString"] = gSysCPU.getCPUString(); - gDebugInfo["CPUInfo"]["CPUFamily"] = gSysCPU.getFamily(); - gDebugInfo["CPUInfo"]["CPUMhz"] = (S32)gSysCPU.getMHz(); - gDebugInfo["CPUInfo"]["CPUAltivec"] = gSysCPU.hasAltivec(); - gDebugInfo["CPUInfo"]["CPUSSE"] = gSysCPU.hasSSE(); - gDebugInfo["CPUInfo"]["CPUSSE2"] = gSysCPU.hasSSE2(); - - gDebugInfo["RAMInfo"]["Physical"] = (LLSD::Integer)(gSysMemory.getPhysicalMemoryKB()); - gDebugInfo["RAMInfo"]["Allocated"] = (LLSD::Integer)(gMemoryAllocated>>10); // MB -> KB - gDebugInfo["OSInfo"] = getOSInfo().getOSStringSimple(); - - // The user is not logged on yet, but record the current grid choice login url - // which may have been the intended grid. This can b - gDebugInfo["GridName"] = LLGridManager::getInstance()->getGridLabel(); - - // *FIX:Mani - move this down in llappviewerwin32 -#ifdef LL_WINDOWS - DWORD thread_id = GetCurrentThreadId(); - gDebugInfo["MainloopThreadID"] = (S32)thread_id; -#endif - - // "CrashNotHandled" is set here, while things are running well, - // in case of a freeze. If there is a freeze, the crash logger will be launched - // and can read this value from the debug_info.log. - // If the crash is handled by LLAppViewer::handleViewerCrash, ie not a freeze, - // then the value of "CrashNotHandled" will be set to true. - gDebugInfo["CrashNotHandled"] = (LLSD::Boolean)true; - - // Insert crash host url (url to post crash log to) if configured. This insures - // that the crash report will go to the proper location in the case of a - // prior freeze. - std::string crashHostUrl = gSavedSettings.get("CrashHostUrl"); - if(crashHostUrl != "") - { - gDebugInfo["CrashHostUrl"] = crashHostUrl; - } - - // Dump some debugging info - LL_INFOS("SystemInfo") << LLTrans::getString("APP_NAME") - << " version " << LLVersionInfo::getShortVersion() << LL_ENDL; - - // Dump the local time and time zone - time_t now; - time(&now); - char tbuffer[256]; /* Flawfinder: ignore */ - strftime(tbuffer, 256, "%Y-%m-%dT%H:%M:%S %Z", localtime(&now)); - LL_INFOS("SystemInfo") << "Local time: " << tbuffer << LL_ENDL; - - // query some system information - LL_INFOS("SystemInfo") << "CPU info:\n" << gSysCPU << LL_ENDL; - LL_INFOS("SystemInfo") << "Memory info:\n" << gSysMemory << LL_ENDL; - LL_INFOS("SystemInfo") << "OS: " << getOSInfo().getOSStringSimple() << LL_ENDL; - LL_INFOS("SystemInfo") << "OS info: " << getOSInfo() << LL_ENDL; - - writeDebugInfo(); // Save out debug_info.log early, in case of crash. -} - -void LLAppViewer::handleViewerCrash() -{ - llinfos << "Handle viewer crash entry." << llendl; - - llinfos << "Last render pool type: " << LLPipeline::sCurRenderPoolType << llendl ; - - //print out recorded call stacks if there are any. - LLError::LLCallStacks::print(); - - LLAppViewer* pApp = LLAppViewer::instance(); - if (pApp->beingDebugged()) - { - // This will drop us into the debugger. - abort(); - } - - if (LLApp::isCrashloggerDisabled()) - { - abort(); - } - - // Returns whether a dialog was shown. - // Only do the logic in here once - if (pApp->mReportedCrash) - { - return; - } - pApp->mReportedCrash = TRUE; - - // Insert crash host url (url to post crash log to) if configured. - std::string crashHostUrl = gSavedSettings.get("CrashHostUrl"); - if(crashHostUrl != "") - { - gDebugInfo["CrashHostUrl"] = crashHostUrl; - } - - //We already do this in writeSystemInfo(), but we do it again here to make /sure/ we have a version - //to check against no matter what - gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel(); - - gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor(); - gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); - gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch(); - gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::getBuild(); - - LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if ( parcel && parcel->getMusicURL()[0]) - { - gDebugInfo["ParcelMusicURL"] = parcel->getMusicURL(); - } - if ( parcel && parcel->getMediaURL()[0]) - { - gDebugInfo["ParcelMediaURL"] = parcel->getMediaURL(); - } - - - gDebugInfo["SettingsFilename"] = gSavedSettings.getString("ClientSettingsFile"); - gDebugInfo["CAFilename"] = gDirUtilp->getCAFile(); - gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName(); - gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath(); - gDebugInfo["SessionLength"] = F32(LLFrameTimer::getElapsedSeconds()); - gDebugInfo["StartupState"] = LLStartUp::getStartupStateString(); - gDebugInfo["RAMInfo"]["Allocated"] = (LLSD::Integer) LLMemory::getCurrentRSS() >> 10; - gDebugInfo["FirstLogin"] = (LLSD::Boolean) gAgent.isFirstLogin(); - gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall"); - - char *minidump_file = pApp->getMiniDumpFilename(); - if(minidump_file && minidump_file[0] != 0) - { - gDebugInfo["MinidumpPath"] = minidump_file; - } - - if(gLogoutInProgress) - { - gDebugInfo["LastExecEvent"] = LAST_EXEC_LOGOUT_CRASH; - } - else - { - gDebugInfo["LastExecEvent"] = gLLErrorActivated ? LAST_EXEC_LLERROR_CRASH : LAST_EXEC_OTHER_CRASH; - } - - if(gAgent.getRegion()) - { - gDebugInfo["CurrentSimHost"] = gAgent.getRegionHost().getHostName(); - gDebugInfo["CurrentRegion"] = gAgent.getRegion()->getName(); - - const LLVector3& loc = gAgent.getPositionAgent(); - gDebugInfo["CurrentLocationX"] = loc.mV[0]; - gDebugInfo["CurrentLocationY"] = loc.mV[1]; - gDebugInfo["CurrentLocationZ"] = loc.mV[2]; - } - - if(LLAppViewer::instance()->mMainloopTimeout) - { - gDebugInfo["MainloopTimeoutState"] = LLAppViewer::instance()->mMainloopTimeout->getState(); - } - - // The crash is being handled here so set this value to false. - // Otherwise the crash logger will think this crash was a freeze. - gDebugInfo["CrashNotHandled"] = (LLSD::Boolean)false; - - //Write out the crash status file - //Use marker file style setup, as that's the simplest, especially since - //we're already in a crash situation - if (gDirUtilp) - { - std::string crash_file_name; - if(gLLErrorActivated) crash_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,LLERROR_MARKER_FILE_NAME); - else crash_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,ERROR_MARKER_FILE_NAME); - llinfos << "Creating crash marker file " << crash_file_name << llendl; - - LLAPRFile crash_file ; - crash_file.open(crash_file_name, LL_APR_W); - if (crash_file.getFileHandle()) - { - LL_INFOS("MarkerFile") << "Created crash marker file " << crash_file_name << LL_ENDL; - } - else - { - LL_WARNS("MarkerFile") << "Cannot create error marker file " << crash_file_name << LL_ENDL; - } - } - - if (gMessageSystem && gDirUtilp) - { - std::string filename; - filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "stats.log"); - llofstream file(filename, llofstream::binary); - if(file.good()) - { - llinfos << "Handle viewer crash generating stats log." << llendl; - gMessageSystem->summarizeLogs(file); - file.close(); - } - } - - if (gMessageSystem) - { - gMessageSystem->getCircuitInfo(gDebugInfo["CircuitInfo"]); - gMessageSystem->stopLogging(); - } - - if (LLWorld::instanceExists()) LLWorld::getInstance()->getInfo(gDebugInfo); - - // Close the debug file - pApp->writeDebugInfo(); - - LLError::logToFile(""); - - // Remove the marker file, since otherwise we'll spawn a process that'll keep it locked - if(gDebugInfo["LastExecEvent"].asInteger() == LAST_EXEC_LOGOUT_CRASH) - { - pApp->removeMarkerFile(true); - } - else - { - pApp->removeMarkerFile(false); - } - -#if LL_SEND_CRASH_REPORTS - // Call to pure virtual, handled by platform specific llappviewer instance. - pApp->handleCrashReporting(); -#endif - - return; -} - -bool LLAppViewer::anotherInstanceRunning() -{ - // We create a marker file when the program starts and remove the file when it finishes. - // If the file is currently locked, that means another process is already running. - - std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, MARKER_FILE_NAME); - LL_DEBUGS("MarkerFile") << "Checking marker file for lock..." << LL_ENDL; - - //Freeze case checks - if (LLAPRFile::isExist(marker_file, NULL, LL_APR_RB)) - { - // File exists, try opening with write permissions - LLAPRFile outfile ; - outfile.open(marker_file, LL_APR_WB); - apr_file_t* fMarker = outfile.getFileHandle() ; - if (!fMarker) - { - // Another instance is running. Skip the rest of these operations. - LL_INFOS("MarkerFile") << "Marker file is locked." << LL_ENDL; - return true; - } - if (apr_file_lock(fMarker, APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE) != APR_SUCCESS) //flock(fileno(fMarker), LOCK_EX | LOCK_NB) == -1) - { - LL_INFOS("MarkerFile") << "Marker file is locked." << LL_ENDL; - return true; - } - // No other instances; we'll lock this file now & delete on quit. - } - LL_DEBUGS("MarkerFile") << "Marker file isn't locked." << LL_ENDL; - return false; -} - -void LLAppViewer::initMarkerFile() -{ - //First, check for the existence of other files. - //There are marker files for two different types of crashes - - mMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,MARKER_FILE_NAME); - LL_DEBUGS("MarkerFile") << "Checking marker file for lock..." << LL_ENDL; - - //We've got 4 things to test for here - // - Other Process Running (SecondLife.exec_marker present, locked) - // - Freeze (SecondLife.exec_marker present, not locked) - // - LLError Crash (SecondLife.llerror_marker present) - // - Other Crash (SecondLife.error_marker present) - // These checks should also remove these files for the last 2 cases if they currently exist - - //LLError/Error checks. Only one of these should ever happen at a time. - std::string logout_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LOGOUT_MARKER_FILE_NAME); - std::string llerror_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LLERROR_MARKER_FILE_NAME); - std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME); - - if (LLAPRFile::isExist(mMarkerFileName, NULL, LL_APR_RB) && !anotherInstanceRunning()) - { - gLastExecEvent = LAST_EXEC_FROZE; - LL_INFOS("MarkerFile") << "Exec marker found: program froze on previous execution" << LL_ENDL; - } - if(LLAPRFile::isExist(logout_marker_file, NULL, LL_APR_RB)) - { - gLastExecEvent = LAST_EXEC_LOGOUT_FROZE; - LL_INFOS("MarkerFile") << "Last exec LLError crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; - LLAPRFile::remove(logout_marker_file); - } - if(LLAPRFile::isExist(llerror_marker_file, NULL, LL_APR_RB)) - { - if(gLastExecEvent == LAST_EXEC_LOGOUT_FROZE) gLastExecEvent = LAST_EXEC_LOGOUT_CRASH; - else gLastExecEvent = LAST_EXEC_LLERROR_CRASH; - LL_INFOS("MarkerFile") << "Last exec LLError crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; - LLAPRFile::remove(llerror_marker_file); - } - if(LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB)) - { - if(gLastExecEvent == LAST_EXEC_LOGOUT_FROZE) gLastExecEvent = LAST_EXEC_LOGOUT_CRASH; - else gLastExecEvent = LAST_EXEC_OTHER_CRASH; - LL_INFOS("MarkerFile") << "Last exec crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; - LLAPRFile::remove(error_marker_file); - } - - // No new markers if another instance is running. - if(anotherInstanceRunning()) - { - return; - } - - // Create the marker file for this execution & lock it - apr_status_t s; - s = mMarkerFile.open(mMarkerFileName, LL_APR_W, TRUE); - - if (s == APR_SUCCESS && mMarkerFile.getFileHandle()) - { - LL_DEBUGS("MarkerFile") << "Marker file created." << LL_ENDL; - } - else - { - LL_INFOS("MarkerFile") << "Failed to create marker file." << LL_ENDL; - return; - } - if (apr_file_lock(mMarkerFile.getFileHandle(), APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE) != APR_SUCCESS) - { - mMarkerFile.close() ; - LL_INFOS("MarkerFile") << "Marker file cannot be locked." << LL_ENDL; - return; - } - - LL_DEBUGS("MarkerFile") << "Marker file locked." << LL_ENDL; -} - -void LLAppViewer::removeMarkerFile(bool leave_logout_marker) -{ - LL_DEBUGS("MarkerFile") << "removeMarkerFile()" << LL_ENDL; - if (mMarkerFile.getFileHandle()) - { - mMarkerFile.close() ; - LLAPRFile::remove( mMarkerFileName ); - } - if (mLogoutMarkerFile != NULL && !leave_logout_marker) - { - LLAPRFile::remove( mLogoutMarkerFileName ); - mLogoutMarkerFile = NULL; - } -} - -void LLAppViewer::forceQuit() -{ - LLApp::setQuitting(); -} - -//TODO: remove -void LLAppViewer::fastQuit(S32 error_code) -{ - // finish pending transfers - flushVFSIO(); - // let sim know we're logging out - sendLogoutRequest(); - // flush network buffers by shutting down messaging system - end_messaging_system(); - // figure out the error code - S32 final_error_code = error_code ? error_code : (S32)isError(); - // this isn't a crash - removeMarkerFile(); - // get outta here - _exit(final_error_code); -} - -void LLAppViewer::requestQuit() -{ - llinfos << "requestQuit" << llendl; - - LLViewerRegion* region = gAgent.getRegion(); - - if( (LLStartUp::getStartupState() < STATE_STARTED) || !region ) - { - // If we have a region, make some attempt to send a logout request first. - // This prevents the halfway-logged-in avatar from hanging around inworld for a couple minutes. - if(region) - { - sendLogoutRequest(); - } - - // Quit immediately - forceQuit(); - return; - } - - // Try to send metrics back to the grid - metricsSend(!gDisconnected); - - LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); - effectp->setPositionGlobal(gAgent.getPositionGlobal()); - effectp->setColor(LLColor4U(gAgent.getEffectColor())); - LLHUDManager::getInstance()->sendEffects(); - effectp->markDead() ;//remove it. - - // Attempt to close all floaters that might be - // editing things. - if (gFloaterView) - { - // application is quitting - gFloaterView->closeAllChildren(true); - } - - LLSideTray::getInstance()->notifyChildren(LLSD().with("request","quit")); - - send_stats(); - - gLogoutTimer.reset(); - mQuitRequested = true; -} - -static bool finish_quit(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - - if (option == 0) - { - LLAppViewer::instance()->requestQuit(); - } - return false; -} -static LLNotificationFunctorRegistration finish_quit_reg("ConfirmQuit", finish_quit); - -static bool switch_standard_skin_and_quit(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - - if (option == 0) - { - gSavedSettings.setString("SessionSettingsFile", ""); - LLAppViewer::instance()->requestQuit(); - } - return false; -} - -static LLNotificationFunctorRegistration standard_skin_quit_reg("SwitchToStandardSkinAndQuit", switch_standard_skin_and_quit); - -void LLAppViewer::userQuit() -{ - if (gDisconnected || gViewerWindow->getProgressView()->getVisible()) - { - requestQuit(); - } - else - { - LLNotificationsUtil::add("ConfirmQuit"); - } -} - -static bool finish_early_exit(const LLSD& notification, const LLSD& response) -{ - LLAppViewer::instance()->forceQuit(); - return false; -} - -void LLAppViewer::earlyExit(const std::string& name, const LLSD& substitutions) -{ - llwarns << "app_early_exit: " << name << llendl; - gDoDisconnect = TRUE; - LLNotificationsUtil::add(name, substitutions, LLSD(), finish_early_exit); -} - -// case where we need the viewer to exit without any need for notifications -void LLAppViewer::earlyExitNoNotify() -{ - llwarns << "app_early_exit with no notification: " << llendl; - gDoDisconnect = TRUE; - finish_early_exit( LLSD(), LLSD() ); -} - -void LLAppViewer::abortQuit() -{ - llinfos << "abortQuit()" << llendl; - mQuitRequested = false; -} - -void LLAppViewer::migrateCacheDirectory() -{ -#if LL_WINDOWS || LL_DARWIN - // NOTE: (Nyx) as of 1.21, cache for mac is moving to /library/caches/SecondLife from - // /library/application support/SecondLife/cache This should clear/delete the old dir. - - // As of 1.23 the Windows cache moved from - // C:\Documents and Settings\James\Application Support\SecondLife\cache - // to - // C:\Documents and Settings\James\Local Settings\Application Support\SecondLife - // - // The Windows Vista equivalent is from - // C:\Users\James\AppData\Roaming\SecondLife\cache - // to - // C:\Users\James\AppData\Local\SecondLife - // - // Note the absence of \cache on the second path. James. - - // Only do this once per fresh install of this version. - if (gSavedSettings.getBOOL("MigrateCacheDirectory")) - { - gSavedSettings.setBOOL("MigrateCacheDirectory", FALSE); - - std::string delimiter = gDirUtilp->getDirDelimiter(); - std::string old_cache_dir = gDirUtilp->getOSUserAppDir() + delimiter + "cache"; - std::string new_cache_dir = gDirUtilp->getCacheDir(true); - - if (gDirUtilp->fileExists(old_cache_dir)) - { - llinfos << "Migrating cache from " << old_cache_dir << " to " << new_cache_dir << llendl; - - // Migrate inventory cache to avoid pain to inventory database after mass update - S32 file_count = 0; - std::string file_name; - std::string mask = delimiter + "*.*"; - while (gDirUtilp->getNextFileInDir(old_cache_dir, mask, file_name)) - { - if (file_name == "." || file_name == "..") continue; - std::string source_path = old_cache_dir + delimiter + file_name; - std::string dest_path = new_cache_dir + delimiter + file_name; - if (!LLFile::rename(source_path, dest_path)) - { - file_count++; - } - } - llinfos << "Moved " << file_count << " files" << llendl; - - // Nuke the old cache - gDirUtilp->setCacheDir(old_cache_dir); - purgeCache(); - gDirUtilp->setCacheDir(new_cache_dir); - -#if LL_DARWIN - // Clean up Mac files not deleted by removing *.* - std::string ds_store = old_cache_dir + "/.DS_Store"; - if (gDirUtilp->fileExists(ds_store)) - { - LLFile::remove(ds_store); - } -#endif - if (LLFile::rmdir(old_cache_dir) != 0) - { - llwarns << "could not delete old cache directory " << old_cache_dir << llendl; - } - } - } -#endif // LL_WINDOWS || LL_DARWIN -} - -void dumpVFSCaches() -{ - llinfos << "======= Static VFS ========" << llendl; - gStaticVFS->listFiles(); -#if LL_WINDOWS - llinfos << "======= Dumping static VFS to StaticVFSDump ========" << llendl; - WCHAR w_str[MAX_PATH]; - GetCurrentDirectory(MAX_PATH, w_str); - S32 res = LLFile::mkdir("StaticVFSDump"); - if (res == -1) - { - if (errno != EEXIST) - { - llwarns << "Couldn't create dir StaticVFSDump" << llendl; - } - } - SetCurrentDirectory(utf8str_to_utf16str("StaticVFSDump").c_str()); - gStaticVFS->dumpFiles(); - SetCurrentDirectory(w_str); -#endif - - llinfos << "========= Dynamic VFS ====" << llendl; - gVFS->listFiles(); -#if LL_WINDOWS - llinfos << "========= Dumping dynamic VFS to VFSDump ====" << llendl; - res = LLFile::mkdir("VFSDump"); - if (res == -1) - { - if (errno != EEXIST) - { - llwarns << "Couldn't create dir VFSDump" << llendl; - } - } - SetCurrentDirectory(utf8str_to_utf16str("VFSDump").c_str()); - gVFS->dumpFiles(); - SetCurrentDirectory(w_str); -#endif -} - -//static -U32 LLAppViewer::getTextureCacheVersion() -{ - //viewer texture cache version, change if the texture cache format changes. - const U32 TEXTURE_CACHE_VERSION = 7; - - return TEXTURE_CACHE_VERSION ; -} - -//static -U32 LLAppViewer::getObjectCacheVersion() -{ - // Viewer object cache version, change if object update - // format changes. JC - const U32 INDRA_OBJECT_CACHE_VERSION = 14; - - return INDRA_OBJECT_CACHE_VERSION; -} - -bool LLAppViewer::initCache() -{ - mPurgeCache = false; - BOOL read_only = mSecondInstance ? TRUE : FALSE; - LLAppViewer::getTextureCache()->setReadOnly(read_only) ; - LLVOCache::getInstance()->setReadOnly(read_only); - - BOOL texture_cache_mismatch = FALSE ; - if (gSavedSettings.getS32("LocalCacheVersion") != LLAppViewer::getTextureCacheVersion()) - { - texture_cache_mismatch = TRUE ; - if(!read_only) - { - gSavedSettings.setS32("LocalCacheVersion", LLAppViewer::getTextureCacheVersion()); - } - } - - if(!read_only) - { - // Purge cache if user requested it - if (gSavedSettings.getBOOL("PurgeCacheOnStartup") || - gSavedSettings.getBOOL("PurgeCacheOnNextStartup")) - { - gSavedSettings.setBOOL("PurgeCacheOnNextStartup", false); - mPurgeCache = true; - } - - // We have moved the location of the cache directory over time. - migrateCacheDirectory(); - - // Setup and verify the cache location - std::string cache_location = gSavedSettings.getString("CacheLocation"); - std::string new_cache_location = gSavedSettings.getString("NewCacheLocation"); - if (new_cache_location != cache_location) - { - gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation")); - purgeCache(); // purge old cache - gSavedSettings.setString("CacheLocation", new_cache_location); - gSavedSettings.setString("CacheLocationTopFolder", gDirUtilp->getBaseFileName(new_cache_location)); - } - } - - if (!gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation"))) - { - LL_WARNS("AppCache") << "Unable to set cache location" << LL_ENDL; - gSavedSettings.setString("CacheLocation", ""); - gSavedSettings.setString("CacheLocationTopFolder", ""); - } - - if (mPurgeCache && !read_only) - { - LLSplashScreen::update(LLTrans::getString("StartupClearingCache")); - purgeCache(); - } - - LLSplashScreen::update(LLTrans::getString("StartupInitializingTextureCache")); - - // Init the texture cache - // Allocate 80% of the cache size for textures - const S32 MB = 1024*1024; - S64 cache_size = (S64)(gSavedSettings.getU32("CacheSize")) * MB; - const S64 MAX_CACHE_SIZE = 1024*MB; - cache_size = llmin(cache_size, MAX_CACHE_SIZE); - S64 texture_cache_size = ((cache_size * 8)/10); - S64 extra = LLAppViewer::getTextureCache()->initCache(LL_PATH_CACHE, texture_cache_size, texture_cache_mismatch); - texture_cache_size -= extra; - - LLVOCache::getInstance()->initCache(LL_PATH_CACHE, gSavedSettings.getU32("CacheNumberOfRegionsForObjects"), getObjectCacheVersion()) ; - - LLSplashScreen::update(LLTrans::getString("StartupInitializingVFS")); - - // Init the VFS - S64 vfs_size = cache_size - texture_cache_size; - const S64 MAX_VFS_SIZE = 1024 * MB; // 1 GB - vfs_size = llmin(vfs_size, MAX_VFS_SIZE); - vfs_size = (vfs_size / MB) * MB; // make sure it is MB aligned - U32 vfs_size_u32 = (U32)vfs_size; - U32 old_vfs_size = gSavedSettings.getU32("VFSOldSize") * MB; - bool resize_vfs = (vfs_size_u32 != old_vfs_size); - if (resize_vfs) - { - gSavedSettings.setU32("VFSOldSize", vfs_size_u32/MB); - } - LL_INFOS("AppCache") << "VFS CACHE SIZE: " << vfs_size/(1024*1024) << " MB" << LL_ENDL; - - // This has to happen BEFORE starting the vfs - //time_t ltime; - srand(time(NULL)); // Flawfinder: ignore - U32 old_salt = gSavedSettings.getU32("VFSSalt"); - U32 new_salt; - std::string old_vfs_data_file; - std::string old_vfs_index_file; - std::string new_vfs_data_file; - std::string new_vfs_index_file; - std::string static_vfs_index_file; - std::string static_vfs_data_file; - - if (gSavedSettings.getBOOL("AllowMultipleViewers")) - { - // don't mess with renaming the VFS in this case - new_salt = old_salt; - } - else - { - do - { - new_salt = rand(); - } while( new_salt == old_salt ); - } - - old_vfs_data_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_DATA_FILE_BASE) + llformat("%u",old_salt); - - // make sure this file exists - llstat s; - S32 stat_result = LLFile::stat(old_vfs_data_file, &s); - if (stat_result) - { - // doesn't exist, look for a data file - std::string mask; - mask = gDirUtilp->getDirDelimiter(); - mask += VFS_DATA_FILE_BASE; - mask += "*"; - - std::string dir; - dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""); - - std::string found_file; - if (gDirUtilp->getNextFileInDir(dir, mask, found_file)) - { - old_vfs_data_file = dir + gDirUtilp->getDirDelimiter() + found_file; - - S32 start_pos = found_file.find_last_of('.'); - if (start_pos > 0) - { - sscanf(found_file.substr(start_pos+1).c_str(), "%d", &old_salt); - } - LL_DEBUGS("AppCache") << "Default vfs data file not present, found: " << old_vfs_data_file << " Old salt: " << old_salt << llendl; - } - } - - old_vfs_index_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_INDEX_FILE_BASE) + llformat("%u",old_salt); - - stat_result = LLFile::stat(old_vfs_index_file, &s); - if (stat_result) - { - // We've got a bad/missing index file, nukem! - LL_WARNS("AppCache") << "Bad or missing vfx index file " << old_vfs_index_file << LL_ENDL; - LL_WARNS("AppCache") << "Removing old vfs data file " << old_vfs_data_file << LL_ENDL; - LLFile::remove(old_vfs_data_file); - LLFile::remove(old_vfs_index_file); - - // Just in case, nuke any other old cache files in the directory. - std::string dir; - dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""); - - std::string mask; - mask = gDirUtilp->getDirDelimiter(); - mask += VFS_DATA_FILE_BASE; - mask += "*"; - - gDirUtilp->deleteFilesInDir(dir, mask); - - mask = gDirUtilp->getDirDelimiter(); - mask += VFS_INDEX_FILE_BASE; - mask += "*"; - - gDirUtilp->deleteFilesInDir(dir, mask); - } - - new_vfs_data_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_DATA_FILE_BASE) + llformat("%u",new_salt); - new_vfs_index_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, VFS_INDEX_FILE_BASE) + llformat("%u",new_salt); - - static_vfs_data_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"static_data.db2"); - static_vfs_index_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"static_index.db2"); - - if (resize_vfs) - { - LL_DEBUGS("AppCache") << "Removing old vfs and re-sizing" << LL_ENDL; - - LLFile::remove(old_vfs_data_file); - LLFile::remove(old_vfs_index_file); - } - else if (old_salt != new_salt) - { - // move the vfs files to a new name before opening - LL_DEBUGS("AppCache") << "Renaming " << old_vfs_data_file << " to " << new_vfs_data_file << LL_ENDL; - LL_DEBUGS("AppCache") << "Renaming " << old_vfs_index_file << " to " << new_vfs_index_file << LL_ENDL; - LLFile::rename(old_vfs_data_file, new_vfs_data_file); - LLFile::rename(old_vfs_index_file, new_vfs_index_file); - } - - // Startup the VFS... - gSavedSettings.setU32("VFSSalt", new_salt); - - // Don't remove VFS after viewer crashes. If user has corrupt data, they can reinstall. JC - gVFS = LLVFS::createLLVFS(new_vfs_index_file, new_vfs_data_file, false, vfs_size_u32, false); - if( !gVFS ) - { - return false; - } - - gStaticVFS = LLVFS::createLLVFS(static_vfs_index_file, static_vfs_data_file, true, 0, false); - if( !gStaticVFS ) - { - return false; - } - - BOOL success = gVFS->isValid() && gStaticVFS->isValid(); - if( !success ) - { - return false; - } - else - { - LLVFile::initClass(); - -#ifndef LL_RELEASE_FOR_DOWNLOAD - if (gSavedSettings.getBOOL("DumpVFSCaches")) - { - dumpVFSCaches(); - } -#endif - - return true; - } -} - -void LLAppViewer::purgeCache() -{ - LL_INFOS("AppCache") << "Purging Cache and Texture Cache..." << llendl; - LLAppViewer::getTextureCache()->purgeCache(LL_PATH_CACHE); - LLVOCache::getInstance()->removeCache(LL_PATH_CACHE); - std::string mask = gDirUtilp->getDirDelimiter() + "*.*"; - gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask); -} - -std::string LLAppViewer::getSecondLifeTitle() const -{ - return LLTrans::getString("APP_NAME"); -} - -std::string LLAppViewer::getWindowTitle() const -{ - return gWindowTitle; -} - -// Callback from a dialog indicating user was logged out. -bool finish_disconnect(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - - if (1 == option) - { - LLAppViewer::instance()->forceQuit(); - } - return false; -} - -// Callback from an early disconnect dialog, force an exit -bool finish_forced_disconnect(const LLSD& notification, const LLSD& response) -{ - LLAppViewer::instance()->forceQuit(); - return false; -} - - -void LLAppViewer::forceDisconnect(const std::string& mesg) -{ - if (gDoDisconnect) - { - // Already popped up one of these dialogs, don't - // do this again. - return; - } - - // *TODO: Translate the message if possible - std::string big_reason = LLAgent::sTeleportErrorMessages[mesg]; - if ( big_reason.size() == 0 ) - { - big_reason = mesg; - } - - LLSD args; - gDoDisconnect = TRUE; - - if (LLStartUp::getStartupState() < STATE_STARTED) - { - // Tell users what happened - args["ERROR_MESSAGE"] = big_reason; - LLNotificationsUtil::add("ErrorMessage", args, LLSD(), &finish_forced_disconnect); - } - else - { - args["MESSAGE"] = big_reason; - LLNotificationsUtil::add("YouHaveBeenLoggedOut", args, LLSD(), &finish_disconnect ); - } -} - -void LLAppViewer::badNetworkHandler() -{ - // Dump the packet - gMessageSystem->dumpPacketToLog(); - - // Flush all of our caches on exit in the case of disconnect due to - // invalid packets. - - mPurgeOnExit = TRUE; - - std::ostringstream message; - message << - "The viewer has detected mangled network data indicative\n" - "of a bad upstream network connection or an incomplete\n" - "local installation of " << LLAppViewer::instance()->getSecondLifeTitle() << ". \n" - " \n" - "Try uninstalling and reinstalling to see if this resolves \n" - "the issue. \n" - " \n" - "If the problem continues, see the Tech Support FAQ at: \n" - "www.secondlife.com/support"; - forceDisconnect(message.str()); - - LLApp::instance()->writeMiniDump(); -} - -// This routine may get called more than once during the shutdown process. -// This can happen because we need to get the screenshot before the window -// is destroyed. -void LLAppViewer::saveFinalSnapshot() -{ - if (!mSavedFinalSnapshot && !gNoRender) - { - gSavedSettings.setVector3d("FocusPosOnLogout", gAgentCamera.calcFocusPositionTargetGlobal()); - gSavedSettings.setVector3d("CameraPosOnLogout", gAgentCamera.calcCameraPositionTargetGlobal()); - gViewerWindow->setCursor(UI_CURSOR_WAIT); - gAgentCamera.changeCameraToThirdPerson( FALSE ); // don't animate, need immediate switch - gSavedSettings.setBOOL("ShowParcelOwners", FALSE); - idle(); - - std::string snap_filename = gDirUtilp->getLindenUserDir(); - snap_filename += gDirUtilp->getDirDelimiter(); - snap_filename += SCREEN_LAST_FILENAME; - // use full pixel dimensions of viewer window (not post-scale dimensions) - gViewerWindow->saveSnapshot(snap_filename, gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw(), FALSE, TRUE); - mSavedFinalSnapshot = TRUE; - } -} - -void LLAppViewer::loadNameCache() -{ - // display names cache - std::string filename = - gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); - LL_INFOS("AvNameCache") << filename << LL_ENDL; - llifstream name_cache_stream(filename); - if(name_cache_stream.is_open()) - { - LLAvatarNameCache::importFile(name_cache_stream); - } - - if (!gCacheName) return; - - std::string name_cache; - name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); - llifstream cache_file(name_cache); - if(cache_file.is_open()) - { - if(gCacheName->importFile(cache_file)) return; - } -} - -void LLAppViewer::saveNameCache() - { - // display names cache - std::string filename = - gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); - llofstream name_cache_stream(filename); - if(name_cache_stream.is_open()) - { - LLAvatarNameCache::exportFile(name_cache_stream); -} - - if (!gCacheName) return; - - std::string name_cache; - name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); - llofstream cache_file(name_cache); - if(cache_file.is_open()) - { - gCacheName->exportFile(cache_file); - } -} - -/*! @brief This class is an LLFrameTimer that can be created with - an elapsed time that starts counting up from the given value - rather than 0.0. - - Otherwise it behaves the same way as LLFrameTimer. -*/ -class LLFrameStatsTimer : public LLFrameTimer -{ -public: - LLFrameStatsTimer(F64 elapsed_already = 0.0) - : LLFrameTimer() - { - mStartTime -= elapsed_already; - } -}; - -static LLFastTimer::DeclareTimer FTM_AUDIO_UPDATE("Update Audio"); -static LLFastTimer::DeclareTimer FTM_CLEANUP("Cleanup"); -static LLFastTimer::DeclareTimer FTM_IDLE_CB("Idle Callbacks"); -static LLFastTimer::DeclareTimer FTM_LOD_UPDATE("Update LOD"); -static LLFastTimer::DeclareTimer FTM_OBJECTLIST_UPDATE("Update Objectlist"); -static LLFastTimer::DeclareTimer FTM_REGION_UPDATE("Update Region"); -static LLFastTimer::DeclareTimer FTM_WORLD_UPDATE("Update World"); -static LLFastTimer::DeclareTimer FTM_NETWORK("Network"); - -/////////////////////////////////////////////////////// -// idle() -// -// Called every time the window is not doing anything. -// Receive packets, update statistics, and schedule a redisplay. -/////////////////////////////////////////////////////// -void LLAppViewer::idle() -{ - LLMemType mt_idle(LLMemType::MTYPE_IDLE); - pingMainloopTimeout("Main:Idle"); - - // Update frame timers - static LLTimer idle_timer; - - LLFrameTimer::updateFrameTime(); - LLFrameTimer::updateFrameCount(); - LLEventTimer::updateClass(); - LLCriticalDamp::updateInterpolants(); - LLMortician::updateClass(); - F32 dt_raw = idle_timer.getElapsedTimeAndResetF32(); - - // Cap out-of-control frame times - // Too low because in menus, swapping, debugger, etc. - // Too high because idle called with no objects in view, etc. - const F32 MIN_FRAME_RATE = 1.f; - const F32 MAX_FRAME_RATE = 200.f; - - F32 frame_rate_clamped = 1.f / dt_raw; - frame_rate_clamped = llclamp(frame_rate_clamped, MIN_FRAME_RATE, MAX_FRAME_RATE); - gFrameDTClamped = 1.f / frame_rate_clamped; - - // Global frame timer - // Smoothly weight toward current frame - gFPSClamped = (frame_rate_clamped + (4.f * gFPSClamped)) / 5.f; - - F32 qas = gSavedSettings.getF32("QuitAfterSeconds"); - if (qas > 0.f) - { - if (gRenderStartTime.getElapsedTimeF32() > qas) - { - LLAppViewer::instance()->forceQuit(); - } - } - - // debug setting to quit after N seconds of being AFK - 0 to never do this - F32 qas_afk = gSavedSettings.getF32("QuitAfterSecondsOfAFK"); - if (qas_afk > 0.f) - { - // idle time is more than setting - if ( gAwayTriggerTimer.getElapsedTimeF32() > qas_afk ) - { - // go ahead and just quit gracefully - LLAppViewer::instance()->requestQuit(); - } - } - - // Must wait until both have avatar object and mute list, so poll - // here. - request_initial_instant_messages(); - - /////////////////////////////////// - // - // Special case idle if still starting up - // - if (LLStartUp::getStartupState() < STATE_STARTED) - { - // Skip rest if idle startup returns false (essentially, no world yet) - gGLActive = TRUE; - if (!idle_startup()) - { - gGLActive = FALSE; - return; - } - gGLActive = FALSE; - } - - - F32 yaw = 0.f; // radians - - if (!gDisconnected) - { - LLFastTimer t(FTM_NETWORK); - // Update spaceserver timeinfo - LLWorld::getInstance()->setSpaceTimeUSec(LLWorld::getInstance()->getSpaceTimeUSec() + (U32)(dt_raw * SEC_TO_MICROSEC)); - - - ////////////////////////////////////// - // - // Update simulator agent state - // - - if (gSavedSettings.getBOOL("RotateRight")) - { - gAgent.moveYaw(-1.f); - } - - { - LLFastTimer t(FTM_AGENT_AUTOPILOT); - // Handle automatic walking towards points - gAgentPilot.updateTarget(); - gAgent.autoPilot(&yaw); - } - - static LLFrameTimer agent_update_timer; - static U32 last_control_flags; - - // When appropriate, update agent location to the simulator. - F32 agent_update_time = agent_update_timer.getElapsedTimeF32(); - BOOL flags_changed = gAgent.controlFlagsDirty() || (last_control_flags != gAgent.getControlFlags()); - - if (flags_changed || (agent_update_time > (1.0f / (F32) AGENT_UPDATES_PER_SECOND))) - { - LLFastTimer t(FTM_AGENT_UPDATE); - // Send avatar and camera info - last_control_flags = gAgent.getControlFlags(); - send_agent_update(TRUE); - agent_update_timer.reset(); - } - } - - ////////////////////////////////////// - // - // Manage statistics - // - // - { - // Initialize the viewer_stats_timer with an already elapsed time - // of SEND_STATS_PERIOD so that the initial stats report will - // be sent immediately. - static LLFrameStatsTimer viewer_stats_timer(SEND_STATS_PERIOD); - reset_statistics(); - - // Update session stats every large chunk of time - // *FIX: (???) SAMANTHA - if (viewer_stats_timer.getElapsedTimeF32() >= SEND_STATS_PERIOD && !gDisconnected) - { - llinfos << "Transmitting sessions stats" << llendl; - send_stats(); - viewer_stats_timer.reset(); - } - - // Print the object debugging stats - static LLFrameTimer object_debug_timer; - if (object_debug_timer.getElapsedTimeF32() > 5.f) - { - object_debug_timer.reset(); - if (gObjectList.mNumDeadObjectUpdates) - { - llinfos << "Dead object updates: " << gObjectList.mNumDeadObjectUpdates << llendl; - gObjectList.mNumDeadObjectUpdates = 0; - } - if (gObjectList.mNumUnknownKills) - { - llinfos << "Kills on unknown objects: " << gObjectList.mNumUnknownKills << llendl; - gObjectList.mNumUnknownKills = 0; - } - if (gObjectList.mNumUnknownUpdates) - { - llinfos << "Unknown object updates: " << gObjectList.mNumUnknownUpdates << llendl; - gObjectList.mNumUnknownUpdates = 0; - } - - // ViewerMetrics FPS piggy-backing on the debug timer. - // The 5-second interval is nice for this purpose. If the object debug - // bit moves or is disabled, please give this a suitable home. - LLViewerAssetStatsFF::record_fps_main(gFPSClamped); - } - } - - if (!gDisconnected) - { - LLFastTimer t(FTM_NETWORK); - - //////////////////////////////////////////////// - // - // Network processing - // - // NOTE: Starting at this point, we may still have pointers to "dead" objects - // floating throughout the various object lists. - // - idleNameCache(); - - idleNetwork(); - - - // Check for away from keyboard, kick idle agents. - idle_afk_check(); - - // Update statistics for this frame - update_statistics(gFrameCount); - } - - //////////////////////////////////////// - // - // Handle the regular UI idle callbacks as well as - // hover callbacks - // - - { -// LLFastTimer t(FTM_IDLE_CB); - - // Do event notifications if necessary. Yes, we may want to move this elsewhere. - gEventNotifier.update(); - - gIdleCallbacks.callFunctions(); - gInventory.idleNotifyObservers(); - } - - // Metrics logging (LLViewerAssetStats, etc.) - { - static LLTimer report_interval; - - // *TODO: Add configuration controls for this - if (report_interval.getElapsedTimeF32() >= app_metrics_interval) - { - metricsSend(! gDisconnected); - report_interval.reset(); - } - } - - if (gDisconnected) - { - return; - } - - gViewerWindow->updateUI(); - - /////////////////////////////////////// - // Agent and camera movement - // - LLCoordGL current_mouse = gViewerWindow->getCurrentMouse(); - - { - // After agent and camera moved, figure out if we need to - // deselect objects. - LLSelectMgr::getInstance()->deselectAllIfTooFar(); - - } - - { - // Handle pending gesture processing - static LLFastTimer::DeclareTimer ftm("Agent Position"); - LLFastTimer t(ftm); - LLGestureMgr::instance().update(); - - gAgent.updateAgentPosition(gFrameDTClamped, yaw, current_mouse.mX, current_mouse.mY); - } - - { - LLFastTimer t(FTM_OBJECTLIST_UPDATE); - - if (!(logoutRequestSent() && hasSavedFinalSnapshot())) - { - gObjectList.update(gAgent, *LLWorld::getInstance()); - } - } - - ////////////////////////////////////// - // - // Deletes objects... - // Has to be done after doing idleUpdates (which can kill objects) - // - - { - LLFastTimer t(FTM_CLEANUP); - gObjectList.cleanDeadObjects(); - LLDrawable::cleanupDeadDrawables(); - } - - // - // After this point, in theory we should never see a dead object - // in the various object/drawable lists. - // - - ////////////////////////////////////// - // - // Update/send HUD effects - // - // At this point, HUD effects may clean up some references to - // dead objects. - // - - { - static LLFastTimer::DeclareTimer ftm("HUD Effects"); - LLFastTimer t(ftm); - LLSelectMgr::getInstance()->updateEffects(); - LLHUDManager::getInstance()->cleanupEffects(); - LLHUDManager::getInstance()->sendEffects(); - } - - //////////////////////////////////////// - // - // Unpack layer data that we've received - // - - { - LLFastTimer t(FTM_NETWORK); - gVLManager.unpackData(); - } - - ///////////////////////// - // - // Update surfaces, and surface textures as well. - // - - LLWorld::getInstance()->updateVisibilities(); - { - const F32 max_region_update_time = .001f; // 1ms - LLFastTimer t(FTM_REGION_UPDATE); - LLWorld::getInstance()->updateRegions(max_region_update_time); - } - - ///////////////////////// - // - // Update weather effects - // - if (!gNoRender) - { - LLWorld::getInstance()->updateClouds(gFrameDTClamped); - gSky.propagateHeavenlyBodies(gFrameDTClamped); // moves sun, moon, and planets - - // Update wind vector - LLVector3 wind_position_region; - static LLVector3 average_wind; - - LLViewerRegion *regionp; - regionp = LLWorld::getInstance()->resolveRegionGlobal(wind_position_region, gAgent.getPositionGlobal()); // puts agent's local coords into wind_position - if (regionp) - { - gWindVec = regionp->mWind.getVelocity(wind_position_region); - - // Compute average wind and use to drive motion of water - - average_wind = regionp->mWind.getAverage(); - F32 cloud_density = regionp->mCloudLayer.getDensityRegion(wind_position_region); - - gSky.setCloudDensityAtAgent(cloud_density); - gSky.setWind(average_wind); - //LLVOWater::setWind(average_wind); - } - else - { - gWindVec.setVec(0.0f, 0.0f, 0.0f); - } - } - - ////////////////////////////////////// - // - // Sort and cull in the new renderer are moved to pipeline.cpp - // Here, particles are updated and drawables are moved. - // - - if (!gNoRender) - { - LLFastTimer t(FTM_WORLD_UPDATE); - gPipeline.updateMove(); - - LLWorld::getInstance()->updateParticles(); - } - - if (LLViewerJoystick::getInstance()->getOverrideCamera()) - { - LLViewerJoystick::getInstance()->moveFlycam(); - } - else - { - if (LLToolMgr::getInstance()->inBuildMode()) - { - LLViewerJoystick::getInstance()->moveObjects(); - } - - gAgentCamera.updateCamera(); - } - - // update media focus - LLViewerMediaFocus::getInstance()->update(); - - // objects and camera should be in sync, do LOD calculations now - { - LLFastTimer t(FTM_LOD_UPDATE); - gObjectList.updateApparentAngles(gAgent); - } - - { - LLFastTimer t(FTM_AUDIO_UPDATE); - - if (gAudiop) - { - audio_update_volume(false); - audio_update_listener(); - audio_update_wind(false); - - // this line actually commits the changes we've made to source positions, etc. - const F32 max_audio_decode_time = 0.002f; // 2 ms decode time - gAudiop->idle(max_audio_decode_time); - } - } - - // Handle shutdown process, for example, - // wait for floaters to close, send quit message, - // forcibly quit if it has taken too long - if (mQuitRequested) - { - gGLActive = TRUE; - idleShutdown(); - } -} - -void LLAppViewer::idleShutdown() -{ - // Wait for all modal alerts to get resolved - if (LLModalDialog::activeCount() > 0) - { - return; - } - - // close IM interface - if(gIMMgr) - { - gIMMgr->disconnectAllSessions(); - } - - // Wait for all floaters to get resolved - if (gFloaterView - && !gFloaterView->allChildrenClosed()) - { - return; - } - - if (LLSideTray::getInstance()->notifyChildren(LLSD().with("request","wait_quit"))) - { - return; - } - - - - // ProductEngine: Try moving this code to where we shut down sTextureCache in cleanup() - // *TODO: ugly - static bool saved_teleport_history = false; - if (!saved_teleport_history) - { - saved_teleport_history = true; - LLTeleportHistory::getInstance()->dump(); - LLLocationHistory::getInstance()->save(); // *TODO: find a better place for doing this - return; - } - - static bool saved_snapshot = false; - if (!saved_snapshot) - { - saved_snapshot = true; - saveFinalSnapshot(); - return; - } - - const F32 SHUTDOWN_UPLOAD_SAVE_TIME = 5.f; - - S32 pending_uploads = gAssetStorage->getNumPendingUploads(); - if (pending_uploads > 0 - && gLogoutTimer.getElapsedTimeF32() < SHUTDOWN_UPLOAD_SAVE_TIME - && !logoutRequestSent()) - { - static S32 total_uploads = 0; - // Sometimes total upload count can change during logout. - total_uploads = llmax(total_uploads, pending_uploads); - gViewerWindow->setShowProgress(TRUE); - S32 finished_uploads = total_uploads - pending_uploads; - F32 percent = 100.f * finished_uploads / total_uploads; - gViewerWindow->setProgressPercent(percent); - gViewerWindow->setProgressString(LLTrans::getString("SavingSettings")); - return; - } - - // All floaters are closed. Tell server we want to quit. - if( !logoutRequestSent() ) - { - sendLogoutRequest(); - - // Wait for a LogoutReply message - gViewerWindow->setShowProgress(TRUE); - gViewerWindow->setProgressPercent(100.f); - gViewerWindow->setProgressString(LLTrans::getString("LoggingOut")); - return; - } - - // Make sure that we quit if we haven't received a reply from the server. - if( logoutRequestSent() - && gLogoutTimer.getElapsedTimeF32() > gLogoutMaxTime ) - { - forceQuit(); - return; - } -} - -void LLAppViewer::sendLogoutRequest() -{ - if(!mLogoutRequestSent) - { - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_LogoutRequest); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gAgent.sendReliableMessage(); - - gLogoutTimer.reset(); - gLogoutMaxTime = LOGOUT_REQUEST_TIME; - mLogoutRequestSent = TRUE; - - if(LLVoiceClient::instanceExists()) - { - LLVoiceClient::getInstance()->leaveChannel(); - } - - //Set internal status variables and marker files - gLogoutInProgress = TRUE; - mLogoutMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,LOGOUT_MARKER_FILE_NAME); - - LLAPRFile outfile ; - outfile.open(mLogoutMarkerFileName, LL_APR_W); - mLogoutMarkerFile = outfile.getFileHandle() ; - if (mLogoutMarkerFile) - { - llinfos << "Created logout marker file " << mLogoutMarkerFileName << llendl; - apr_file_close(mLogoutMarkerFile); - } - else - { - llwarns << "Cannot create logout marker file " << mLogoutMarkerFileName << llendl; - } - } -} - -void LLAppViewer::idleNameCache() -{ - // Neither old nor new name cache can function before agent has a region - LLViewerRegion* region = gAgent.getRegion(); - if (!region) return; - - // deal with any queued name requests and replies. - gCacheName->processPending(); - - // Can't run the new cache until we have the list of capabilities - // for the agent region, and can therefore decide whether to use - // display names or fall back to the old name system. - if (!region->capabilitiesReceived()) return; - - // Agent may have moved to a different region, so need to update cap URL - // for name lookups. Can't do this in the cap grant code, as caps are - // granted to neighbor regions before the main agent gets there. Can't - // do it in the move-into-region code because cap not guaranteed to be - // granted yet, for example on teleport. - bool had_capability = LLAvatarNameCache::hasNameLookupURL(); - std::string name_lookup_url; - name_lookup_url.reserve(128); // avoid a memory allocation below - name_lookup_url = region->getCapability("GetDisplayNames"); - bool have_capability = !name_lookup_url.empty(); - if (have_capability) - { - // we have support for display names, use it - U32 url_size = name_lookup_url.size(); - // capabilities require URLs with slashes before query params: - // https://:/cap//?ids= - // but the caps are granted like: - // https://:/cap/ - if (url_size > 0 && name_lookup_url[url_size-1] != '/') - { - name_lookup_url += '/'; - } - LLAvatarNameCache::setNameLookupURL(name_lookup_url); - } - else - { - // Display names not available on this region - LLAvatarNameCache::setNameLookupURL( std::string() ); - } - - // Error recovery - did we change state? - if (had_capability != have_capability) - { - // name tags are persistant on screen, so make sure they refresh - LLVOAvatar::invalidateNameTags(); - } - - LLAvatarNameCache::idle(); -} - -// -// Handle messages, and all message related stuff -// - -#define TIME_THROTTLE_MESSAGES - -#ifdef TIME_THROTTLE_MESSAGES -#define CHECK_MESSAGES_DEFAULT_MAX_TIME .020f // 50 ms = 50 fps (just for messages!) -static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; -#endif - -static LLFastTimer::DeclareTimer FTM_IDLE_NETWORK("Idle Network"); - -void LLAppViewer::idleNetwork() -{ - LLMemType mt_in(LLMemType::MTYPE_IDLE_NETWORK); - pingMainloopTimeout("idleNetwork"); - - gObjectList.mNumNewObjects = 0; - S32 total_decoded = 0; - - if (!gSavedSettings.getBOOL("SpeedTest")) - { - LLFastTimer t(FTM_IDLE_NETWORK); // decode - - LLTimer check_message_timer; - // Read all available packets from network - const S64 frame_count = gFrameCount; // U32->S64 - F32 total_time = 0.0f; - - while (gMessageSystem->checkAllMessages(frame_count, gServicePump)) - { - if (gDoDisconnect) - { - // We're disconnecting, don't process any more messages from the server - // We're usually disconnecting due to either network corruption or a - // server going down, so this is OK. - break; - } - - total_decoded++; - gPacketsIn++; - - if (total_decoded > MESSAGE_MAX_PER_FRAME) - { - break; - } - -#ifdef TIME_THROTTLE_MESSAGES - // Prevent slow packets from completely destroying the frame rate. - // This usually happens due to clumps of avatars taking huge amount - // of network processing time (which needs to be fixed, but this is - // a good limit anyway). - total_time = check_message_timer.getElapsedTimeF32(); - if (total_time >= CheckMessagesMaxTime) - break; -#endif - } - - // Handle per-frame message system processing. - gMessageSystem->processAcks(); - -#ifdef TIME_THROTTLE_MESSAGES - if (total_time >= CheckMessagesMaxTime) - { - // Increase CheckMessagesMaxTime so that we will eventually catch up - CheckMessagesMaxTime *= 1.035f; // 3.5% ~= x2 in 20 frames, ~8x in 60 frames - } - else - { - // Reset CheckMessagesMaxTime to default value - CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; - } -#endif - - - - // we want to clear the control after sending out all necessary agent updates - gAgent.resetControlFlags(); - - // Decode enqueued messages... - S32 remaining_possible_decodes = MESSAGE_MAX_PER_FRAME - total_decoded; - - if( remaining_possible_decodes <= 0 ) - { - llinfos << "Maxed out number of messages per frame at " << MESSAGE_MAX_PER_FRAME << llendl; - } - - if (gPrintMessagesThisFrame) - { - llinfos << "Decoded " << total_decoded << " msgs this frame!" << llendl; - gPrintMessagesThisFrame = FALSE; - } - } - LLViewerStats::getInstance()->mNumNewObjectsStat.addValue(gObjectList.mNumNewObjects); - - // Retransmit unacknowledged packets. - gXferManager->retransmitUnackedPackets(); - gAssetStorage->checkForTimeouts(); - gViewerThrottle.updateDynamicThrottle(); - - // Check that the circuit between the viewer and the agent's current - // region is still alive - LLViewerRegion *agent_region = gAgent.getRegion(); - if (agent_region && (LLStartUp::getStartupState()==STATE_STARTED)) - { - LLUUID this_region_id = agent_region->getRegionID(); - bool this_region_alive = agent_region->isAlive(); - if ((mAgentRegionLastAlive && !this_region_alive) // newly dead - && (mAgentRegionLastID == this_region_id)) // same region - { - forceDisconnect(LLTrans::getString("AgentLostConnection")); - } - mAgentRegionLastID = this_region_id; - mAgentRegionLastAlive = this_region_alive; - } -} - -void LLAppViewer::disconnectViewer() -{ - if (gDisconnected) - { - return; - } - // - // Cleanup after quitting. - // - // Save snapshot for next time, if we made it through initialization - - llinfos << "Disconnecting viewer!" << llendl; - - // Dump our frame statistics - - // Remember if we were flying - gSavedSettings.setBOOL("FlyingAtExit", gAgent.getFlying() ); - - // Un-minimize all windows so they don't get saved minimized - if (!gNoRender) - { - if (gFloaterView) - { - gFloaterView->restoreAll(); - } - } - - if (LLSelectMgr::getInstance()) - { - LLSelectMgr::getInstance()->deselectAll(); - } - - // save inventory if appropriate - gInventory.cache(gInventory.getRootFolderID(), gAgent.getID()); - if (gInventory.getLibraryRootFolderID().notNull() - && gInventory.getLibraryOwnerID().notNull()) - { - gInventory.cache( - gInventory.getLibraryRootFolderID(), - gInventory.getLibraryOwnerID()); - } - - saveNameCache(); - - // close inventory interface, close all windows - LLFloaterInventory::cleanup(); - - gAgentWearables.cleanup(); - gAgentCamera.cleanup(); - // Also writes cached agent settings to gSavedSettings - gAgent.cleanup(); - - // This is where we used to call gObjectList.destroy() and then delete gWorldp. - // Now we just ask the LLWorld singleton to cleanly shut down. - if(LLWorld::instanceExists()) - { - LLWorld::getInstance()->destroyClass(); - } - - // call all self-registered classes - LLDestroyClassList::instance().fireCallbacks(); - - cleanup_xfer_manager(); - gDisconnected = TRUE; - - // Pass the connection state to LLUrlEntryParcel not to attempt - // parcel info requests while disconnected. - LLUrlEntryParcel::setDisconnected(gDisconnected); -} - -void LLAppViewer::forceErrorLLError() -{ - llerrs << "This is an llerror" << llendl; -} - -void LLAppViewer::forceErrorBreakpoint() -{ -#ifdef LL_WINDOWS - DebugBreak(); -#endif - return; -} - -void LLAppViewer::forceErrorBadMemoryAccess() -{ - S32* crash = NULL; - *crash = 0xDEADBEEF; - return; -} - -void LLAppViewer::forceErrorInfiniteLoop() -{ - while(true) - { - ; - } - return; -} - -void LLAppViewer::forceErrorSoftwareException() -{ - // *FIX: Any way to insure it won't be handled? - throw; -} - -void LLAppViewer::forceErrorDriverCrash() -{ - glDeleteTextures(1, NULL); -} - -void LLAppViewer::initMainloopTimeout(const std::string& state, F32 secs) -{ - if(!mMainloopTimeout) - { - mMainloopTimeout = new LLWatchdogTimeout(); - resumeMainloopTimeout(state, secs); - } -} - -void LLAppViewer::destroyMainloopTimeout() -{ - if(mMainloopTimeout) - { - delete mMainloopTimeout; - mMainloopTimeout = NULL; - } -} - -void LLAppViewer::resumeMainloopTimeout(const std::string& state, F32 secs) -{ - if(mMainloopTimeout) - { - if(secs < 0.0f) - { - secs = gSavedSettings.getF32("MainloopTimeoutDefault"); - } - - mMainloopTimeout->setTimeout(secs); - mMainloopTimeout->start(state); - } -} - -void LLAppViewer::pauseMainloopTimeout() -{ - if(mMainloopTimeout) - { - mMainloopTimeout->stop(); - } -} - -void LLAppViewer::pingMainloopTimeout(const std::string& state, F32 secs) -{ -// if(!restoreErrorTrap()) -// { -// llwarns << "!!!!!!!!!!!!! Its an error trap!!!!" << state << llendl; -// } - - if(mMainloopTimeout) - { - if(secs < 0.0f) - { - secs = gSavedSettings.getF32("MainloopTimeoutDefault"); - } - - mMainloopTimeout->setTimeout(secs); - mMainloopTimeout->ping(state); - } -} - -void LLAppViewer::handleLoginComplete() -{ - gLoggedInTime.start(); - initMainloopTimeout("Mainloop Init"); - - // Store some data to DebugInfo in case of a freeze. - gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel(); - - gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor(); - gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); - gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch(); - gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::getBuild(); - - LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if ( parcel && parcel->getMusicURL()[0]) - { - gDebugInfo["ParcelMusicURL"] = parcel->getMusicURL(); - } - if ( parcel && parcel->getMediaURL()[0]) - { - gDebugInfo["ParcelMediaURL"] = parcel->getMediaURL(); - } - - gDebugInfo["SettingsFilename"] = gSavedSettings.getString("ClientSettingsFile"); - gDebugInfo["CAFilename"] = gDirUtilp->getCAFile(); - gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName(); - gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath(); - - if(gAgent.getRegion()) - { - gDebugInfo["CurrentSimHost"] = gAgent.getRegionHost().getHostName(); - gDebugInfo["CurrentRegion"] = gAgent.getRegion()->getName(); - } - - if(LLAppViewer::instance()->mMainloopTimeout) - { - gDebugInfo["MainloopTimeoutState"] = LLAppViewer::instance()->mMainloopTimeout->getState(); - } - - mOnLoginCompleted(); - - writeDebugInfo(); -} - -// *TODO - generalize this and move DSO wrangling to a helper class -brad -void LLAppViewer::loadEventHostModule(S32 listen_port) -{ - std::string dso_name = -#if LL_WINDOWS - "lleventhost.dll"; -#elif LL_DARWIN - "liblleventhost.dylib"; -#else - "liblleventhost.so"; -#endif - - std::string dso_path = gDirUtilp->findFile(dso_name, - gDirUtilp->getAppRODataDir(), - gDirUtilp->getExecutableDir()); - - if(dso_path == "") - { - llerrs << "QAModeEventHost requested but module \"" << dso_name << "\" not found!" << llendl; - return; - } - - LL_INFOS("eventhost") << "Found lleventhost at '" << dso_path << "'" << LL_ENDL; -#if ! defined(LL_WINDOWS) - { - std::string outfile("/tmp/lleventhost.file.out"); - std::string command("file '" + dso_path + "' > '" + outfile + "' 2>&1"); - int rc = system(command.c_str()); - if (rc != 0) - { - LL_WARNS("eventhost") << command << " ==> " << rc << ':' << LL_ENDL; - } - else - { - LL_INFOS("eventhost") << command << ':' << LL_ENDL; - } - { - std::ifstream reader(outfile.c_str()); - std::string line; - while (std::getline(reader, line)) - { - size_t len = line.length(); - if (len && line[len-1] == '\n') - line.erase(len-1); - LL_INFOS("eventhost") << line << LL_ENDL; - } - } - remove(outfile.c_str()); - } -#endif // LL_WINDOWS - - apr_dso_handle_t * eventhost_dso_handle = NULL; - apr_pool_t * eventhost_dso_memory_pool = NULL; - - //attempt to load the shared library - apr_pool_create(&eventhost_dso_memory_pool, NULL); - apr_status_t rv = apr_dso_load(&eventhost_dso_handle, - dso_path.c_str(), - eventhost_dso_memory_pool); - llassert_always(! ll_apr_warn_status(rv, eventhost_dso_handle)); - llassert_always(eventhost_dso_handle != NULL); - - int (*ll_plugin_start_func)(LLSD const &) = NULL; - rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll_plugin_start_func, eventhost_dso_handle, "ll_plugin_start"); - - llassert_always(! ll_apr_warn_status(rv, eventhost_dso_handle)); - llassert_always(ll_plugin_start_func != NULL); - - LLSD args; - args["listen_port"] = listen_port; - - int status = ll_plugin_start_func(args); - - if(status != 0) - { - llerrs << "problem loading eventhost plugin, status: " << status << llendl; - } - - mPlugins.insert(eventhost_dso_handle); -} - -void LLAppViewer::launchUpdater() -{ - LLSD query_map = LLSD::emptyMap(); - // *TODO place os string in a global constant -#if LL_WINDOWS - query_map["os"] = "win"; -#elif LL_DARWIN - query_map["os"] = "mac"; -#elif LL_LINUX - query_map["os"] = "lnx"; -#elif LL_SOLARIS - query_map["os"] = "sol"; -#endif - // *TODO change userserver to be grid on both viewer and sim, since - // userserver no longer exists. - query_map["userserver"] = LLGridManager::getInstance()->getGridLabel(); - query_map["channel"] = LLVersionInfo::getChannel(); - // *TODO constantize this guy - // *NOTE: This URL is also used in win_setup/lldownloader.cpp - LLURI update_url = LLURI::buildHTTP("secondlife.com", 80, "update.php", query_map); - - if(LLAppViewer::sUpdaterInfo) - { - delete LLAppViewer::sUpdaterInfo; - } - LLAppViewer::sUpdaterInfo = new LLAppViewer::LLUpdaterInfo() ; - - // if a sim name was passed in via command line parameter (typically through a SLURL) - if ( LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION ) - { - // record the location to start at next time - gSavedSettings.setString( "NextLoginLocation", LLStartUp::getStartSLURL().getSLURLString()); - }; - -#if LL_WINDOWS - LLAppViewer::sUpdaterInfo->mUpdateExePath = gDirUtilp->getTempFilename(); - if (LLAppViewer::sUpdaterInfo->mUpdateExePath.empty()) - { - delete LLAppViewer::sUpdaterInfo ; - LLAppViewer::sUpdaterInfo = NULL ; - - // We're hosed, bail - LL_WARNS("AppInit") << "LLDir::getTempFilename() failed" << LL_ENDL; - return; - } - - LLAppViewer::sUpdaterInfo->mUpdateExePath += ".exe"; - - std::string updater_source = gDirUtilp->getAppRODataDir(); - updater_source += gDirUtilp->getDirDelimiter(); - updater_source += "updater.exe"; - - LL_DEBUGS("AppInit") << "Calling CopyFile source: " << updater_source - << " dest: " << LLAppViewer::sUpdaterInfo->mUpdateExePath - << LL_ENDL; - - - if (!CopyFileA(updater_source.c_str(), LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), FALSE)) - { - delete LLAppViewer::sUpdaterInfo ; - LLAppViewer::sUpdaterInfo = NULL ; - - LL_WARNS("AppInit") << "Unable to copy the updater!" << LL_ENDL; - - return; - } - - LLAppViewer::sUpdaterInfo->mParams << "-url \"" << update_url.asString() << "\""; - - LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << " " << LLAppViewer::sUpdaterInfo->mParams.str() << LL_ENDL; - - //Explicitly remove the marker file, otherwise we pass the lock onto the child process and things get weird. - LLAppViewer::instance()->removeMarkerFile(); // In case updater fails - - // *NOTE:Mani The updater is spawned as the last thing before the WinMain exit. - // see LLAppViewerWin32.cpp - -#elif LL_DARWIN - LLAppViewer::sUpdaterInfo->mUpdateExePath = "'"; - LLAppViewer::sUpdaterInfo->mUpdateExePath += gDirUtilp->getAppRODataDir(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "/mac-updater.app/Contents/MacOS/mac-updater' -url \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += update_url.asString(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -name \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += LLAppViewer::instance()->getSecondLifeTitle(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -bundleid \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += LL_VERSION_BUNDLE_ID; - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" &"; - - LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << LL_ENDL; - - // Run the auto-updater. - system(LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str()); /* Flawfinder: ignore */ - -#elif (LL_LINUX || LL_SOLARIS) && LL_GTK - // we tell the updater where to find the xml containing string - // translations which it can use for its own UI - std::string xml_strings_file = "strings.xml"; - std::vector xui_path_vec = LLUI::getXUIPaths(); - std::string xml_search_paths; - std::vector::const_iterator iter; - // build comma-delimited list of xml paths to pass to updater - for (iter = xui_path_vec.begin(); iter != xui_path_vec.end(); ) - { - std::string this_skin_dir = gDirUtilp->getDefaultSkinDir() - + gDirUtilp->getDirDelimiter() - + (*iter); - llinfos << "Got a XUI path: " << this_skin_dir << llendl; - xml_search_paths.append(this_skin_dir); - ++iter; - if (iter != xui_path_vec.end()) - xml_search_paths.append(","); // comma-delimit - } - // build the overall command-line to run the updater correctly - LLAppViewer::sUpdaterInfo->mUpdateExePath = - gDirUtilp->getExecutableDir() + "/" + "linux-updater.bin" + - " --url \"" + update_url.asString() + "\"" + - " --name \"" + LLAppViewer::instance()->getSecondLifeTitle() + "\"" + - " --dest \"" + gDirUtilp->getAppRODataDir() + "\"" + - " --stringsdir \"" + xml_search_paths + "\"" + - " --stringsfile \"" + xml_strings_file + "\""; - - LL_INFOS("AppInit") << "Calling updater: " - << LLAppViewer::sUpdaterInfo->mUpdateExePath << LL_ENDL; - - // *TODO: we could use the gdk equivalent to ensure the updater - // gets started on the same screen. - GError *error = NULL; - if (!g_spawn_command_line_async(LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), &error)) - { - llerrs << "Failed to launch updater: " - << error->message - << llendl; - } - if (error) { - g_error_free(error); - } -#else - OSMessageBox(LLTrans::getString("MBNoAutoUpdate"), LLStringUtil::null, OSMB_OK); -#endif - - // *REMOVE:Mani - Saving for reference... - // LLAppViewer::instance()->forceQuit(); -} - - -//virtual -void LLAppViewer::setMasterSystemAudioMute(bool mute) -{ - gSavedSettings.setBOOL("MuteAudio", mute); -} - -//virtual -bool LLAppViewer::getMasterSystemAudioMute() -{ - return gSavedSettings.getBOOL("MuteAudio"); -} - -//---------------------------------------------------------------------------- -// Metrics-related methods (static and otherwise) -//---------------------------------------------------------------------------- - -/** - * LLViewerAssetStats collects data on a per-region (as defined by the agent's - * location) so we need to tell it about region changes which become a kind of - * hidden variable/global state in the collectors. For collectors not running - * on the main thread, we need to send a message to move the data over safely - * and cheaply (amortized over a run). - */ -void LLAppViewer::metricsUpdateRegion(U64 region_handle) -{ - if (0 != region_handle) - { - LLViewerAssetStatsFF::set_region_main(region_handle); - if (LLAppViewer::sTextureFetch) - { - // Send a region update message into 'thread1' to get the new region. - LLAppViewer::sTextureFetch->commandSetRegion(region_handle); - } - else - { - // No 'thread1', a.k.a. TextureFetch, so update directly - LLViewerAssetStatsFF::set_region_thread1(region_handle); - } - } -} - - -/** - * Attempts to start a multi-threaded metrics report to be sent back to - * the grid for consumption. - */ -void LLAppViewer::metricsSend(bool enable_reporting) -{ - if (! gViewerAssetStatsMain) - return; - - if (LLAppViewer::sTextureFetch) - { - LLViewerRegion * regionp = gAgent.getRegion(); - - if (enable_reporting && regionp) - { - std::string caps_url = regionp->getCapability("ViewerMetrics"); - - // Make a copy of the main stats to send into another thread. - // Receiving thread takes ownership. - LLViewerAssetStats * main_stats(new LLViewerAssetStats(*gViewerAssetStatsMain)); - - // Send a report request into 'thread1' to get the rest of the data - // and provide some additional parameters while here. - LLAppViewer::sTextureFetch->commandSendMetrics(caps_url, - gAgentSessionID, - gAgentID, - main_stats); - main_stats = 0; // Ownership transferred - } - else - { - LLAppViewer::sTextureFetch->commandDataBreak(); - } - } - - // Reset even if we can't report. Rather than gather up a huge chunk of - // data, we'll keep to our sampling interval and retain the data - // resolution in time. - gViewerAssetStatsMain->reset(); -} - + /** + * @file llappviewer.cpp + * @brief The LLAppViewer class definitions + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llappviewer.h" + +// Viewer includes +#include "llversioninfo.h" +#include "llversionviewer.h" +#include "llfeaturemanager.h" +#include "lluictrlfactory.h" +#include "lltexteditor.h" +#include "llerrorcontrol.h" +#include "lleventtimer.h" +#include "llviewertexturelist.h" +#include "llgroupmgr.h" +#include "llagent.h" +#include "llagentcamera.h" +#include "llagentlanguage.h" +#include "llagentwearables.h" +#include "llwindow.h" +#include "llviewerstats.h" +#include "llviewerstatsrecorder.h" +#include "llmd5.h" +#include "llpumpio.h" +#include "llmimetypes.h" +#include "llslurl.h" +#include "llstartup.h" +#include "llfocusmgr.h" +#include "llviewerjoystick.h" +#include "llallocator.h" +#include "llares.h" +#include "llcurl.h" +#include "lltexturestats.h" +#include "lltexturestats.h" +#include "llviewerwindow.h" +#include "llviewerdisplay.h" +#include "llviewermedia.h" +#include "llviewerparcelmedia.h" +#include "llviewermediafocus.h" +#include "llviewermessage.h" +#include "llviewerobjectlist.h" +#include "llworldmap.h" +#include "llmutelist.h" +#include "llviewerhelp.h" +#include "lluicolortable.h" +#include "llurldispatcher.h" +#include "llurlhistory.h" +//#include "llfirstuse.h" +#include "llrender.h" +#include "llteleporthistory.h" +#include "lllocationhistory.h" +#include "llfasttimerview.h" +#include "llvoicechannel.h" +#include "llvoavatarself.h" +#include "llsidetray.h" +#include "llfeaturemanager.h" +#include "llurlmatch.h" +#include "lltextutil.h" +#include "lllogininstance.h" +#include "llprogressview.h" + +#include "llweb.h" +#include "llsecondlifeurls.h" +#include "llupdaterservice.h" + +// Linden library includes +#include "llavatarnamecache.h" +#include "llimagej2c.h" +#include "llmemory.h" +#include "llprimitive.h" +#include "llurlaction.h" +#include "llurlentry.h" +#include "llvfile.h" +#include "llvfsthread.h" +#include "llvolumemgr.h" +#include "llxfermanager.h" + +#include "llnotificationmanager.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" + +// Third party library includes +#include + + +#if LL_WINDOWS +# include // For _SH_DENYWR in initMarkerFile +#else +# include // For initMarkerFile support +#endif + +#include "llapr.h" +#include "apr_dso.h" +#include + +#include "llviewerkeyboard.h" +#include "lllfsthread.h" +#include "llworkerthread.h" +#include "lltexturecache.h" +#include "lltexturefetch.h" +#include "llimageworker.h" +#include "llevents.h" + +// The files below handle dependencies from cleanup. +#include "llkeyframemotion.h" +#include "llworldmap.h" +#include "llhudmanager.h" +#include "lltoolmgr.h" +#include "llassetstorage.h" +#include "llpolymesh.h" +#include "llcachename.h" +#include "llaudioengine.h" +#include "llstreamingaudio.h" +#include "llviewermenu.h" +#include "llselectmgr.h" +#include "lltrans.h" +#include "lltransutil.h" +#include "lltracker.h" +#include "llviewerparcelmgr.h" +#include "llworldmapview.h" +#include "llpostprocess.h" +#include "llwlparammanager.h" +#include "llwaterparammanager.h" + +#include "lldebugview.h" +#include "llconsole.h" +#include "llcontainerview.h" +#include "lltooltip.h" + +#include "llsdserialize.h" + +#include "llworld.h" +#include "llhudeffecttrail.h" +#include "llvectorperfoptions.h" +#include "llslurl.h" +#include "llwatchdog.h" + +// Included so that constants/settings might be initialized +// in save_settings_to_globals() +#include "llbutton.h" +#include "llstatusbar.h" +#include "llsurface.h" +#include "llvosky.h" +#include "llvotree.h" +#include "llvoavatar.h" +#include "llfolderview.h" +#include "llagentpilot.h" +#include "llvovolume.h" +#include "llflexibleobject.h" +#include "llvosurfacepatch.h" +#include "llviewerfloaterreg.h" +#include "llcommandlineparser.h" +#include "llfloatermemleak.h" +#include "llfloaterreg.h" +#include "llfloatersnapshot.h" +#include "llfloaterinventory.h" + +// includes for idle() idleShutdown() +#include "llviewercontrol.h" +#include "lleventnotifier.h" +#include "llcallbacklist.h" +#include "pipeline.h" +#include "llgesturemgr.h" +#include "llsky.h" +#include "llvlmanager.h" +#include "llviewercamera.h" +#include "lldrawpoolbump.h" +#include "llvieweraudio.h" +#include "llimview.h" +#include "llviewerthrottle.h" +#include "llparcel.h" +#include "llavatariconctrl.h" +#include "llgroupiconctrl.h" +#include "llviewerassetstats.h" + +// Include for security api initialization +#include "llsecapi.h" +#include "llmachineid.h" + +#include "llmainlooprepeater.h" + +// *FIX: These extern globals should be cleaned up. +// The globals either represent state/config/resource-storage of either +// this app, or another 'component' of the viewer. App globals should be +// moved into the app class, where as the other globals should be +// moved out of here. +// If a global symbol reference seems valid, it will be included +// via header files above. + +//---------------------------------------------------------------------------- +// llviewernetwork.h +#include "llviewernetwork.h" +// define a self-registering event API object +#include "llappviewerlistener.h" + +#if (LL_LINUX || LL_SOLARIS) && LL_GTK +#include "glib.h" +#endif // (LL_LINUX || LL_SOLARIS) && LL_GTK + +#if LL_MSVC +// disable boost::lexical_cast warning +#pragma warning (disable:4702) +#endif + +static LLAppViewerListener sAppViewerListener(LLAppViewer::instance); + +////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor +// +//---------------------------------------------------------------------------- +// viewer.cpp - these are only used in viewer, should be easily moved. + +#if LL_DARWIN +extern void init_apple_menu(const char* product); +#endif // LL_DARWIN + +extern BOOL gRandomizeFramerate; +extern BOOL gPeriodicSlowFrame; +extern BOOL gDebugGL; + +//////////////////////////////////////////////////////////// +// All from the last globals push... +const F32 DEFAULT_AFK_TIMEOUT = 5.f * 60.f; // time with no input before user flagged as Away From Keyboard + +F32 gSimLastTime; // Used in LLAppViewer::init and send_stats() +F32 gSimFrames; + +BOOL gShowObjectUpdates = FALSE; +BOOL gUseQuickTime = TRUE; + +eLastExecEvent gLastExecEvent = LAST_EXEC_NORMAL; + +LLSD gDebugInfo; + +U32 gFrameCount = 0; +U32 gForegroundFrameCount = 0; // number of frames that app window was in foreground +LLPumpIO* gServicePump = NULL; + +U64 gFrameTime = 0; +F32 gFrameTimeSeconds = 0.f; +F32 gFrameIntervalSeconds = 0.f; +F32 gFPSClamped = 10.f; // Pretend we start at target rate. +F32 gFrameDTClamped = 0.f; // Time between adjacent checks to network for packets +U64 gStartTime = 0; // gStartTime is "private", used only to calculate gFrameTimeSeconds +U32 gFrameStalls = 0; +const F64 FRAME_STALL_THRESHOLD = 1.0; + +LLTimer gRenderStartTime; +LLFrameTimer gForegroundTime; +LLFrameTimer gLoggedInTime; +LLTimer gLogoutTimer; +static const F32 LOGOUT_REQUEST_TIME = 6.f; // this will be cut short by the LogoutReply msg. +F32 gLogoutMaxTime = LOGOUT_REQUEST_TIME; + +BOOL gDisconnected = FALSE; + +// used to restore texture state after a mode switch +LLFrameTimer gRestoreGLTimer; +BOOL gRestoreGL = FALSE; +BOOL gUseWireframe = FALSE; + +// VFS globals - see llappviewer.h +LLVFS* gStaticVFS = NULL; + +LLMemoryInfo gSysMemory; +U64 gMemoryAllocated = 0; // updated in display_stats() in llviewerdisplay.cpp + +std::string gLastVersionChannel; + +LLVector3 gWindVec(3.0, 3.0, 0.0); +LLVector3 gRelativeWindVec(0.0, 0.0, 0.0); + +U32 gPacketsIn = 0; + +BOOL gPrintMessagesThisFrame = FALSE; + +BOOL gRandomizeFramerate = FALSE; +BOOL gPeriodicSlowFrame = FALSE; + +BOOL gCrashOnStartup = FALSE; +BOOL gLLErrorActivated = FALSE; +BOOL gLogoutInProgress = FALSE; + +//////////////////////////////////////////////////////////// +// Internal globals... that should be removed. +static std::string gArgs; + +const std::string MARKER_FILE_NAME("SecondLife.exec_marker"); +const std::string ERROR_MARKER_FILE_NAME("SecondLife.error_marker"); +const std::string LLERROR_MARKER_FILE_NAME("SecondLife.llerror_marker"); +const std::string LOGOUT_MARKER_FILE_NAME("SecondLife.logout_marker"); +static BOOL gDoDisconnect = FALSE; +static std::string gLaunchFileOnQuit; + +// Used on Win32 for other apps to identify our window (eg, win_setup) +const char* const VIEWER_WINDOW_CLASSNAME = "Second Life"; + +//---------------------------------------------------------------------------- + +// List of entries from strings.xml to always replace +static std::set default_trans_args; +void init_default_trans_args() +{ + default_trans_args.insert("SECOND_LIFE"); // World + default_trans_args.insert("APP_NAME"); + default_trans_args.insert("CAPITALIZED_APP_NAME"); + default_trans_args.insert("SECOND_LIFE_GRID"); + default_trans_args.insert("SUPPORT_SITE"); +} + +//---------------------------------------------------------------------------- +// File scope definitons +const char *VFS_DATA_FILE_BASE = "data.db2.x."; +const char *VFS_INDEX_FILE_BASE = "index.db2.x."; + + +struct SettingsFile : public LLInitParam::Block +{ + Mandatory name; + Optional file_name; + Optional required, + persistent; + Optional file_name_setting; + + SettingsFile() + : name("name"), + file_name("file_name"), + required("required", false), + persistent("persistent", true), + file_name_setting("file_name_setting") + {} +}; + +struct SettingsGroup : public LLInitParam::Block +{ + Mandatory name; + Mandatory path_index; + Multiple files; + + SettingsGroup() + : name("name"), + path_index("path_index"), + files("file") + {} +}; + +struct SettingsFiles : public LLInitParam::Block +{ + Multiple groups; + + SettingsFiles() + : groups("group") + {} +}; + +static std::string gWindowTitle; + +LLAppViewer::LLUpdaterInfo *LLAppViewer::sUpdaterInfo = NULL ; + +//---------------------------------------------------------------------------- +// Metrics logging control constants +//---------------------------------------------------------------------------- +static const F32 METRICS_INTERVAL_DEFAULT = 600.0; +static const F32 METRICS_INTERVAL_QA = 30.0; +static F32 app_metrics_interval = METRICS_INTERVAL_DEFAULT; +static bool app_metrics_qa_mode = false; + +void idle_afk_check() +{ + // check idle timers + if (gSavedSettings.getS32("AFKTimeout") && (gAwayTriggerTimer.getElapsedTimeF32() > gSavedSettings.getS32("AFKTimeout"))) + { + gAgent.setAFK(); + } +} + +// A callback set in LLAppViewer::init() +static void ui_audio_callback(const LLUUID& uuid) +{ + if (gAudiop) + { + gAudiop->triggerSound(uuid, gAgent.getID(), 1.0f, LLAudioEngine::AUDIO_TYPE_UI); + } +} + +bool create_text_segment_icon_from_url_match(LLUrlMatch* match,LLTextBase* base) +{ + if(!match || !base || base->getPlainText()) + return false; + + LLUUID match_id = match->getID(); + + LLIconCtrl* icon; + + if(gAgent.isInGroup(match_id, TRUE)) + { + LLGroupIconCtrl::Params icon_params; + icon_params.group_id = match_id; + icon_params.rect = LLRect(0, 16, 16, 0); + icon_params.visible = true; + icon = LLUICtrlFactory::instance().create(icon_params); + } + else + { + LLAvatarIconCtrl::Params icon_params; + icon_params.avatar_id = match_id; + icon_params.rect = LLRect(0, 16, 16, 0); + icon_params.visible = true; + icon = LLUICtrlFactory::instance().create(icon_params); + } + + LLInlineViewSegment::Params params; + params.force_newline = false; + params.view = icon; + params.left_pad = 4; + params.right_pad = 4; + params.top_pad = -2; + params.bottom_pad = 2; + + base->appendWidget(params," ",false); + + return true; +} + +void request_initial_instant_messages() +{ + static BOOL requested = FALSE; + if (!requested + && gMessageSystem + && LLMuteList::getInstance()->isLoaded() + && isAgentAvatarValid()) + { + // Auto-accepted inventory items may require the avatar object + // to build a correct name. Likewise, inventory offers from + // muted avatars require the mute list to properly mute. + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_RetrieveInstantMessages); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gAgent.sendReliableMessage(); + requested = TRUE; + } +} + +// A settings system callback for CrashSubmitBehavior +bool handleCrashSubmitBehaviorChanged(const LLSD& newvalue) +{ + S32 cb = newvalue.asInteger(); + const S32 NEVER_SUBMIT_REPORT = 2; + if(cb == NEVER_SUBMIT_REPORT) + { + LLAppViewer::instance()->destroyMainloopTimeout(); + } + return true; +} + +// Use these strictly for things that are constructed at startup, +// or for things that are performance critical. JC +static void settings_to_globals() +{ + LLBUTTON_H_PAD = gSavedSettings.getS32("ButtonHPad"); + BTN_HEIGHT_SMALL = gSavedSettings.getS32("ButtonHeightSmall"); + BTN_HEIGHT = gSavedSettings.getS32("ButtonHeight"); + + MENU_BAR_HEIGHT = gSavedSettings.getS32("MenuBarHeight"); + MENU_BAR_WIDTH = gSavedSettings.getS32("MenuBarWidth"); + + LLSurface::setTextureSize(gSavedSettings.getU32("RegionTextureSize")); + + LLImageGL::sGlobalUseAnisotropic = gSavedSettings.getBOOL("RenderAnisotropic"); + LLVOVolume::sLODFactor = gSavedSettings.getF32("RenderVolumeLODFactor"); + LLVOVolume::sDistanceFactor = 1.f-LLVOVolume::sLODFactor * 0.1f; + LLVolumeImplFlexible::sUpdateFactor = gSavedSettings.getF32("RenderFlexTimeFactor"); + LLVOTree::sTreeFactor = gSavedSettings.getF32("RenderTreeLODFactor"); + LLVOAvatar::sLODFactor = gSavedSettings.getF32("RenderAvatarLODFactor"); + LLVOAvatar::sMaxVisible = (U32)gSavedSettings.getS32("RenderAvatarMaxVisible"); + LLVOAvatar::sVisibleInFirstPerson = gSavedSettings.getBOOL("FirstPersonAvatarVisible"); + // clamp auto-open time to some minimum usable value + LLFolderView::sAutoOpenTime = llmax(0.25f, gSavedSettings.getF32("FolderAutoOpenDelay")); + LLSelectMgr::sRectSelectInclusive = gSavedSettings.getBOOL("RectangleSelectInclusive"); + LLSelectMgr::sRenderHiddenSelections = gSavedSettings.getBOOL("RenderHiddenSelections"); + LLSelectMgr::sRenderLightRadius = gSavedSettings.getBOOL("RenderLightRadius"); + + gAgentPilot.mNumRuns = gSavedSettings.getS32("StatsNumRuns"); + gAgentPilot.mQuitAfterRuns = gSavedSettings.getBOOL("StatsQuitAfterRuns"); + gAgent.setHideGroupTitle(gSavedSettings.getBOOL("RenderHideGroupTitle")); + + gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc"); + gShowObjectUpdates = gSavedSettings.getBOOL("ShowObjectUpdates"); + LLWorldMapView::sMapScale = gSavedSettings.getF32("MapScale"); +} + +static void settings_modify() +{ + LLRenderTarget::sUseFBO = gSavedSettings.getBOOL("RenderUseFBO"); + LLVOAvatar::sUseImpostors = gSavedSettings.getBOOL("RenderUseImpostors"); + LLVOSurfacePatch::sLODFactor = gSavedSettings.getF32("RenderTerrainLODFactor"); + LLVOSurfacePatch::sLODFactor *= LLVOSurfacePatch::sLODFactor; //square lod factor to get exponential range of [1,4] + gDebugGL = gSavedSettings.getBOOL("RenderDebugGL") || gDebugSession; + gDebugPipeline = gSavedSettings.getBOOL("RenderDebugPipeline"); + gAuditTexture = gSavedSettings.getBOOL("AuditTexture"); +#if LL_VECTORIZE + if (gSysCPU.hasAltivec()) + { + gSavedSettings.setBOOL("VectorizeEnable", TRUE ); + gSavedSettings.setU32("VectorizeProcessor", 0 ); + } + else + if (gSysCPU.hasSSE2()) + { + gSavedSettings.setBOOL("VectorizeEnable", TRUE ); + gSavedSettings.setU32("VectorizeProcessor", 2 ); + } + else + if (gSysCPU.hasSSE()) + { + gSavedSettings.setBOOL("VectorizeEnable", TRUE ); + gSavedSettings.setU32("VectorizeProcessor", 1 ); + } + else + { + // Don't bother testing or running if CPU doesn't support it. JC + gSavedSettings.setBOOL("VectorizePerfTest", FALSE ); + gSavedSettings.setBOOL("VectorizeEnable", FALSE ); + gSavedSettings.setU32("VectorizeProcessor", 0 ); + gSavedSettings.setBOOL("VectorizeSkin", FALSE); + } +#else + // This build target doesn't support SSE, don't test/run. + gSavedSettings.setBOOL("VectorizePerfTest", FALSE ); + gSavedSettings.setBOOL("VectorizeEnable", FALSE ); + gSavedSettings.setU32("VectorizeProcessor", 0 ); + gSavedSettings.setBOOL("VectorizeSkin", FALSE); + + // disable fullscreen mode, unsupported + gSavedSettings.setBOOL("WindowFullScreen", FALSE); +#endif +} + +class LLFastTimerLogThread : public LLThread +{ +public: + std::string mFile; + + LLFastTimerLogThread(std::string& test_name) : LLThread("fast timer log") + { + std::string file_name = test_name + std::string(".slp"); + mFile = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, file_name); + } + + void run() + { + std::ofstream os(mFile.c_str()); + + while (!LLAppViewer::instance()->isQuitting()) + { + LLFastTimer::writeLog(os); + os.flush(); + ms_sleep(32); + } + + os.close(); + } + +}; + +//virtual +bool LLAppViewer::initSLURLHandler() +{ + // does nothing unless subclassed + return false; +} + +//virtual +bool LLAppViewer::sendURLToOtherInstance(const std::string& url) +{ + // does nothing unless subclassed + return false; +} + +//---------------------------------------------------------------------------- +// LLAppViewer definition + +// Static members. +// The single viewer app. +LLAppViewer* LLAppViewer::sInstance = NULL; + +const std::string LLAppViewer::sGlobalSettingsName = "Global"; + +LLTextureCache* LLAppViewer::sTextureCache = NULL; +LLImageDecodeThread* LLAppViewer::sImageDecodeThread = NULL; +LLTextureFetch* LLAppViewer::sTextureFetch = NULL; + +LLAppViewer::LLAppViewer() : + mMarkerFile(), + mLogoutMarkerFile(NULL), + mReportedCrash(false), + mNumSessions(0), + mPurgeCache(false), + mPurgeOnExit(false), + mSecondInstance(false), + mSavedFinalSnapshot(false), + mForceGraphicsDetail(false), + mQuitRequested(false), + mLogoutRequestSent(false), + mYieldTime(-1), + mMainloopTimeout(NULL), + mAgentRegionLastAlive(false), + mRandomizeFramerate(LLCachedControl(gSavedSettings,"Randomize Framerate", FALSE)), + mPeriodicSlowFrame(LLCachedControl(gSavedSettings,"Periodic Slow Frame", FALSE)), + mFastTimerLogThread(NULL), + mUpdater(new LLUpdaterService()), + mSettingsLocationList(NULL) +{ + if(NULL != sInstance) + { + llerrs << "Oh no! An instance of LLAppViewer already exists! LLAppViewer is sort of like a singleton." << llendl; + } + + setupErrorHandling(); + sInstance = this; + gLoggedInTime.stop(); + + LLLoginInstance::instance().setUpdaterService(mUpdater.get()); +} + +LLAppViewer::~LLAppViewer() +{ + delete mSettingsLocationList; + + LLLoginInstance::instance().setUpdaterService(0); + + destroyMainloopTimeout(); + + // If we got to this destructor somehow, the app didn't hang. + removeMarkerFile(); +} + +bool LLAppViewer::init() +{ + // + // Start of the application + // + // IMPORTANT! Do NOT put anything that will write + // into the log files during normal startup until AFTER + // we run the "program crashed last time" error handler below. + // + LLFastTimer::reset(); + + // Need to do this initialization before we do anything else, since anything + // that touches files should really go through the lldir API + gDirUtilp->initAppDirs("SecondLife"); + // set skin search path to default, will be overridden later + // this allows simple skinned file lookups to work + gDirUtilp->setSkinFolder("default"); + + initLogging(); + + // + // OK to write stuff to logs now, we've now crash reported if necessary + // + + init_default_trans_args(); + + if (!initConfiguration()) + return false; + + // write Google Breakpad minidump files to our log directory + std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); + logdir += gDirUtilp->getDirDelimiter(); + setMiniDumpDir(logdir); + + // Although initLogging() is the right place to mess with + // setFatalFunction(), we can't query gSavedSettings until after + // initConfiguration(). + S32 rc(gSavedSettings.getS32("QAModeTermCode")); + if (rc >= 0) + { + // QAModeTermCode set, terminate with that rc on LL_ERRS. Use _exit() + // rather than exit() because normal cleanup depends too much on + // successful startup! + LLError::setFatalFunction(boost::bind(_exit, rc)); + } + + mAlloc.setProfilingEnabled(gSavedSettings.getBOOL("MemProfiling")); + +#if LL_RECORD_VIEWER_STATS + LLViewerStatsRecorder::initClass(); +#endif + + // *NOTE:Mani - LLCurl::initClass is not thread safe. + // Called before threads are created. + LLCurl::initClass(); + LLMachineID::init(); + + { + // Viewer metrics initialization + static LLCachedControl metrics_submode(gSavedSettings, + "QAModeMetrics", + false, + "Enables QA features (logging, faster cycling) for metrics collector"); + + if (metrics_submode) + { + app_metrics_qa_mode = true; + app_metrics_interval = METRICS_INTERVAL_QA; + } + LLViewerAssetStatsFF::init(); + } + + initThreads(); + writeSystemInfo(); + + // Initialize updater service (now that we have an io pump) + initUpdater(); + if(isQuitting()) + { + // Early out here because updater set the quitting flag. + return true; + } + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + // *FIX: The following code isn't grouped into functions yet. + + // Statistics / debug timer initialization + init_statistics(); + + // + // Various introspection concerning the libs we're using - particularly + // the libs involved in getting to a full login screen. + // + LL_INFOS("InitInfo") << "J2C Engine is: " << LLImageJ2C::getEngineInfo() << LL_ENDL; + LL_INFOS("InitInfo") << "libcurl version is: " << LLCurl::getVersionString() << LL_ENDL; + + // Get the single value from the crash settings file, if it exists + std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); + gCrashSettings.loadFromFile(crash_settings_filename); + if(gSavedSettings.getBOOL("IgnoreAllNotifications")) + { + gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ALWAYS_SEND); + gCrashSettings.saveToFile(crash_settings_filename, FALSE); + } + + ///////////////////////////////////////////////// + // OS-specific login dialogs + ///////////////////////////////////////////////// + + //test_cached_control(); + + // track number of times that app has run + mNumSessions = gSavedSettings.getS32("NumSessions"); + mNumSessions++; + gSavedSettings.setS32("NumSessions", mNumSessions); + + if (gSavedSettings.getBOOL("VerboseLogs")) + { + LLError::setPrintLocation(true); + } + + // Widget construction depends on LLUI being initialized + LLUI::settings_map_t settings_map; + settings_map["config"] = &gSavedSettings; + settings_map["ignores"] = &gWarningSettings; + settings_map["floater"] = &gSavedSettings; // *TODO: New settings file + settings_map["account"] = &gSavedPerAccountSettings; + + LLUI::initClass(settings_map, + LLUIImageList::getInstance(), + ui_audio_callback, + &LLUI::sGLScaleFactor); + + // Setup paths and LLTrans after LLUI::initClass has been called + LLUI::setupPaths(); + LLTransUtil::parseStrings("strings.xml", default_trans_args); + LLTransUtil::parseLanguageStrings("language_settings.xml"); + + // LLKeyboard relies on LLUI to know what some accelerator keys are called. + LLKeyboard::setStringTranslatorFunc( LLTrans::getKeyboardString ); + + LLWeb::initClass(); // do this after LLUI + + // Provide the text fields with callbacks for opening Urls + LLUrlAction::setOpenURLCallback(&LLWeb::loadURL); + LLUrlAction::setOpenURLInternalCallback(&LLWeb::loadURLInternal); + LLUrlAction::setOpenURLExternalCallback(&LLWeb::loadURLExternal); + LLUrlAction::setExecuteSLURLCallback(&LLURLDispatcher::dispatchFromTextEditor); + + // Let code in llui access the viewer help floater + LLUI::sHelpImpl = LLViewerHelp::getInstance(); + + // Load translations for tooltips + LLFloater::initClass(); + + ///////////////////////////////////////////////// + + LLToolMgr::getInstance(); // Initialize tool manager if not already instantiated + + LLViewerFloaterReg::registerFloaters(); + + ///////////////////////////////////////////////// + // + // Load settings files + // + // + LLGroupMgr::parseRoleActions("role_actions.xml"); + + LLAgent::parseTeleportMessages("teleport_strings.xml"); + + LLViewerJointMesh::updateVectorize(); + + // load MIME type -> media impl mappings + std::string mime_types_name; +#if LL_DARWIN + mime_types_name = "mime_types_mac.xml"; +#elif LL_LINUX + mime_types_name = "mime_types_linux.xml"; +#else + mime_types_name = "mime_types.xml"; +#endif + LLMIMETypes::parseMIMETypes( mime_types_name ); + + // Copy settings to globals. *TODO: Remove or move to appropriage class initializers + settings_to_globals(); + // Setup settings listeners + settings_setup_listeners(); + // Modify settings based on system configuration and compile options + settings_modify(); + + // Find partition serial number (Windows) or hardware serial (Mac) + mSerialNumber = generateSerialNumber(); + + // do any necessary set-up for accepting incoming SLURLs from apps + initSLURLHandler(); + + if(false == initHardwareTest()) + { + // Early out from user choice. + return false; + } + + // Prepare for out-of-memory situations, during which we will crash on + // purpose and save a dump. +#if LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP + MemSetErrorHandler(first_mem_error_handler); +#endif // LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP + + // *Note: this is where gViewerStats used to be created. + + // + // Initialize the VFS, and gracefully handle initialization errors + // + + if (!initCache()) + { + std::ostringstream msg; + msg << LLTrans::getString("MBUnableToAccessFile"); + OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); + return 1; + } + + // Initialize the repeater service. + LLMainLoopRepeater::instance().start(); + + // + // Initialize the window + // + gGLActive = TRUE; + initWindow(); + + // initWindow also initializes the Feature List, so now we can initialize this global. + LLCubeMap::sUseCubeMaps = LLFeatureManager::getInstance()->isFeatureAvailable("RenderCubeMap"); + + // call all self-registered classes + LLInitClassList::instance().fireCallbacks(); + + LLFolderViewItem::initClass(); // SJB: Needs to happen after initWindow(), not sure why but related to fonts + + gGLManager.getGLInfo(gDebugInfo); + gGLManager.printGLInfoString(); + + // Load Default bindings + std::string key_bindings_file = gDirUtilp->findFile("keys.xml", + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + + + if (!gViewerKeyboard.loadBindingsXML(key_bindings_file)) + { + std::string key_bindings_file = gDirUtilp->findFile("keys.ini", + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + if (!gViewerKeyboard.loadBindings(key_bindings_file)) + { + LL_ERRS("InitInfo") << "Unable to open keys.ini" << LL_ENDL; + } + } + + // If we don't have the right GL requirements, exit. + if (!gGLManager.mHasRequirements && !gNoRender) + { + // can't use an alert here since we're exiting and + // all hell breaks lose. + OSMessageBox( + LLNotifications::instance().getGlobalString("UnsupportedGLRequirements"), + LLStringUtil::null, + OSMB_OK); + return 0; + } + + // alert the user if they are using unsupported hardware + if(!gSavedSettings.getBOOL("AlertedUnsupportedHardware")) + { + bool unsupported = false; + LLSD args; + std::string minSpecs; + + // get cpu data from xml + std::stringstream minCPUString(LLNotifications::instance().getGlobalString("UnsupportedCPUAmount")); + S32 minCPU = 0; + minCPUString >> minCPU; + + // get RAM data from XML + std::stringstream minRAMString(LLNotifications::instance().getGlobalString("UnsupportedRAMAmount")); + U64 minRAM = 0; + minRAMString >> minRAM; + minRAM = minRAM * 1024 * 1024; + + if(!LLFeatureManager::getInstance()->isGPUSupported() && LLFeatureManager::getInstance()->getGPUClass() != GPU_CLASS_UNKNOWN) + { + minSpecs += LLNotifications::instance().getGlobalString("UnsupportedGPU"); + minSpecs += "\n"; + unsupported = true; + } + if(gSysCPU.getMHz() < minCPU) + { + minSpecs += LLNotifications::instance().getGlobalString("UnsupportedCPU"); + minSpecs += "\n"; + unsupported = true; + } + if(gSysMemory.getPhysicalMemoryClamped() < minRAM) + { + minSpecs += LLNotifications::instance().getGlobalString("UnsupportedRAM"); + minSpecs += "\n"; + unsupported = true; + } + + if (LLFeatureManager::getInstance()->getGPUClass() == GPU_CLASS_UNKNOWN) + { + LLNotificationsUtil::add("UnknownGPU"); + } + + if(unsupported) + { + if(!gSavedSettings.controlExists("WarnUnsupportedHardware") + || gSavedSettings.getBOOL("WarnUnsupportedHardware")) + { + args["MINSPECS"] = minSpecs; + LLNotificationsUtil::add("UnsupportedHardware", args ); + } + + } + } + + + // save the graphics card + gDebugInfo["GraphicsCard"] = LLFeatureManager::getInstance()->getGPUString(); + + // Save the current version to the prefs file + gSavedSettings.setString("LastRunVersion", + LLVersionInfo::getChannelAndVersion()); + + gSimLastTime = gRenderStartTime.getElapsedTimeF32(); + gSimFrames = (F32)gFrameCount; + + LLViewerJoystick::getInstance()->init(false); + + try { + initializeSecHandler(); + } + catch (LLProtectedDataException ex) + { + LLNotificationsUtil::add("CorruptedProtectedDataStore"); + } + LLHTTPClient::setCertVerifyCallback(secapiSSLCertVerifyCallback); + + + gGLActive = FALSE; + if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0) + { + loadEventHostModule(gSavedSettings.getS32("QAModeEventHostPort")); + } + + LLViewerMedia::initClass(); + LLTextUtil::TextHelpers::iconCallbackCreationFunction = create_text_segment_icon_from_url_match; + + //EXT-7013 - On windows for some locale (Japanese) standard + //datetime formatting functions didn't support some parameters such as "weekday". + //Names for days and months localized in xml are also useful for Polish locale(STORM-107). + std::string language = LLControlGroup::getInstance(sGlobalSettingsName)->getString("Language"); + if(language == "ja" || language == "pl") + { + LLStringOps::setupWeekDaysNames(LLTrans::getString("dateTimeWeekdaysNames")); + LLStringOps::setupWeekDaysShortNames(LLTrans::getString("dateTimeWeekdaysShortNames")); + LLStringOps::setupMonthNames(LLTrans::getString("dateTimeMonthNames")); + LLStringOps::setupMonthShortNames(LLTrans::getString("dateTimeMonthShortNames")); + LLStringOps::setupDayFormat(LLTrans::getString("dateTimeDayFormat")); + + LLStringOps::sAM = LLTrans::getString("dateTimeAM"); + LLStringOps::sPM = LLTrans::getString("dateTimePM"); + } + + LLAgentLanguage::init(); + + + + return true; +} + +static LLFastTimer::DeclareTimer FTM_MESSAGES("System Messages"); +static LLFastTimer::DeclareTimer FTM_SLEEP("Sleep"); +static LLFastTimer::DeclareTimer FTM_TEXTURE_CACHE("Texture Cache"); +static LLFastTimer::DeclareTimer FTM_DECODE("Image Decode"); +static LLFastTimer::DeclareTimer FTM_VFS("VFS Thread"); +static LLFastTimer::DeclareTimer FTM_LFS("LFS Thread"); +static LLFastTimer::DeclareTimer FTM_PAUSE_THREADS("Pause Threads"); +static LLFastTimer::DeclareTimer FTM_IDLE("Idle"); +static LLFastTimer::DeclareTimer FTM_PUMP("Pump"); +static LLFastTimer::DeclareTimer FTM_PUMP_ARES("Ares"); +static LLFastTimer::DeclareTimer FTM_PUMP_SERVICE("Service"); +static LLFastTimer::DeclareTimer FTM_SERVICE_CALLBACK("Callback"); +static LLFastTimer::DeclareTimer FTM_AGENT_AUTOPILOT("Autopilot"); +static LLFastTimer::DeclareTimer FTM_AGENT_UPDATE("Update"); + +bool LLAppViewer::mainLoop() +{ + LLMemType mt1(LLMemType::MTYPE_MAIN); + mMainloopTimeout = new LLWatchdogTimeout(); + + //------------------------------------------- + // Run main loop until time to quit + //------------------------------------------- + + // Create IO Pump to use for HTTP Requests. + gServicePump = new LLPumpIO(gAPRPoolp); + LLHTTPClient::setPump(*gServicePump); + LLCurl::setCAFile(gDirUtilp->getCAFile()); + + // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated. + + LLVoiceChannel::initClass(); + LLVoiceClient::getInstance()->init(gServicePump); + LLTimer frameTimer,idleTimer; + LLTimer debugTime; + LLFrameTimer memCheckTimer; + LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); + joystick->setNeedsReset(true); + + LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); + // As we do not (yet) send data on the mainloop LLEventPump that varies + // with each frame, no need to instantiate a new LLSD event object each + // time. Obviously, if that changes, just instantiate the LLSD at the + // point of posting. + LLSD newFrame; + + const F32 memory_check_interval = 1.0f ; //second + + // Handle messages + while (!LLApp::isExiting()) + { + LLFastTimer::nextFrame(); // Should be outside of any timer instances + + //clear call stack records + llclearcallstacks; + + //check memory availability information + { + if(memory_check_interval < memCheckTimer.getElapsedTimeF32()) + { + memCheckTimer.reset() ; + + //update the availability of memory + LLMemoryInfo::getAvailableMemoryKB(mAvailPhysicalMemInKB, mAvailVirtualMemInKB) ; + } + llcallstacks << "Available physical mem(KB): " << mAvailPhysicalMemInKB << llcallstacksendl ; + llcallstacks << "Available virtual mem(KB): " << mAvailVirtualMemInKB << llcallstacksendl ; + } + + try + { + pingMainloopTimeout("Main:MiscNativeWindowEvents"); + + if (gViewerWindow) + { + LLFastTimer t2(FTM_MESSAGES); + gViewerWindow->mWindow->processMiscNativeEvents(); + } + + pingMainloopTimeout("Main:GatherInput"); + + if (gViewerWindow) + { + LLFastTimer t2(FTM_MESSAGES); + if (!restoreErrorTrap()) + { + llwarns << " Someone took over my signal/exception handler (post messagehandling)!" << llendl; + } + + gViewerWindow->mWindow->gatherInput(); + } + +#if 1 && !LL_RELEASE_FOR_DOWNLOAD + // once per second debug info + if (debugTime.getElapsedTimeF32() > 1.f) + { + debugTime.reset(); + } + +#endif + //memory leaking simulation + LLFloaterMemLeak* mem_leak_instance = + LLFloaterReg::findTypedInstance("mem_leaking"); + if(mem_leak_instance) + { + mem_leak_instance->idle() ; + } + + // canonical per-frame event + mainloop.post(newFrame); + + if (!LLApp::isExiting()) + { + pingMainloopTimeout("Main:JoystickKeyboard"); + + // Scan keyboard for movement keys. Command keys and typing + // are handled by windows callbacks. Don't do this until we're + // done initializing. JC + if (gViewerWindow->mWindow->getVisible() + && gViewerWindow->getActive() + && !gViewerWindow->mWindow->getMinimized() + && LLStartUp::getStartupState() == STATE_STARTED + && !gViewerWindow->getShowProgress() + && !gFocusMgr.focusLocked()) + { + LLMemType mjk(LLMemType::MTYPE_JOY_KEY); + joystick->scanJoystick(); + gKeyboard->scanKeyboard(); + } + + // Update state based on messages, user input, object idle. + { + pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds! + + LLFastTimer t3(FTM_IDLE); + idle(); + + if (gAres != NULL && gAres->isInitialized()) + { + LLMemType mt_ip(LLMemType::MTYPE_IDLE_PUMP); + pingMainloopTimeout("Main:ServicePump"); + LLFastTimer t4(FTM_PUMP); + { + LLFastTimer t(FTM_PUMP_ARES); + gAres->process(); + } + { + LLFastTimer t(FTM_PUMP_SERVICE); + // this pump is necessary to make the login screen show up + gServicePump->pump(); + + { + LLFastTimer t(FTM_SERVICE_CALLBACK); + gServicePump->callback(); + } + } + } + + resumeMainloopTimeout(); + } + + if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED)) + { + pauseMainloopTimeout(); + saveFinalSnapshot(); + disconnectViewer(); + resumeMainloopTimeout(); + } + + // Render scene. + if (!LLApp::isExiting()) + { + pingMainloopTimeout("Main:Display"); + gGLActive = TRUE; + display(); + pingMainloopTimeout("Main:Snapshot"); + LLFloaterSnapshot::update(); // take snapshots + gGLActive = FALSE; + } + + } + + pingMainloopTimeout("Main:Sleep"); + + pauseMainloopTimeout(); + + // Sleep and run background threads + { + LLMemType mt_sleep(LLMemType::MTYPE_SLEEP); + LLFastTimer t2(FTM_SLEEP); + + // yield some time to the os based on command line option + if(mYieldTime >= 0) + { + ms_sleep(mYieldTime); + } + + // yield cooperatively when not running as foreground window + if ( gNoRender + || (gViewerWindow && !gViewerWindow->mWindow->getVisible()) + || !gFocusMgr.getAppHasFocus()) + { + // Sleep if we're not rendering, or the window is minimized. + S32 milliseconds_to_sleep = llclamp(gSavedSettings.getS32("BackgroundYieldTime"), 0, 1000); + // don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads + // of equal priority on Windows + if (milliseconds_to_sleep > 0) + { + ms_sleep(milliseconds_to_sleep); + // also pause worker threads during this wait period + LLAppViewer::getTextureCache()->pause(); + LLAppViewer::getImageDecodeThread()->pause(); + } + } + + if (mRandomizeFramerate) + { + ms_sleep(rand() % 200); + } + + if (mPeriodicSlowFrame + && (gFrameCount % 10 == 0)) + { + llinfos << "Periodic slow frame - sleeping 500 ms" << llendl; + ms_sleep(500); + } + + static const F64 FRAME_SLOW_THRESHOLD = 0.5; //2 frames per seconds + const F64 max_idle_time = llmin(.005*10.0*gFrameTimeSeconds, 0.005); // 5 ms a second + idleTimer.reset(); + bool is_slow = (frameTimer.getElapsedTimeF64() > FRAME_SLOW_THRESHOLD) ; + S32 total_work_pending = 0; + S32 total_io_pending = 0; + while(!is_slow)//do not unpause threads if the frame rates are very low. + { + S32 work_pending = 0; + S32 io_pending = 0; + { + LLFastTimer ftm(FTM_TEXTURE_CACHE); + work_pending += LLAppViewer::getTextureCache()->update(1); // unpauses the texture cache thread + } + { + LLFastTimer ftm(FTM_DECODE); + work_pending += LLAppViewer::getImageDecodeThread()->update(1); // unpauses the image thread + } + { + LLFastTimer ftm(FTM_DECODE); + work_pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread + } + + { + LLFastTimer ftm(FTM_VFS); + io_pending += LLVFSThread::updateClass(1); + } + { + LLFastTimer ftm(FTM_LFS); + io_pending += LLLFSThread::updateClass(1); + } + + if (io_pending > 1000) + { + ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up + } + + total_work_pending += work_pending ; + total_io_pending += io_pending ; + + if (!work_pending || idleTimer.getElapsedTimeF64() >= max_idle_time) + { + break; + } + } + + if(!total_work_pending) //pause texture fetching threads if nothing to process. + { + LLAppViewer::getTextureCache()->pause(); + LLAppViewer::getImageDecodeThread()->pause(); + LLAppViewer::getTextureFetch()->pause(); + } + if(!total_io_pending) //pause file threads if nothing to process. + { + LLVFSThread::sLocal->pause(); + LLLFSThread::sLocal->pause(); + } + + if ((LLStartUp::getStartupState() >= STATE_CLEANUP) && + (frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD)) + { + gFrameStalls++; + } + frameTimer.reset(); + + resumeMainloopTimeout(); + + pingMainloopTimeout("Main:End"); + } + } + catch(std::bad_alloc) + { + { + llinfos << "Availabe physical memory(KB) at the beginning of the frame: " << mAvailPhysicalMemInKB << llendl ; + llinfos << "Availabe virtual memory(KB) at the beginning of the frame: " << mAvailVirtualMemInKB << llendl ; + + LLMemoryInfo::getAvailableMemoryKB(mAvailPhysicalMemInKB, mAvailVirtualMemInKB) ; + + llinfos << "Current availabe physical memory(KB): " << mAvailPhysicalMemInKB << llendl ; + llinfos << "Current availabe virtual memory(KB): " << mAvailVirtualMemInKB << llendl ; + } + + //stop memory leaking simulation + LLFloaterMemLeak* mem_leak_instance = + LLFloaterReg::findTypedInstance("mem_leaking"); + if(mem_leak_instance) + { + mem_leak_instance->stop() ; + llwarns << "Bad memory allocation in LLAppViewer::mainLoop()!" << llendl ; + } + else + { + //output possible call stacks to log file. + LLError::LLCallStacks::print() ; + + llerrs << "Bad memory allocation in LLAppViewer::mainLoop()!" << llendl ; + } + } + } + + // Save snapshot for next time, if we made it through initialization + if (STATE_STARTED == LLStartUp::getStartupState()) + { + try + { + saveFinalSnapshot(); + } + catch(std::bad_alloc) + { + llwarns << "Bad memory allocation when saveFinalSnapshot() is called!" << llendl ; + + //stop memory leaking simulation + LLFloaterMemLeak* mem_leak_instance = + LLFloaterReg::findTypedInstance("mem_leaking"); + if(mem_leak_instance) + { + mem_leak_instance->stop() ; + } + } + } + + delete gServicePump; + + destroyMainloopTimeout(); + + llinfos << "Exiting main_loop" << llendflush; + + return true; +} + +void LLAppViewer::flushVFSIO() +{ + while (1) + { + S32 pending = LLVFSThread::updateClass(0); + pending += LLLFSThread::updateClass(0); + if (!pending) + { + break; + } + llinfos << "Waiting for pending IO to finish: " << pending << llendflush; + ms_sleep(100); + } +} + +bool LLAppViewer::cleanup() +{ + // workaround for DEV-35406 crash on shutdown + LLEventPumps::instance().reset(); + + // remove any old breakpad minidump files from the log directory + if (! isError()) + { + std::string logdir = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); + logdir += gDirUtilp->getDirDelimiter(); + gDirUtilp->deleteFilesInDir(logdir, "*-*-*-*-*.dmp"); + } + + // *TODO - generalize this and move DSO wrangling to a helper class -brad + std::set::const_iterator i; + for(i = mPlugins.begin(); i != mPlugins.end(); ++i) + { + int (*ll_plugin_stop_func)(void) = NULL; + apr_status_t rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll_plugin_stop_func, *i, "ll_plugin_stop"); + ll_plugin_stop_func(); + + rv = apr_dso_unload(*i); + } + mPlugins.clear(); + + //flag all elements as needing to be destroyed immediately + // to ensure shutdown order + LLMortician::setZealous(TRUE); + + LLVoiceClient::getInstance()->terminate(); + + disconnectViewer(); + + llinfos << "Viewer disconnected" << llendflush; + + display_cleanup(); + + release_start_screen(); // just in case + + LLError::logToFixedBuffer(NULL); + + llinfos << "Cleaning Up" << llendflush; + + // Must clean up texture references before viewer window is destroyed. + if(LLHUDManager::instanceExists()) + { + LLHUDManager::getInstance()->updateEffects(); + LLHUDObject::updateAll(); + LLHUDManager::getInstance()->cleanupEffects(); + LLHUDObject::cleanupHUDObjects(); + llinfos << "HUD Objects cleaned up" << llendflush; + } + + LLKeyframeDataCache::clear(); + + // End TransferManager before deleting systems it depends on (Audio, VFS, AssetStorage) +#if 0 // this seems to get us stuck in an infinite loop... + gTransferManager.cleanup(); +#endif + + // Note: this is where gWorldMap used to be deleted. + + // Note: this is where gHUDManager used to be deleted. + if(LLHUDManager::instanceExists()) + { + LLHUDManager::getInstance()->shutdownClass(); + } + + delete gAssetStorage; + gAssetStorage = NULL; + + LLPolyMesh::freeAllMeshes(); + + LLStartUp::cleanupNameCache(); + + // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be deleted. + + LLWorldMap::getInstance()->reset(); // release any images + + llinfos << "Global stuff deleted" << llendflush; + + if (gAudiop) + { + // shut down the streaming audio sub-subsystem first, in case it relies on not outliving the general audio subsystem. + + LLStreamingAudioInterface *sai = gAudiop->getStreamingAudioImpl(); + delete sai; + gAudiop->setStreamingAudioImpl(NULL); + + // shut down the audio subsystem + + bool want_longname = false; + if (gAudiop->getDriverName(want_longname) == "FMOD") + { + // This hack exists because fmod likes to occasionally + // crash or hang forever when shutting down, for no + // apparent reason. + llwarns << "Hack, skipping FMOD audio engine cleanup" << llendflush; + } + else + { + gAudiop->shutdown(); + } + + delete gAudiop; + gAudiop = NULL; + } + + // Note: this is where LLFeatureManager::getInstance()-> used to be deleted. + + // Patch up settings for next time + // Must do this before we delete the viewer window, + // such that we can suck rectangle information out of + // it. + cleanupSavedSettings(); + llinfos << "Settings patched up" << llendflush; + + // delete some of the files left around in the cache. + removeCacheFiles("*.wav"); + removeCacheFiles("*.tmp"); + removeCacheFiles("*.lso"); + removeCacheFiles("*.out"); + removeCacheFiles("*.dsf"); + removeCacheFiles("*.bodypart"); + removeCacheFiles("*.clothing"); + + llinfos << "Cache files removed" << llendflush; + + // Wait for any pending VFS IO + flushVFSIO(); + llinfos << "Shutting down Views" << llendflush; + + // Destroy the UI + if( gViewerWindow) + gViewerWindow->shutdownViews(); + + llinfos << "Cleaning up Inventory" << llendflush; + + // Cleanup Inventory after the UI since it will delete any remaining observers + // (Deleted observers should have already removed themselves) + gInventory.cleanupInventory(); + + llinfos << "Cleaning up Selections" << llendflush; + + // Clean up selection managers after UI is destroyed, as UI may be observing them. + // Clean up before GL is shut down because we might be holding on to objects with texture references + LLSelectMgr::cleanupGlobals(); + + llinfos << "Shutting down OpenGL" << llendflush; + + // Shut down OpenGL + if( gViewerWindow) + { + gViewerWindow->shutdownGL(); + + // Destroy window, and make sure we're not fullscreen + // This may generate window reshape and activation events. + // Therefore must do this before destroying the message system. + delete gViewerWindow; + gViewerWindow = NULL; + llinfos << "ViewerWindow deleted" << llendflush; + } + + llinfos << "Cleaning up Keyboard & Joystick" << llendflush; + + // viewer UI relies on keyboard so keep it aound until viewer UI isa gone + delete gKeyboard; + gKeyboard = NULL; + + // Turn off Space Navigator and similar devices + LLViewerJoystick::getInstance()->terminate(); + + llinfos << "Cleaning up Objects" << llendflush; + + LLViewerObject::cleanupVOClasses(); + + LLWaterParamManager::cleanupClass(); + LLWLParamManager::cleanupClass(); + LLPostProcess::cleanupClass(); + + LLTracker::cleanupInstance(); + + // *FIX: This is handled in LLAppViewerWin32::cleanup(). + // I'm keeping the comment to remember its order in cleanup, + // in case of unforseen dependency. + //#if LL_WINDOWS + // gDXHardware.cleanup(); + //#endif // LL_WINDOWS + + LLVolumeMgr* volume_manager = LLPrimitive::getVolumeManager(); + if (!volume_manager->cleanup()) + { + llwarns << "Remaining references in the volume manager!" << llendflush; + } + LLPrimitive::cleanupVolumeManager(); + + llinfos << "Additional Cleanup..." << llendflush; + + LLViewerParcelMgr::cleanupGlobals(); + + // *Note: this is where gViewerStats used to be deleted. + + //end_messaging_system(); + + LLFollowCamMgr::cleanupClass(); + //LLVolumeMgr::cleanupClass(); + LLPrimitive::cleanupVolumeManager(); + LLWorldMapView::cleanupClass(); + LLFolderViewItem::cleanupClass(); + LLUI::cleanupClass(); + + // + // Shut down the VFS's AFTER the decode manager cleans up (since it cleans up vfiles). + // Also after viewerwindow is deleted, since it may have image pointers (which have vfiles) + // Also after shutting down the messaging system since it has VFS dependencies + + // + llinfos << "Cleaning up VFS" << llendflush; + LLVFile::cleanupClass(); + + llinfos << "Saving Data" << llendflush; + + // Store the time of our current logoff + gSavedPerAccountSettings.setU32("LastLogoff", time_corrected()); + + // Must do this after all panels have been deleted because panels that have persistent rects + // save their rects on delete. + gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE); + + LLUIColorTable::instance().saveUserSettings(); + + // PerAccountSettingsFile should be empty if no user has been logged on. + // *FIX:Mani This should get really saved in a "logoff" mode. + if (gSavedSettings.getString("PerAccountSettingsFile").empty()) + { + llinfos << "Not saving per-account settings; don't know the account name yet." << llendl; + } + else + { + gSavedPerAccountSettings.saveToFile(gSavedSettings.getString("PerAccountSettingsFile"), TRUE); + llinfos << "Saved settings" << llendflush; + } + + std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE); + // save all settings, even if equals defaults + gCrashSettings.saveToFile(crash_settings_filename, FALSE); + + std::string warnings_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Warnings")); + gWarningSettings.saveToFile(warnings_settings_filename, TRUE); + + // Save URL history file + LLURLHistory::saveFile("url_history.xml"); + + // save mute list. gMuteList used to also be deleted here too. + LLMuteList::getInstance()->cache(gAgent.getID()); + + if (mPurgeOnExit) + { + llinfos << "Purging all cache files on exit" << llendflush; + std::string mask = gDirUtilp->getDirDelimiter() + "*.*"; + gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask); + } + + removeMarkerFile(); // Any crashes from here on we'll just have to ignore + + writeDebugInfo(); + + LLLocationHistory::getInstance()->save(); + + LLAvatarIconIDCache::getInstance()->save(); + + LLViewerMedia::saveCookieFile(); + + // Stop the plugin read thread if it's running. + LLPluginProcessParent::setUseReadThread(false); + + llinfos << "Shutting down Threads" << llendflush; + + // Let threads finish + LLTimer idleTimer; + idleTimer.reset(); + const F64 max_idle_time = 5.f; // 5 seconds + while(1) + { + S32 pending = 0; + pending += LLAppViewer::getTextureCache()->update(1); // unpauses the worker thread + pending += LLAppViewer::getImageDecodeThread()->update(1); // unpauses the image thread + pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread + pending += LLVFSThread::updateClass(0); + pending += LLLFSThread::updateClass(0); + F64 idle_time = idleTimer.getElapsedTimeF64(); + if(!pending) + { + break ; //done + } + else if(idle_time >= max_idle_time) + { + llwarns << "Quitting with pending background tasks." << llendl; + break; + } + } + + // Delete workers first + // shotdown all worker threads before deleting them in case of co-dependencies + sTextureFetch->shutdown(); + sTextureCache->shutdown(); + sImageDecodeThread->shutdown(); + + sTextureFetch->shutDownTextureCacheThread() ; + sTextureFetch->shutDownImageDecodeThread() ; + + delete sTextureCache; + sTextureCache = NULL; + delete sTextureFetch; + sTextureFetch = NULL; + delete sImageDecodeThread; + sImageDecodeThread = NULL; + delete mFastTimerLogThread; + mFastTimerLogThread = NULL; + + if (LLFastTimerView::sAnalyzePerformance) + { + llinfos << "Analyzing performance" << llendl; + + std::string baseline_name = LLFastTimer::sLogName + "_baseline.slp"; + std::string current_name = LLFastTimer::sLogName + ".slp"; + std::string report_name = LLFastTimer::sLogName + "_report.csv"; + + LLFastTimerView::doAnalysis( + gDirUtilp->getExpandedFilename(LL_PATH_LOGS, baseline_name), + gDirUtilp->getExpandedFilename(LL_PATH_LOGS, current_name), + gDirUtilp->getExpandedFilename(LL_PATH_LOGS, report_name)); + } + LLMetricPerformanceTesterBasic::cleanClass() ; + +#if LL_RECORD_VIEWER_STATS + LLViewerStatsRecorder::cleanupClass(); +#endif + + llinfos << "Cleaning up Media and Textures" << llendflush; + + //Note: + //LLViewerMedia::cleanupClass() has to be put before gTextureList.shutdown() + //because some new image might be generated during cleaning up media. --bao + LLViewerMedia::cleanupClass(); + LLViewerParcelMedia::cleanupClass(); + gTextureList.shutdown(); // shutdown again in case a callback added something + LLUIImageList::getInstance()->cleanUp(); + + // This should eventually be done in LLAppViewer + LLImage::cleanupClass(); + LLVFSThread::cleanupClass(); + LLLFSThread::cleanupClass(); + +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "Auditing VFS" << llendl; + if(gVFS) + { + gVFS->audit(); + } +#endif + + llinfos << "Misc Cleanup" << llendflush; + + // For safety, the LLVFS has to be deleted *after* LLVFSThread. This should be cleaned up. + // (LLVFS doesn't know about LLVFSThread so can't kill pending requests) -Steve + delete gStaticVFS; + gStaticVFS = NULL; + delete gVFS; + gVFS = NULL; + + gSavedSettings.cleanup(); + LLUIColorTable::instance().clear(); + gCrashSettings.cleanup(); + + LLWatchdog::getInstance()->cleanup(); + + LLViewerAssetStatsFF::cleanup(); + + llinfos << "Shutting down message system" << llendflush; + end_messaging_system(); + + // *NOTE:Mani - The following call is not thread safe. + LLCurl::cleanupClass(); + + // If we're exiting to launch an URL, do that here so the screen + // is at the right resolution before we launch IE. + if (!gLaunchFileOnQuit.empty()) + { + llinfos << "Launch file on quit." << llendflush; +#if LL_WINDOWS + // Indicate an application is starting. + SetCursor(LoadCursor(NULL, IDC_WAIT)); +#endif + + // HACK: Attempt to wait until the screen res. switch is complete. + ms_sleep(1000); + + LLWeb::loadURLExternal( gLaunchFileOnQuit, false ); + llinfos << "File launched." << llendflush; + } + + LLMainLoopRepeater::instance().stop(); + + ll_close_fail_log(); + + MEM_TRACK_RELEASE + + llinfos << "Goodbye!" << llendflush; + + // return 0; + return true; +} + +// A callback for llerrs to call during the watchdog error. +void watchdog_llerrs_callback(const std::string &error_string) +{ + gLLErrorActivated = true; + +#ifdef LL_WINDOWS + RaiseException(0,0,0,0); +#else + raise(SIGQUIT); +#endif +} + +// A callback for the watchdog to call. +void watchdog_killer_callback() +{ + LLError::setFatalFunction(watchdog_llerrs_callback); + llerrs << "Watchdog killer event" << llendl; +} + +bool LLAppViewer::initThreads() +{ +#if MEM_TRACK_MEM + static const bool enable_threads = false; +#else + static const bool enable_threads = true; +#endif + + LLVFSThread::initClass(enable_threads && false); + LLLFSThread::initClass(enable_threads && false); + + // Image decoding + LLAppViewer::sImageDecodeThread = new LLImageDecodeThread(enable_threads && true); + LLAppViewer::sTextureCache = new LLTextureCache(enable_threads && true); + LLAppViewer::sTextureFetch = new LLTextureFetch(LLAppViewer::getTextureCache(), + sImageDecodeThread, + enable_threads && true, + app_metrics_qa_mode); + LLImage::initClass(); + + if (LLFastTimer::sLog || LLFastTimer::sMetricLog) + { + LLFastTimer::sLogLock = new LLMutex(NULL); + mFastTimerLogThread = new LLFastTimerLogThread(LLFastTimer::sLogName); + mFastTimerLogThread->start(); + } + + // *FIX: no error handling here! + return true; +} + +void errorCallback(const std::string &error_string) +{ +#ifndef LL_RELEASE_FOR_DOWNLOAD + OSMessageBox(error_string, LLTrans::getString("MBFatalError"), OSMB_OK); +#endif + + //Set the ErrorActivated global so we know to create a marker file + gLLErrorActivated = true; + + LLError::crashAndLoop(error_string); +} + +bool LLAppViewer::initLogging() +{ + // + // Set up logging defaults for the viewer + // + LLError::initForApplication( + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + LLError::setFatalFunction(errorCallback); + + // Remove the last ".old" log file. + std::string old_log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + "SecondLife.old"); + LLFile::remove(old_log_file); + + // Rename current log file to ".old" + std::string log_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, + "SecondLife.log"); + LLFile::rename(log_file, old_log_file); + + // Set the log file to SecondLife.log + + LLError::logToFile(log_file); + + // *FIX:Mani no error handling here! + return true; +} + +bool LLAppViewer::loadSettingsFromDirectory(const std::string& location_key, + bool set_defaults) +{ + if (!mSettingsLocationList) + { + llerrs << "Invalid settings location list" << llendl; + } + + LLControlGroup* global_settings = LLControlGroup::getInstance(sGlobalSettingsName); + for(LLInitParam::ParamIterator::const_iterator it = mSettingsLocationList->groups.begin(), end_it = mSettingsLocationList->groups.end(); + it != end_it; + ++it) + { + // skip settings groups that aren't the one we requested + if (it->name() != location_key) continue; + + ELLPath path_index = (ELLPath)it->path_index(); + if(path_index <= LL_PATH_NONE || path_index >= LL_PATH_LAST) + { + llerrs << "Out of range path index in app_settings/settings_files.xml" << llendl; + return false; + } + + LLInitParam::ParamIterator::const_iterator file_it, end_file_it; + for (file_it = it->files.begin(), end_file_it = it->files.end(); + file_it != end_file_it; + ++file_it) + { + llinfos << "Attempting to load settings for the group " << file_it->name() + << " - from location " << location_key << llendl; + + LLControlGroup* settings_group = LLControlGroup::getInstance(file_it->name); + if(!settings_group) + { + llwarns << "No matching settings group for name " << file_it->name() << llendl; + continue; + } + + std::string full_settings_path; + + if (file_it->file_name_setting.isProvided() + && global_settings->controlExists(file_it->file_name_setting)) + { + // try to find filename stored in file_name_setting control + full_settings_path = global_settings->getString(file_it->file_name_setting); + if (!gDirUtilp->fileExists(full_settings_path)) + { + // search in default path + full_settings_path = gDirUtilp->getExpandedFilename((ELLPath)path_index, full_settings_path); + } + } + else + { + // by default, use specified file name + full_settings_path = gDirUtilp->getExpandedFilename((ELLPath)path_index, file_it->file_name()); + } + + if(settings_group->loadFromFile(full_settings_path, set_defaults, file_it->persistent)) + { // success! + llinfos << "Loaded settings file " << full_settings_path << llendl; + } + else + { // failed to load + if(file_it->required) + { + llerrs << "Error: Cannot load required settings file from: " << full_settings_path << llendl; + return false; + } + else + { + // only complain if we actually have a filename at this point + if (!full_settings_path.empty()) + { + llinfos << "Cannot load " << full_settings_path << " - No settings found." << llendl; + } + } + } + } + } + + return true; +} + +std::string LLAppViewer::getSettingsFilename(const std::string& location_key, + const std::string& file) +{ + for(LLInitParam::ParamIterator::const_iterator it = mSettingsLocationList->groups.begin(), end_it = mSettingsLocationList->groups.end(); + it != end_it; + ++it) + { + if (it->name() == location_key) + { + LLInitParam::ParamIterator::const_iterator file_it, end_file_it; + for (file_it = it->files.begin(), end_file_it = it->files.end(); + file_it != end_file_it; + ++file_it) + { + if (file_it->name() == file) + { + return file_it->file_name; + } + } + } + } + + return std::string(); +} + +void LLAppViewer::loadColorSettings() +{ + LLUIColorTable::instance().loadFromSettings(); +} + +bool LLAppViewer::initConfiguration() +{ + //Load settings files list + std::string settings_file_list = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "settings_files.xml"); + //LLControlGroup settings_control("SettingsFiles"); + //llinfos << "Loading settings file list " << settings_file_list << llendl; + //if (0 == settings_control.loadFromFile(settings_file_list)) + //{ + // llerrs << "Cannot load default configuration file " << settings_file_list << llendl; + //} + + LLXMLNodePtr root; + BOOL success = LLXMLNode::parseFile(settings_file_list, root, NULL); + if (!success) + { + llerrs << "Cannot load default configuration file " << settings_file_list << llendl; + } + + mSettingsLocationList = new SettingsFiles(); + + LLXUIParser parser; + parser.readXUI(root, *mSettingsLocationList, settings_file_list); + + if (!mSettingsLocationList->validateBlock()) + { + llerrs << "Invalid settings file list " << settings_file_list << llendl; + } + + // The settings and command line parsing have a fragile + // order-of-operation: + // - load defaults from app_settings + // - set procedural settings values + // - read command line settings + // - selectively apply settings needed to load user settings. + // - load overrides from user_settings + // - apply command line settings (to override the overrides) + // - load per account settings (happens in llstartup + + // - load defaults + bool set_defaults = true; + if(!loadSettingsFromDirectory("Default", set_defaults)) + { + std::ostringstream msg; + msg << "Unable to load default settings file. The installation may be corrupted."; + OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); + return false; + } + + LLUI::setupPaths(); // setup paths for LLTrans based on settings files only + LLTransUtil::parseStrings("strings.xml", default_trans_args); + LLTransUtil::parseLanguageStrings("language_settings.xml"); + // - set procedural settings + // Note: can't use LL_PATH_PER_SL_ACCOUNT for any of these since we haven't logged in yet + gSavedSettings.setString("ClientSettingsFile", + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global"))); + +#ifndef LL_RELEASE_FOR_DOWNLOAD + // provide developer build only overrides for these control variables that are not + // persisted to settings.xml + LLControlVariable* c = gSavedSettings.getControl("ShowConsoleWindow"); + if (c) + { + c->setValue(true, false); + } + c = gSavedSettings.getControl("AllowMultipleViewers"); + if (c) + { + c->setValue(true, false); + } +#endif + +#ifndef LL_RELEASE_FOR_DOWNLOAD + gSavedSettings.setBOOL("QAMode", TRUE ); + gSavedSettings.setS32("WatchdogEnabled", 0); +#endif + + gCrashSettings.getControl(CRASH_BEHAVIOR_SETTING)->getSignal()->connect(boost::bind(&handleCrashSubmitBehaviorChanged, _2)); + + // These are warnings that appear on the first experience of that condition. + // They are already set in the settings_default.xml file, but still need to be added to LLFirstUse + // for disable/reset ability +// LLFirstUse::addConfigVariable("FirstBalanceIncrease"); +// LLFirstUse::addConfigVariable("FirstBalanceDecrease"); +// LLFirstUse::addConfigVariable("FirstSit"); +// LLFirstUse::addConfigVariable("FirstMap"); +// LLFirstUse::addConfigVariable("FirstGoTo"); +// LLFirstUse::addConfigVariable("FirstBuild"); +// LLFirstUse::addConfigVariable("FirstLeftClickNoHit"); +// LLFirstUse::addConfigVariable("FirstTeleport"); +// LLFirstUse::addConfigVariable("FirstOverrideKeys"); +// LLFirstUse::addConfigVariable("FirstAttach"); +// LLFirstUse::addConfigVariable("FirstAppearance"); +// LLFirstUse::addConfigVariable("FirstInventory"); +// LLFirstUse::addConfigVariable("FirstSandbox"); +// LLFirstUse::addConfigVariable("FirstFlexible"); +// LLFirstUse::addConfigVariable("FirstDebugMenus"); +// LLFirstUse::addConfigVariable("FirstSculptedPrim"); +// LLFirstUse::addConfigVariable("FirstVoice"); +// LLFirstUse::addConfigVariable("FirstMedia"); + + // - read command line settings. + LLControlGroupCLP clp; + std::string cmd_line_config = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, + "cmd_line.xml"); + + clp.configure(cmd_line_config, &gSavedSettings); + + if(!initParseCommandLine(clp)) + { + llwarns << "Error parsing command line options. Command Line options ignored." << llendl; + + llinfos << "Command line usage:\n" << clp << llendl; + + std::ostringstream msg; + msg << LLTrans::getString("MBCmdLineError") << clp.getErrorMessage(); + OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK); + return false; + } + + // - selectively apply settings + + // If the user has specified a alternate settings file name. + // Load it now before loading the user_settings/settings.xml + if(clp.hasOption("settings")) + { + std::string user_settings_filename = + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + clp.getOption("settings")[0]); + gSavedSettings.setString("ClientSettingsFile", user_settings_filename); + llinfos << "Using command line specified settings filename: " + << user_settings_filename << llendl; + } + + // - load overrides from user_settings + loadSettingsFromDirectory("User"); + + if (gSavedSettings.getBOOL("FirstRunThisInstall")) + { + gSavedSettings.setString("SessionSettingsFile", "settings_minimal.xml"); + } + + if (clp.hasOption("sessionsettings")) + { + std::string session_settings_filename = clp.getOption("sessionsettings")[0]; + gSavedSettings.setString("SessionSettingsFile", session_settings_filename); + llinfos << "Using session settings filename: " + << session_settings_filename << llendl; + } + loadSettingsFromDirectory("Session"); + + if (clp.hasOption("usersessionsettings")) + { + std::string user_session_settings_filename = clp.getOption("usersessionsettings")[0]; + gSavedSettings.setString("UserSessionSettingsFile", user_session_settings_filename); + llinfos << "Using user session settings filename: " + << user_session_settings_filename << llendl; + + } + loadSettingsFromDirectory("UserSession"); + + // - apply command line settings + clp.notify(); + + // Register the core crash option as soon as we can + // if we want gdb post-mortem on cores we need to be up and running + // ASAP or we might miss init issue etc. + if(clp.hasOption("disablecrashlogger")) + { + llwarns << "Crashes will be handled by system, stack trace logs and crash logger are both disabled" << llendl; + LLAppViewer::instance()->disableCrashlogger(); + } + + // Handle initialization from settings. + // Start up the debugging console before handling other options. + if (gSavedSettings.getBOOL("ShowConsoleWindow")) + { + initConsole(); + } + + if(clp.hasOption("help")) + { + std::ostringstream msg; + msg << LLTrans::getString("MBCmdLineUsg") << "\n" << clp; + llinfos << msg.str() << llendl; + + OSMessageBox( + msg.str().c_str(), + LLStringUtil::null, + OSMB_OK); + + return false; + } + + if(clp.hasOption("set")) + { + const LLCommandLineParser::token_vector_t& set_values = clp.getOption("set"); + if(0x1 & set_values.size()) + { + llwarns << "Invalid '--set' parameter count." << llendl; + } + else + { + LLCommandLineParser::token_vector_t::const_iterator itr = set_values.begin(); + for(; itr != set_values.end(); ++itr) + { + const std::string& name = *itr; + const std::string& value = *(++itr); + LLControlVariable* c = LLControlGroup::getInstance(sGlobalSettingsName)->getControl(name); + if(c) + { + c->setValue(value, false); + } + else + { + llwarns << "'--set' specified with unknown setting: '" + << name << "'." << llendl; + } + } + } + } + + if(clp.hasOption("channel")) + { + LLVersionInfo::resetChannel(clp.getOption("channel")[0]); + } + + + // If we have specified crash on startup, set the global so we'll trigger the crash at the right time + if(clp.hasOption("crashonstartup")) + { + gCrashOnStartup = TRUE; + } + + if (clp.hasOption("logperformance")) + { + LLFastTimer::sLog = TRUE; + LLFastTimer::sLogName = std::string("performance"); + } + + if (clp.hasOption("logmetrics")) + { + LLFastTimer::sMetricLog = TRUE ; + // '--logmetrics' can be specified with a named test metric argument so the data gathering is done only on that test + // In the absence of argument, every metric is gathered (makes for a rather slow run and hard to decipher report...) + std::string test_name = clp.getOption("logmetrics")[0]; + llinfos << "'--logmetrics' argument : " << test_name << llendl; + if (test_name == "") + { + llwarns << "No '--logmetrics' argument given, will output all metrics to " << DEFAULT_METRIC_NAME << llendl; + LLFastTimer::sLogName = DEFAULT_METRIC_NAME; + } + else + { + LLFastTimer::sLogName = test_name; + } + } + + if (clp.hasOption("graphicslevel")) + { + const LLCommandLineParser::token_vector_t& value = clp.getOption("graphicslevel"); + if(value.size() != 1) + { + llwarns << "Usage: -graphicslevel <0-3>" << llendl; + } + else + { + std::string detail = value.front(); + mForceGraphicsDetail = TRUE; + + switch (detail.c_str()[0]) + { + case '0': + gSavedSettings.setU32("RenderQualityPerformance", 0); + break; + case '1': + gSavedSettings.setU32("RenderQualityPerformance", 1); + break; + case '2': + gSavedSettings.setU32("RenderQualityPerformance", 2); + break; + case '3': + gSavedSettings.setU32("RenderQualityPerformance", 3); + break; + default: + mForceGraphicsDetail = FALSE; + llwarns << "Usage: -graphicslevel <0-3>" << llendl; + break; + } + } + } + + if (clp.hasOption("analyzeperformance")) + { + LLFastTimerView::sAnalyzePerformance = TRUE; + } + + if (clp.hasOption("replaysession")) + { + LLAgentPilot::sReplaySession = TRUE; + } + + if (clp.hasOption("nonotifications")) + { + gSavedSettings.getControl("IgnoreAllNotifications")->setValue(true, false); + } + + if (clp.hasOption("debugsession")) + { + gDebugSession = TRUE; + gDebugGL = TRUE; + + ll_init_fail_log(gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "test_failures.log")); + } + + // Handle slurl use. NOTE: Don't let SL-55321 reappear. + + // *FIX: This init code should be made more robust to prevent + // the issue SL-55321 from returning. One thought is to allow + // only select options to be set from command line when a slurl + // is specified. More work on the settings system is needed to + // achieve this. For now... + + // *NOTE:Mani The command line parser parses tokens and is + // setup to bail after parsing the '--url' option or the + // first option specified without a '--option' flag (or + // any other option that uses the 'last_option' setting - + // see LLControlGroupCLP::configure()) + + // What can happen is that someone can use IE (or potentially + // other browsers) and do the rough equivalent of command + // injection and steal passwords. Phoenix. SL-55321 + if(clp.hasOption("url")) + { + LLStartUp::setStartSLURL(LLSLURL(clp.getOption("url")[0])); + if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION) + { + LLGridManager::getInstance()->setGridChoice(LLStartUp::getStartSLURL().getGrid()); + + } + } + else if(clp.hasOption("slurl")) + { + LLSLURL start_slurl(clp.getOption("slurl")[0]); + LLStartUp::setStartSLURL(start_slurl); + } + + const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); + if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) + { + // hack to force the skin to default. + gDirUtilp->setSkinFolder(skinfolder->getValue().asString()); + //gDirUtilp->setSkinFolder("default"); + } + + mYieldTime = gSavedSettings.getS32("YieldTime"); + + // Read skin/branding settings if specified. + //if (! gDirUtilp->getSkinDir().empty() ) + //{ + // std::string skin_def_file = gDirUtilp->findSkinnedFilename("skin.xml"); + // LLXmlTree skin_def_tree; + + // if (!skin_def_tree.parseFile(skin_def_file)) + // { + // llerrs << "Failed to parse skin definition." << llendl; + // } + + //} + +#if LL_DARWIN + // Initialize apple menubar and various callbacks + init_apple_menu(LLTrans::getString("APP_NAME").c_str()); + +#if __ppc__ + // If the CPU doesn't have Altivec (i.e. it's not at least a G4), don't go any further. + // Only test PowerPC - all Intel Macs have SSE. + if(!gSysCPU.hasAltivec()) + { + std::ostringstream msg; + msg << LLTrans::getString("MBRequiresAltiVec"); + OSMessageBox( + msg.str(), + LLStringUtil::null, + OSMB_OK); + removeMarkerFile(); + return false; + } +#endif + +#endif // LL_DARWIN + + // Display splash screen. Must be after above check for previous + // crash as this dialog is always frontmost. + std::string splash_msg; + LLStringUtil::format_map_t args; + args["[APP_NAME]"] = LLTrans::getString("SECOND_LIFE"); + splash_msg = LLTrans::getString("StartupLoading", args); + LLSplashScreen::show(); + LLSplashScreen::update(splash_msg); + + //LLVolumeMgr::initClass(); + LLVolumeMgr* volume_manager = new LLVolumeMgr(); + volume_manager->useMutex(); // LLApp and LLMutex magic must be manually enabled + LLPrimitive::setVolumeManager(volume_manager); + + // Note: this is where we used to initialize gFeatureManagerp. + + gStartTime = totalTime(); + + // + // Set the name of the window + // + gWindowTitle = LLTrans::getString("APP_NAME"); +#if LL_DEBUG + gWindowTitle += std::string(" [DEBUG] ") + gArgs; +#else + gWindowTitle += std::string(" ") + gArgs; +#endif + LLStringUtil::truncate(gWindowTitle, 255); + + //RN: if we received a URL, hand it off to the existing instance. + // don't call anotherInstanceRunning() when doing URL handoff, as + // it relies on checking a marker file which will not work when running + // out of different directories + + if (LLStartUp::getStartSLURL().isValid()) + { + if (sendURLToOtherInstance(LLStartUp::getStartSLURL().getSLURLString())) + { + // successfully handed off URL to existing instance, exit + return false; + } + } + + if (!gSavedSettings.getBOOL("AllowMultipleViewers")) + { + // + // Check for another instance of the app running + // + + mSecondInstance = anotherInstanceRunning(); + + if (mSecondInstance) + { + std::ostringstream msg; + msg << LLTrans::getString("MBAlreadyRunning"); + OSMessageBox( + msg.str(), + LLStringUtil::null, + OSMB_OK); + return false; + } + + initMarkerFile(); + + checkForCrash(); + } + else + { + mSecondInstance = anotherInstanceRunning(); + + if (mSecondInstance) + { + // This is the second instance of SL. Turn off voice support, + // but make sure the setting is *not* persisted. + LLControlVariable* disable_voice = gSavedSettings.getControl("CmdLineDisableVoice"); + if(disable_voice) + { + const BOOL DO_NOT_PERSIST = FALSE; + disable_voice->setValue(LLSD(TRUE), DO_NOT_PERSIST); + } + } + + initMarkerFile(); + + if(!mSecondInstance) + { + checkForCrash(); + } + } + + // need to do this here - need to have initialized global settings first + std::string nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" ); + if ( !nextLoginLocation.empty() ) + { + LLStartUp::setStartSLURL(LLSLURL(nextLoginLocation)); + }; + + gLastRunVersion = gSavedSettings.getString("LastRunVersion"); + + loadColorSettings(); + + return true; // Config was successful. +} + +namespace { + // *TODO - decide if there's a better place for these functions. + // do we need a file llupdaterui.cpp or something? -brad + + void apply_update_callback(LLSD const & notification, LLSD const & response) + { + lldebugs << "LLUpdate user response: " << response << llendl; + if(response["OK_okcancelbuttons"].asBoolean()) + { + llinfos << "LLUpdate restarting viewer" << llendl; + static const bool install_if_ready = true; + // *HACK - this lets us launch the installer immediately for now + LLUpdaterService().startChecking(install_if_ready); + } + } + + void apply_update_ok_callback(LLSD const & notification, LLSD const & response) + { + llinfos << "LLUpdate restarting viewer" << llendl; + static const bool install_if_ready = true; + // *HACK - this lets us launch the installer immediately for now + LLUpdaterService().startChecking(install_if_ready); + } + + void on_update_downloaded(LLSD const & data) + { + std::string notification_name; + void (*apply_callback)(LLSD const &, LLSD const &) = NULL; + + if(data["required"].asBoolean()) + { + if(LLStartUp::getStartupState() <= STATE_LOGIN_WAIT) + { + // The user never saw the progress bar. + apply_callback = &apply_update_ok_callback; + notification_name = "RequiredUpdateDownloadedVerboseDialog"; + } + else if(LLStartUp::getStartupState() < STATE_WORLD_INIT) + { + // The user is logging in but blocked. + apply_callback = &apply_update_ok_callback; + notification_name = "RequiredUpdateDownloadedDialog"; + } + else + { + // The user is already logged in; treat like an optional update. + apply_callback = &apply_update_callback; + notification_name = "DownloadBackgroundTip"; + } + } + else + { + apply_callback = &apply_update_callback; + if(LLStartUp::getStartupState() < STATE_STARTED) + { + // CHOP-262 we need to use a different notification + // method prior to login. + notification_name = "DownloadBackgroundDialog"; + } + else + { + notification_name = "DownloadBackgroundTip"; + } + } + + LLSD substitutions; + substitutions["VERSION"] = data["version"]; + + // truncate version at the rightmost '.' + std::string version_short(data["version"]); + size_t short_length = version_short.rfind('.'); + if (short_length != std::string::npos) + { + version_short.resize(short_length); + } + + LLUIString relnotes_url("[RELEASE_NOTES_BASE_URL][CHANNEL_URL]/[VERSION_SHORT]"); + relnotes_url.setArg("[VERSION_SHORT]", version_short); + + // *TODO thread the update service's response through to this point + std::string const & channel = LLVersionInfo::getChannel(); + boost::shared_ptr channel_escaped(curl_escape(channel.c_str(), channel.size()), &curl_free); + + relnotes_url.setArg("[CHANNEL_URL]", channel_escaped.get()); + relnotes_url.setArg("[RELEASE_NOTES_BASE_URL]", LLTrans::getString("RELEASE_NOTES_BASE_URL")); + substitutions["RELEASE_NOTES_FULL_URL"] = relnotes_url.getString(); + + LLNotificationsUtil::add(notification_name, substitutions, LLSD(), apply_callback); + } + + void install_error_callback(LLSD const & notification, LLSD const & response) + { + LLAppViewer::instance()->forceQuit(); + } + + bool notify_update(LLSD const & evt) + { + std::string notification_name; + switch (evt["type"].asInteger()) + { + case LLUpdaterService::DOWNLOAD_COMPLETE: + on_update_downloaded(evt); + break; + case LLUpdaterService::INSTALL_ERROR: + if(evt["required"].asBoolean()) { + LLNotificationsUtil::add("FailedRequiredUpdateInstall", LLSD(), LLSD(), &install_error_callback); + } else { + LLNotificationsUtil::add("FailedUpdateInstall"); + } + break; + default: + break; + } + + // let others also handle this event by default + return false; + } + + bool on_bandwidth_throttle(LLUpdaterService * updater, LLSD const & evt) + { + updater->setBandwidthLimit(evt.asInteger() * (1024/8)); + return false; // Let others receive this event. + }; +}; + +void LLAppViewer::initUpdater() +{ + // Initialize the updater service. + // Generate URL to the udpater service + // Get Channel + // Get Version + std::string url = gSavedSettings.getString("UpdaterServiceURL"); + std::string channel = LLVersionInfo::getChannel(); + std::string version = LLVersionInfo::getVersion(); + std::string protocol_version = gSavedSettings.getString("UpdaterServiceProtocolVersion"); + std::string service_path = gSavedSettings.getString("UpdaterServicePath"); + U32 check_period = gSavedSettings.getU32("UpdaterServiceCheckPeriod"); + + mUpdater->setAppExitCallback(boost::bind(&LLAppViewer::forceQuit, this)); + mUpdater->initialize(protocol_version, + url, + service_path, + channel, + version); + mUpdater->setCheckPeriod(check_period); + mUpdater->setBandwidthLimit((int)gSavedSettings.getF32("UpdaterMaximumBandwidth") * (1024/8)); + gSavedSettings.getControl("UpdaterMaximumBandwidth")->getSignal()-> + connect(boost::bind(&on_bandwidth_throttle, mUpdater.get(), _2)); + if(gSavedSettings.getU32("UpdaterServiceSetting")) + { + bool install_if_ready = true; + mUpdater->startChecking(install_if_ready); + } + + LLEventPump & updater_pump = LLEventPumps::instance().obtain(LLUpdaterService::pumpName()); + updater_pump.listen("notify_update", ¬ify_update); +} + +void LLAppViewer::checkForCrash(void) +{ + +#if LL_SEND_CRASH_REPORTS + if (gLastExecEvent == LAST_EXEC_FROZE) + { + llinfos << "Last execution froze, requesting to send crash report." << llendl; + // + // Pop up a freeze or crash warning dialog + // + S32 choice; + if(gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) == CRASH_BEHAVIOR_ASK) + { + std::ostringstream msg; + msg << LLTrans::getString("MBFrozenCrashed"); + std::string alert = LLTrans::getString("APP_NAME") + " " + LLTrans::getString("MBAlert"); + choice = OSMessageBox(msg.str(), + alert, + OSMB_YESNO); + } + else if(gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) == CRASH_BEHAVIOR_NEVER_SEND) + { + choice = OSBTN_NO; + } + else + { + choice = OSBTN_YES; + } + + if (OSBTN_YES == choice) + { + llinfos << "Sending crash report." << llendl; + + bool report_freeze = true; + handleCrashReporting(report_freeze); + } + else + { + llinfos << "Not sending crash report." << llendl; + } + } +#endif // LL_SEND_CRASH_REPORTS + +} + +bool LLAppViewer::initWindow() +{ + LL_INFOS("AppInit") << "Initializing window..." << LL_ENDL; + + // store setting in a global for easy access and modification + gNoRender = gSavedSettings.getBOOL("DisableRendering"); + + // always start windowed + BOOL ignorePixelDepth = gSavedSettings.getBOOL("IgnorePixelDepth"); + gViewerWindow = new LLViewerWindow(gWindowTitle, + VIEWER_WINDOW_CLASSNAME, + gSavedSettings.getS32("WindowX"), gSavedSettings.getS32("WindowY"), + gSavedSettings.getS32("WindowWidth"), gSavedSettings.getS32("WindowHeight"), + gSavedSettings.getBOOL("WindowFullScreen"), ignorePixelDepth); + + // Need to load feature table before cheking to start watchdog. + const S32 NEVER_SUBMIT_REPORT = 2; + bool use_watchdog = false; + int watchdog_enabled_setting = gSavedSettings.getS32("WatchdogEnabled"); + if(watchdog_enabled_setting == -1){ + use_watchdog = !LLFeatureManager::getInstance()->isFeatureAvailable("WatchdogDisabled"); + } + else + { + // The user has explicitly set this setting; always use that value. + use_watchdog = bool(watchdog_enabled_setting); + } + + bool send_reports = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING) != NEVER_SUBMIT_REPORT; + if(use_watchdog && send_reports) + { + LLWatchdog::getInstance()->init(watchdog_killer_callback); + } + + LLNotificationsUI::LLNotificationManager::getInstance(); + + if (gSavedSettings.getBOOL("WindowMaximized")) + { + gViewerWindow->mWindow->maximize(); + } + + if (!gNoRender) + { + // + // Initialize GL stuff + // + + if (mForceGraphicsDetail) + { + LLFeatureManager::getInstance()->setGraphicsLevel(gSavedSettings.getU32("RenderQualityPerformance"), false); + } + + // Set this flag in case we crash while initializing GL + gSavedSettings.setBOOL("RenderInitError", TRUE); + gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); + + gPipeline.init(); + stop_glerror(); + gViewerWindow->initGLDefaults(); + + gSavedSettings.setBOOL("RenderInitError", FALSE); + gSavedSettings.saveToFile( gSavedSettings.getString("ClientSettingsFile"), TRUE ); + } + + //If we have a startup crash, it's usually near GL initialization, so simulate that. + if(gCrashOnStartup) + { + LLAppViewer::instance()->forceErrorLLError(); + } + + LLUI::sWindow = gViewerWindow->getWindow(); + + // Show watch cursor + gViewerWindow->setCursor(UI_CURSOR_WAIT); + + // Finish view initialization + gViewerWindow->initBase(); + + // show viewer window + //gViewerWindow->mWindow->show(); + + + return true; +} + +void LLAppViewer::writeDebugInfo() +{ + std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log"); + llinfos << "Opening debug file " << debug_filename << llendl; + llofstream out_file(debug_filename); + LLSDSerialize::toPrettyXML(gDebugInfo, out_file); + out_file.close(); +} + +void LLAppViewer::cleanupSavedSettings() +{ + gSavedSettings.setBOOL("MouseSun", FALSE); + + gSavedSettings.setBOOL("UseEnergy", TRUE); // force toggle to turn off, since sends message to simulator + + gSavedSettings.setBOOL("DebugWindowProc", gDebugWindowProc); + + gSavedSettings.setBOOL("ShowObjectUpdates", gShowObjectUpdates); + + if (!gNoRender) + { + if (gDebugView) + { + gSavedSettings.setBOOL("ShowDebugConsole", gDebugView->mDebugConsolep->getVisible()); + } + } + + // save window position if not maximized + // as we don't track it in callbacks + if(NULL != gViewerWindow) + { + BOOL maximized = gViewerWindow->mWindow->getMaximized(); + if (!maximized) + { + LLCoordScreen window_pos; + + if (gViewerWindow->mWindow->getPosition(&window_pos)) + { + gSavedSettings.setS32("WindowX", window_pos.mX); + gSavedSettings.setS32("WindowY", window_pos.mY); + } + } + } + + gSavedSettings.setF32("MapScale", LLWorldMapView::sMapScale ); + + // Some things are cached in LLAgent. + if (gAgent.isInitialized()) + { + gSavedSettings.setF32("RenderFarClip", gAgentCamera.mDrawDistance); + } +} + +void LLAppViewer::removeCacheFiles(const std::string& file_mask) +{ + std::string mask = gDirUtilp->getDirDelimiter() + file_mask; + gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), mask); +} + +void LLAppViewer::writeSystemInfo() +{ + gDebugInfo["SLLog"] = LLError::logFileName(); + + gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel(); + gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor(); + gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); + gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch(); + gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::getBuild(); + + gDebugInfo["CAFilename"] = gDirUtilp->getCAFile(); + + gDebugInfo["CPUInfo"]["CPUString"] = gSysCPU.getCPUString(); + gDebugInfo["CPUInfo"]["CPUFamily"] = gSysCPU.getFamily(); + gDebugInfo["CPUInfo"]["CPUMhz"] = (S32)gSysCPU.getMHz(); + gDebugInfo["CPUInfo"]["CPUAltivec"] = gSysCPU.hasAltivec(); + gDebugInfo["CPUInfo"]["CPUSSE"] = gSysCPU.hasSSE(); + gDebugInfo["CPUInfo"]["CPUSSE2"] = gSysCPU.hasSSE2(); + + gDebugInfo["RAMInfo"]["Physical"] = (LLSD::Integer)(gSysMemory.getPhysicalMemoryKB()); + gDebugInfo["RAMInfo"]["Allocated"] = (LLSD::Integer)(gMemoryAllocated>>10); // MB -> KB + gDebugInfo["OSInfo"] = getOSInfo().getOSStringSimple(); + + // The user is not logged on yet, but record the current grid choice login url + // which may have been the intended grid. This can b + gDebugInfo["GridName"] = LLGridManager::getInstance()->getGridLabel(); + + // *FIX:Mani - move this down in llappviewerwin32 +#ifdef LL_WINDOWS + DWORD thread_id = GetCurrentThreadId(); + gDebugInfo["MainloopThreadID"] = (S32)thread_id; +#endif + + // "CrashNotHandled" is set here, while things are running well, + // in case of a freeze. If there is a freeze, the crash logger will be launched + // and can read this value from the debug_info.log. + // If the crash is handled by LLAppViewer::handleViewerCrash, ie not a freeze, + // then the value of "CrashNotHandled" will be set to true. + gDebugInfo["CrashNotHandled"] = (LLSD::Boolean)true; + + // Insert crash host url (url to post crash log to) if configured. This insures + // that the crash report will go to the proper location in the case of a + // prior freeze. + std::string crashHostUrl = gSavedSettings.get("CrashHostUrl"); + if(crashHostUrl != "") + { + gDebugInfo["CrashHostUrl"] = crashHostUrl; + } + + // Dump some debugging info + LL_INFOS("SystemInfo") << LLTrans::getString("APP_NAME") + << " version " << LLVersionInfo::getShortVersion() << LL_ENDL; + + // Dump the local time and time zone + time_t now; + time(&now); + char tbuffer[256]; /* Flawfinder: ignore */ + strftime(tbuffer, 256, "%Y-%m-%dT%H:%M:%S %Z", localtime(&now)); + LL_INFOS("SystemInfo") << "Local time: " << tbuffer << LL_ENDL; + + // query some system information + LL_INFOS("SystemInfo") << "CPU info:\n" << gSysCPU << LL_ENDL; + LL_INFOS("SystemInfo") << "Memory info:\n" << gSysMemory << LL_ENDL; + LL_INFOS("SystemInfo") << "OS: " << getOSInfo().getOSStringSimple() << LL_ENDL; + LL_INFOS("SystemInfo") << "OS info: " << getOSInfo() << LL_ENDL; + + writeDebugInfo(); // Save out debug_info.log early, in case of crash. +} + +void LLAppViewer::handleViewerCrash() +{ + llinfos << "Handle viewer crash entry." << llendl; + + llinfos << "Last render pool type: " << LLPipeline::sCurRenderPoolType << llendl ; + + //print out recorded call stacks if there are any. + LLError::LLCallStacks::print(); + + LLAppViewer* pApp = LLAppViewer::instance(); + if (pApp->beingDebugged()) + { + // This will drop us into the debugger. + abort(); + } + + if (LLApp::isCrashloggerDisabled()) + { + abort(); + } + + // Returns whether a dialog was shown. + // Only do the logic in here once + if (pApp->mReportedCrash) + { + return; + } + pApp->mReportedCrash = TRUE; + + // Insert crash host url (url to post crash log to) if configured. + std::string crashHostUrl = gSavedSettings.get("CrashHostUrl"); + if(crashHostUrl != "") + { + gDebugInfo["CrashHostUrl"] = crashHostUrl; + } + + //We already do this in writeSystemInfo(), but we do it again here to make /sure/ we have a version + //to check against no matter what + gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel(); + + gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor(); + gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); + gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch(); + gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::getBuild(); + + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if ( parcel && parcel->getMusicURL()[0]) + { + gDebugInfo["ParcelMusicURL"] = parcel->getMusicURL(); + } + if ( parcel && parcel->getMediaURL()[0]) + { + gDebugInfo["ParcelMediaURL"] = parcel->getMediaURL(); + } + + + gDebugInfo["SettingsFilename"] = gSavedSettings.getString("ClientSettingsFile"); + gDebugInfo["CAFilename"] = gDirUtilp->getCAFile(); + gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName(); + gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath(); + gDebugInfo["SessionLength"] = F32(LLFrameTimer::getElapsedSeconds()); + gDebugInfo["StartupState"] = LLStartUp::getStartupStateString(); + gDebugInfo["RAMInfo"]["Allocated"] = (LLSD::Integer) LLMemory::getCurrentRSS() >> 10; + gDebugInfo["FirstLogin"] = (LLSD::Boolean) gAgent.isFirstLogin(); + gDebugInfo["FirstRunThisInstall"] = gSavedSettings.getBOOL("FirstRunThisInstall"); + + char *minidump_file = pApp->getMiniDumpFilename(); + if(minidump_file && minidump_file[0] != 0) + { + gDebugInfo["MinidumpPath"] = minidump_file; + } + + if(gLogoutInProgress) + { + gDebugInfo["LastExecEvent"] = LAST_EXEC_LOGOUT_CRASH; + } + else + { + gDebugInfo["LastExecEvent"] = gLLErrorActivated ? LAST_EXEC_LLERROR_CRASH : LAST_EXEC_OTHER_CRASH; + } + + if(gAgent.getRegion()) + { + gDebugInfo["CurrentSimHost"] = gAgent.getRegionHost().getHostName(); + gDebugInfo["CurrentRegion"] = gAgent.getRegion()->getName(); + + const LLVector3& loc = gAgent.getPositionAgent(); + gDebugInfo["CurrentLocationX"] = loc.mV[0]; + gDebugInfo["CurrentLocationY"] = loc.mV[1]; + gDebugInfo["CurrentLocationZ"] = loc.mV[2]; + } + + if(LLAppViewer::instance()->mMainloopTimeout) + { + gDebugInfo["MainloopTimeoutState"] = LLAppViewer::instance()->mMainloopTimeout->getState(); + } + + // The crash is being handled here so set this value to false. + // Otherwise the crash logger will think this crash was a freeze. + gDebugInfo["CrashNotHandled"] = (LLSD::Boolean)false; + + //Write out the crash status file + //Use marker file style setup, as that's the simplest, especially since + //we're already in a crash situation + if (gDirUtilp) + { + std::string crash_file_name; + if(gLLErrorActivated) crash_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,LLERROR_MARKER_FILE_NAME); + else crash_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,ERROR_MARKER_FILE_NAME); + llinfos << "Creating crash marker file " << crash_file_name << llendl; + + LLAPRFile crash_file ; + crash_file.open(crash_file_name, LL_APR_W); + if (crash_file.getFileHandle()) + { + LL_INFOS("MarkerFile") << "Created crash marker file " << crash_file_name << LL_ENDL; + } + else + { + LL_WARNS("MarkerFile") << "Cannot create error marker file " << crash_file_name << LL_ENDL; + } + } + + if (gMessageSystem && gDirUtilp) + { + std::string filename; + filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "stats.log"); + llofstream file(filename, llofstream::binary); + if(file.good()) + { + llinfos << "Handle viewer crash generating stats log." << llendl; + gMessageSystem->summarizeLogs(file); + file.close(); + } + } + + if (gMessageSystem) + { + gMessageSystem->getCircuitInfo(gDebugInfo["CircuitInfo"]); + gMessageSystem->stopLogging(); + } + + if (LLWorld::instanceExists()) LLWorld::getInstance()->getInfo(gDebugInfo); + + // Close the debug file + pApp->writeDebugInfo(); + + LLError::logToFile(""); + + // Remove the marker file, since otherwise we'll spawn a process that'll keep it locked + if(gDebugInfo["LastExecEvent"].asInteger() == LAST_EXEC_LOGOUT_CRASH) + { + pApp->removeMarkerFile(true); + } + else + { + pApp->removeMarkerFile(false); + } + +#if LL_SEND_CRASH_REPORTS + // Call to pure virtual, handled by platform specific llappviewer instance. + pApp->handleCrashReporting(); +#endif + + return; +} + +bool LLAppViewer::anotherInstanceRunning() +{ + // We create a marker file when the program starts and remove the file when it finishes. + // If the file is currently locked, that means another process is already running. + + std::string marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, MARKER_FILE_NAME); + LL_DEBUGS("MarkerFile") << "Checking marker file for lock..." << LL_ENDL; + + //Freeze case checks + if (LLAPRFile::isExist(marker_file, NULL, LL_APR_RB)) + { + // File exists, try opening with write permissions + LLAPRFile outfile ; + outfile.open(marker_file, LL_APR_WB); + apr_file_t* fMarker = outfile.getFileHandle() ; + if (!fMarker) + { + // Another instance is running. Skip the rest of these operations. + LL_INFOS("MarkerFile") << "Marker file is locked." << LL_ENDL; + return true; + } + if (apr_file_lock(fMarker, APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE) != APR_SUCCESS) //flock(fileno(fMarker), LOCK_EX | LOCK_NB) == -1) + { + LL_INFOS("MarkerFile") << "Marker file is locked." << LL_ENDL; + return true; + } + // No other instances; we'll lock this file now & delete on quit. + } + LL_DEBUGS("MarkerFile") << "Marker file isn't locked." << LL_ENDL; + return false; +} + +void LLAppViewer::initMarkerFile() +{ + //First, check for the existence of other files. + //There are marker files for two different types of crashes + + mMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,MARKER_FILE_NAME); + LL_DEBUGS("MarkerFile") << "Checking marker file for lock..." << LL_ENDL; + + //We've got 4 things to test for here + // - Other Process Running (SecondLife.exec_marker present, locked) + // - Freeze (SecondLife.exec_marker present, not locked) + // - LLError Crash (SecondLife.llerror_marker present) + // - Other Crash (SecondLife.error_marker present) + // These checks should also remove these files for the last 2 cases if they currently exist + + //LLError/Error checks. Only one of these should ever happen at a time. + std::string logout_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LOGOUT_MARKER_FILE_NAME); + std::string llerror_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, LLERROR_MARKER_FILE_NAME); + std::string error_marker_file = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ERROR_MARKER_FILE_NAME); + + if (LLAPRFile::isExist(mMarkerFileName, NULL, LL_APR_RB) && !anotherInstanceRunning()) + { + gLastExecEvent = LAST_EXEC_FROZE; + LL_INFOS("MarkerFile") << "Exec marker found: program froze on previous execution" << LL_ENDL; + } + if(LLAPRFile::isExist(logout_marker_file, NULL, LL_APR_RB)) + { + gLastExecEvent = LAST_EXEC_LOGOUT_FROZE; + LL_INFOS("MarkerFile") << "Last exec LLError crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; + LLAPRFile::remove(logout_marker_file); + } + if(LLAPRFile::isExist(llerror_marker_file, NULL, LL_APR_RB)) + { + if(gLastExecEvent == LAST_EXEC_LOGOUT_FROZE) gLastExecEvent = LAST_EXEC_LOGOUT_CRASH; + else gLastExecEvent = LAST_EXEC_LLERROR_CRASH; + LL_INFOS("MarkerFile") << "Last exec LLError crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; + LLAPRFile::remove(llerror_marker_file); + } + if(LLAPRFile::isExist(error_marker_file, NULL, LL_APR_RB)) + { + if(gLastExecEvent == LAST_EXEC_LOGOUT_FROZE) gLastExecEvent = LAST_EXEC_LOGOUT_CRASH; + else gLastExecEvent = LAST_EXEC_OTHER_CRASH; + LL_INFOS("MarkerFile") << "Last exec crashed, setting LastExecEvent to " << gLastExecEvent << LL_ENDL; + LLAPRFile::remove(error_marker_file); + } + + // No new markers if another instance is running. + if(anotherInstanceRunning()) + { + return; + } + + // Create the marker file for this execution & lock it + apr_status_t s; + s = mMarkerFile.open(mMarkerFileName, LL_APR_W, TRUE); + + if (s == APR_SUCCESS && mMarkerFile.getFileHandle()) + { + LL_DEBUGS("MarkerFile") << "Marker file created." << LL_ENDL; + } + else + { + LL_INFOS("MarkerFile") << "Failed to create marker file." << LL_ENDL; + return; + } + if (apr_file_lock(mMarkerFile.getFileHandle(), APR_FLOCK_NONBLOCK | APR_FLOCK_EXCLUSIVE) != APR_SUCCESS) + { + mMarkerFile.close() ; + LL_INFOS("MarkerFile") << "Marker file cannot be locked." << LL_ENDL; + return; + } + + LL_DEBUGS("MarkerFile") << "Marker file locked." << LL_ENDL; +} + +void LLAppViewer::removeMarkerFile(bool leave_logout_marker) +{ + LL_DEBUGS("MarkerFile") << "removeMarkerFile()" << LL_ENDL; + if (mMarkerFile.getFileHandle()) + { + mMarkerFile.close() ; + LLAPRFile::remove( mMarkerFileName ); + } + if (mLogoutMarkerFile != NULL && !leave_logout_marker) + { + LLAPRFile::remove( mLogoutMarkerFileName ); + mLogoutMarkerFile = NULL; + } +} + +void LLAppViewer::forceQuit() +{ + LLApp::setQuitting(); +} + +//TODO: remove +void LLAppViewer::fastQuit(S32 error_code) +{ + // finish pending transfers + flushVFSIO(); + // let sim know we're logging out + sendLogoutRequest(); + // flush network buffers by shutting down messaging system + end_messaging_system(); + // figure out the error code + S32 final_error_code = error_code ? error_code : (S32)isError(); + // this isn't a crash + removeMarkerFile(); + // get outta here + _exit(final_error_code); +} + +void LLAppViewer::requestQuit() +{ + llinfos << "requestQuit" << llendl; + + LLViewerRegion* region = gAgent.getRegion(); + + if( (LLStartUp::getStartupState() < STATE_STARTED) || !region ) + { + // If we have a region, make some attempt to send a logout request first. + // This prevents the halfway-logged-in avatar from hanging around inworld for a couple minutes. + if(region) + { + sendLogoutRequest(); + } + + // Quit immediately + forceQuit(); + return; + } + + // Try to send metrics back to the grid + metricsSend(!gDisconnected); + + LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); + effectp->setPositionGlobal(gAgent.getPositionGlobal()); + effectp->setColor(LLColor4U(gAgent.getEffectColor())); + LLHUDManager::getInstance()->sendEffects(); + effectp->markDead() ;//remove it. + + // Attempt to close all floaters that might be + // editing things. + if (gFloaterView) + { + // application is quitting + gFloaterView->closeAllChildren(true); + } + + LLSideTray::getInstance()->notifyChildren(LLSD().with("request","quit")); + + send_stats(); + + gLogoutTimer.reset(); + mQuitRequested = true; +} + +static bool finish_quit(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if (option == 0) + { + LLAppViewer::instance()->requestQuit(); + } + return false; +} +static LLNotificationFunctorRegistration finish_quit_reg("ConfirmQuit", finish_quit); + +static bool switch_standard_skin_and_quit(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if (option == 0) + { + gSavedSettings.setString("SessionSettingsFile", ""); + LLAppViewer::instance()->requestQuit(); + } + return false; +} + +static LLNotificationFunctorRegistration standard_skin_quit_reg("SwitchToStandardSkinAndQuit", switch_standard_skin_and_quit); + +void LLAppViewer::userQuit() +{ + if (gDisconnected || gViewerWindow->getProgressView()->getVisible()) + { + requestQuit(); + } + else + { + LLNotificationsUtil::add("ConfirmQuit"); + } +} + +static bool finish_early_exit(const LLSD& notification, const LLSD& response) +{ + LLAppViewer::instance()->forceQuit(); + return false; +} + +void LLAppViewer::earlyExit(const std::string& name, const LLSD& substitutions) +{ + llwarns << "app_early_exit: " << name << llendl; + gDoDisconnect = TRUE; + LLNotificationsUtil::add(name, substitutions, LLSD(), finish_early_exit); +} + +// case where we need the viewer to exit without any need for notifications +void LLAppViewer::earlyExitNoNotify() +{ + llwarns << "app_early_exit with no notification: " << llendl; + gDoDisconnect = TRUE; + finish_early_exit( LLSD(), LLSD() ); +} + +void LLAppViewer::abortQuit() +{ + llinfos << "abortQuit()" << llendl; + mQuitRequested = false; +} + +void LLAppViewer::migrateCacheDirectory() +{ +#if LL_WINDOWS || LL_DARWIN + // NOTE: (Nyx) as of 1.21, cache for mac is moving to /library/caches/SecondLife from + // /library/application support/SecondLife/cache This should clear/delete the old dir. + + // As of 1.23 the Windows cache moved from + // C:\Documents and Settings\James\Application Support\SecondLife\cache + // to + // C:\Documents and Settings\James\Local Settings\Application Support\SecondLife + // + // The Windows Vista equivalent is from + // C:\Users\James\AppData\Roaming\SecondLife\cache + // to + // C:\Users\James\AppData\Local\SecondLife + // + // Note the absence of \cache on the second path. James. + + // Only do this once per fresh install of this version. + if (gSavedSettings.getBOOL("MigrateCacheDirectory")) + { + gSavedSettings.setBOOL("MigrateCacheDirectory", FALSE); + + std::string delimiter = gDirUtilp->getDirDelimiter(); + std::string old_cache_dir = gDirUtilp->getOSUserAppDir() + delimiter + "cache"; + std::string new_cache_dir = gDirUtilp->getCacheDir(true); + + if (gDirUtilp->fileExists(old_cache_dir)) + { + llinfos << "Migrating cache from " << old_cache_dir << " to " << new_cache_dir << llendl; + + // Migrate inventory cache to avoid pain to inventory database after mass update + S32 file_count = 0; + std::string file_name; + std::string mask = delimiter + "*.*"; + while (gDirUtilp->getNextFileInDir(old_cache_dir, mask, file_name)) + { + if (file_name == "." || file_name == "..") continue; + std::string source_path = old_cache_dir + delimiter + file_name; + std::string dest_path = new_cache_dir + delimiter + file_name; + if (!LLFile::rename(source_path, dest_path)) + { + file_count++; + } + } + llinfos << "Moved " << file_count << " files" << llendl; + + // Nuke the old cache + gDirUtilp->setCacheDir(old_cache_dir); + purgeCache(); + gDirUtilp->setCacheDir(new_cache_dir); + +#if LL_DARWIN + // Clean up Mac files not deleted by removing *.* + std::string ds_store = old_cache_dir + "/.DS_Store"; + if (gDirUtilp->fileExists(ds_store)) + { + LLFile::remove(ds_store); + } +#endif + if (LLFile::rmdir(old_cache_dir) != 0) + { + llwarns << "could not delete old cache directory " << old_cache_dir << llendl; + } + } + } +#endif // LL_WINDOWS || LL_DARWIN +} + +void dumpVFSCaches() +{ + llinfos << "======= Static VFS ========" << llendl; + gStaticVFS->listFiles(); +#if LL_WINDOWS + llinfos << "======= Dumping static VFS to StaticVFSDump ========" << llendl; + WCHAR w_str[MAX_PATH]; + GetCurrentDirectory(MAX_PATH, w_str); + S32 res = LLFile::mkdir("StaticVFSDump"); + if (res == -1) + { + if (errno != EEXIST) + { + llwarns << "Couldn't create dir StaticVFSDump" << llendl; + } + } + SetCurrentDirectory(utf8str_to_utf16str("StaticVFSDump").c_str()); + gStaticVFS->dumpFiles(); + SetCurrentDirectory(w_str); +#endif + + llinfos << "========= Dynamic VFS ====" << llendl; + gVFS->listFiles(); +#if LL_WINDOWS + llinfos << "========= Dumping dynamic VFS to VFSDump ====" << llendl; + res = LLFile::mkdir("VFSDump"); + if (res == -1) + { + if (errno != EEXIST) + { + llwarns << "Couldn't create dir VFSDump" << llendl; + } + } + SetCurrentDirectory(utf8str_to_utf16str("VFSDump").c_str()); + gVFS->dumpFiles(); + SetCurrentDirectory(w_str); +#endif +} + +//static +U32 LLAppViewer::getTextureCacheVersion() +{ + //viewer texture cache version, change if the texture cache format changes. + const U32 TEXTURE_CACHE_VERSION = 7; + + return TEXTURE_CACHE_VERSION ; +} + +//static +U32 LLAppViewer::getObjectCacheVersion() +{ + // Viewer object cache version, change if object update + // format changes. JC + const U32 INDRA_OBJECT_CACHE_VERSION = 14; + + return INDRA_OBJECT_CACHE_VERSION; +} + +bool LLAppViewer::initCache() +{ + mPurgeCache = false; + BOOL read_only = mSecondInstance ? TRUE : FALSE; + LLAppViewer::getTextureCache()->setReadOnly(read_only) ; + LLVOCache::getInstance()->setReadOnly(read_only); + + BOOL texture_cache_mismatch = FALSE ; + if (gSavedSettings.getS32("LocalCacheVersion") != LLAppViewer::getTextureCacheVersion()) + { + texture_cache_mismatch = TRUE ; + if(!read_only) + { + gSavedSettings.setS32("LocalCacheVersion", LLAppViewer::getTextureCacheVersion()); + } + } + + if(!read_only) + { + // Purge cache if user requested it + if (gSavedSettings.getBOOL("PurgeCacheOnStartup") || + gSavedSettings.getBOOL("PurgeCacheOnNextStartup")) + { + gSavedSettings.setBOOL("PurgeCacheOnNextStartup", false); + mPurgeCache = true; + } + + // We have moved the location of the cache directory over time. + migrateCacheDirectory(); + + // Setup and verify the cache location + std::string cache_location = gSavedSettings.getString("CacheLocation"); + std::string new_cache_location = gSavedSettings.getString("NewCacheLocation"); + if (new_cache_location != cache_location) + { + gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation")); + purgeCache(); // purge old cache + gSavedSettings.setString("CacheLocation", new_cache_location); + gSavedSettings.setString("CacheLocationTopFolder", gDirUtilp->getBaseFileName(new_cache_location)); + } + } + + if (!gDirUtilp->setCacheDir(gSavedSettings.getString("CacheLocation"))) + { + LL_WARNS("AppCache") << "Unable to set cache location" << LL_ENDL; + gSavedSettings.setString("CacheLocation", ""); + gSavedSettings.setString("CacheLocationTopFolder", ""); + } + + if (mPurgeCache && !read_only) + { + LLSplashScreen::update(LLTrans::getString("StartupClearingCache")); + purgeCache(); + } + + LLSplashScreen::update(LLTrans::getString("StartupInitializingTextureCache")); + + // Init the texture cache + // Allocate 80% of the cache size for textures + const S32 MB = 1024*1024; + S64 cache_size = (S64)(gSavedSettings.getU32("CacheSize")) * MB; + const S64 MAX_CACHE_SIZE = 1024*MB; + cache_size = llmin(cache_size, MAX_CACHE_SIZE); + S64 texture_cache_size = ((cache_size * 8)/10); + S64 extra = LLAppViewer::getTextureCache()->initCache(LL_PATH_CACHE, texture_cache_size, texture_cache_mismatch); + texture_cache_size -= extra; + + LLVOCache::getInstance()->initCache(LL_PATH_CACHE, gSavedSettings.getU32("CacheNumberOfRegionsForObjects"), getObjectCacheVersion()) ; + + LLSplashScreen::update(LLTrans::getString("StartupInitializingVFS")); + + // Init the VFS + S64 vfs_size = cache_size - texture_cache_size; + const S64 MAX_VFS_SIZE = 1024 * MB; // 1 GB + vfs_size = llmin(vfs_size, MAX_VFS_SIZE); + vfs_size = (vfs_size / MB) * MB; // make sure it is MB aligned + U32 vfs_size_u32 = (U32)vfs_size; + U32 old_vfs_size = gSavedSettings.getU32("VFSOldSize") * MB; + bool resize_vfs = (vfs_size_u32 != old_vfs_size); + if (resize_vfs) + { + gSavedSettings.setU32("VFSOldSize", vfs_size_u32/MB); + } + LL_INFOS("AppCache") << "VFS CACHE SIZE: " << vfs_size/(1024*1024) << " MB" << LL_ENDL; + + // This has to happen BEFORE starting the vfs + //time_t ltime; + srand(time(NULL)); // Flawfinder: ignore + U32 old_salt = gSavedSettings.getU32("VFSSalt"); + U32 new_salt; + std::string old_vfs_data_file; + std::string old_vfs_index_file; + std::string new_vfs_data_file; + std::string new_vfs_index_file; + std::string static_vfs_index_file; + std::string static_vfs_data_file; + + if (gSavedSettings.getBOOL("AllowMultipleViewers")) + { + // don't mess with renaming the VFS in this case + new_salt = old_salt; + } + else + { + do + { + new_salt = rand(); + } while( new_salt == old_salt ); + } + + old_vfs_data_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_DATA_FILE_BASE) + llformat("%u",old_salt); + + // make sure this file exists + llstat s; + S32 stat_result = LLFile::stat(old_vfs_data_file, &s); + if (stat_result) + { + // doesn't exist, look for a data file + std::string mask; + mask = gDirUtilp->getDirDelimiter(); + mask += VFS_DATA_FILE_BASE; + mask += "*"; + + std::string dir; + dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""); + + std::string found_file; + if (gDirUtilp->getNextFileInDir(dir, mask, found_file)) + { + old_vfs_data_file = dir + gDirUtilp->getDirDelimiter() + found_file; + + S32 start_pos = found_file.find_last_of('.'); + if (start_pos > 0) + { + sscanf(found_file.substr(start_pos+1).c_str(), "%d", &old_salt); + } + LL_DEBUGS("AppCache") << "Default vfs data file not present, found: " << old_vfs_data_file << " Old salt: " << old_salt << llendl; + } + } + + old_vfs_index_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_INDEX_FILE_BASE) + llformat("%u",old_salt); + + stat_result = LLFile::stat(old_vfs_index_file, &s); + if (stat_result) + { + // We've got a bad/missing index file, nukem! + LL_WARNS("AppCache") << "Bad or missing vfx index file " << old_vfs_index_file << LL_ENDL; + LL_WARNS("AppCache") << "Removing old vfs data file " << old_vfs_data_file << LL_ENDL; + LLFile::remove(old_vfs_data_file); + LLFile::remove(old_vfs_index_file); + + // Just in case, nuke any other old cache files in the directory. + std::string dir; + dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""); + + std::string mask; + mask = gDirUtilp->getDirDelimiter(); + mask += VFS_DATA_FILE_BASE; + mask += "*"; + + gDirUtilp->deleteFilesInDir(dir, mask); + + mask = gDirUtilp->getDirDelimiter(); + mask += VFS_INDEX_FILE_BASE; + mask += "*"; + + gDirUtilp->deleteFilesInDir(dir, mask); + } + + new_vfs_data_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,VFS_DATA_FILE_BASE) + llformat("%u",new_salt); + new_vfs_index_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, VFS_INDEX_FILE_BASE) + llformat("%u",new_salt); + + static_vfs_data_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"static_data.db2"); + static_vfs_index_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"static_index.db2"); + + if (resize_vfs) + { + LL_DEBUGS("AppCache") << "Removing old vfs and re-sizing" << LL_ENDL; + + LLFile::remove(old_vfs_data_file); + LLFile::remove(old_vfs_index_file); + } + else if (old_salt != new_salt) + { + // move the vfs files to a new name before opening + LL_DEBUGS("AppCache") << "Renaming " << old_vfs_data_file << " to " << new_vfs_data_file << LL_ENDL; + LL_DEBUGS("AppCache") << "Renaming " << old_vfs_index_file << " to " << new_vfs_index_file << LL_ENDL; + LLFile::rename(old_vfs_data_file, new_vfs_data_file); + LLFile::rename(old_vfs_index_file, new_vfs_index_file); + } + + // Startup the VFS... + gSavedSettings.setU32("VFSSalt", new_salt); + + // Don't remove VFS after viewer crashes. If user has corrupt data, they can reinstall. JC + gVFS = LLVFS::createLLVFS(new_vfs_index_file, new_vfs_data_file, false, vfs_size_u32, false); + if( !gVFS ) + { + return false; + } + + gStaticVFS = LLVFS::createLLVFS(static_vfs_index_file, static_vfs_data_file, true, 0, false); + if( !gStaticVFS ) + { + return false; + } + + BOOL success = gVFS->isValid() && gStaticVFS->isValid(); + if( !success ) + { + return false; + } + else + { + LLVFile::initClass(); + +#ifndef LL_RELEASE_FOR_DOWNLOAD + if (gSavedSettings.getBOOL("DumpVFSCaches")) + { + dumpVFSCaches(); + } +#endif + + return true; + } +} + +void LLAppViewer::purgeCache() +{ + LL_INFOS("AppCache") << "Purging Cache and Texture Cache..." << llendl; + LLAppViewer::getTextureCache()->purgeCache(LL_PATH_CACHE); + LLVOCache::getInstance()->removeCache(LL_PATH_CACHE); + std::string mask = gDirUtilp->getDirDelimiter() + "*.*"; + gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask); +} + +std::string LLAppViewer::getSecondLifeTitle() const +{ + return LLTrans::getString("APP_NAME"); +} + +std::string LLAppViewer::getWindowTitle() const +{ + return gWindowTitle; +} + +// Callback from a dialog indicating user was logged out. +bool finish_disconnect(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if (1 == option) + { + LLAppViewer::instance()->forceQuit(); + } + return false; +} + +// Callback from an early disconnect dialog, force an exit +bool finish_forced_disconnect(const LLSD& notification, const LLSD& response) +{ + LLAppViewer::instance()->forceQuit(); + return false; +} + + +void LLAppViewer::forceDisconnect(const std::string& mesg) +{ + if (gDoDisconnect) + { + // Already popped up one of these dialogs, don't + // do this again. + return; + } + + // *TODO: Translate the message if possible + std::string big_reason = LLAgent::sTeleportErrorMessages[mesg]; + if ( big_reason.size() == 0 ) + { + big_reason = mesg; + } + + LLSD args; + gDoDisconnect = TRUE; + + if (LLStartUp::getStartupState() < STATE_STARTED) + { + // Tell users what happened + args["ERROR_MESSAGE"] = big_reason; + LLNotificationsUtil::add("ErrorMessage", args, LLSD(), &finish_forced_disconnect); + } + else + { + args["MESSAGE"] = big_reason; + LLNotificationsUtil::add("YouHaveBeenLoggedOut", args, LLSD(), &finish_disconnect ); + } +} + +void LLAppViewer::badNetworkHandler() +{ + // Dump the packet + gMessageSystem->dumpPacketToLog(); + + // Flush all of our caches on exit in the case of disconnect due to + // invalid packets. + + mPurgeOnExit = TRUE; + + std::ostringstream message; + message << + "The viewer has detected mangled network data indicative\n" + "of a bad upstream network connection or an incomplete\n" + "local installation of " << LLAppViewer::instance()->getSecondLifeTitle() << ". \n" + " \n" + "Try uninstalling and reinstalling to see if this resolves \n" + "the issue. \n" + " \n" + "If the problem continues, see the Tech Support FAQ at: \n" + "www.secondlife.com/support"; + forceDisconnect(message.str()); + + LLApp::instance()->writeMiniDump(); +} + +// This routine may get called more than once during the shutdown process. +// This can happen because we need to get the screenshot before the window +// is destroyed. +void LLAppViewer::saveFinalSnapshot() +{ + if (!mSavedFinalSnapshot && !gNoRender) + { + gSavedSettings.setVector3d("FocusPosOnLogout", gAgentCamera.calcFocusPositionTargetGlobal()); + gSavedSettings.setVector3d("CameraPosOnLogout", gAgentCamera.calcCameraPositionTargetGlobal()); + gViewerWindow->setCursor(UI_CURSOR_WAIT); + gAgentCamera.changeCameraToThirdPerson( FALSE ); // don't animate, need immediate switch + gSavedSettings.setBOOL("ShowParcelOwners", FALSE); + idle(); + + std::string snap_filename = gDirUtilp->getLindenUserDir(); + snap_filename += gDirUtilp->getDirDelimiter(); + snap_filename += SCREEN_LAST_FILENAME; + // use full pixel dimensions of viewer window (not post-scale dimensions) + gViewerWindow->saveSnapshot(snap_filename, gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw(), FALSE, TRUE); + mSavedFinalSnapshot = TRUE; + } +} + +void LLAppViewer::loadNameCache() +{ + // display names cache + std::string filename = + gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); + LL_INFOS("AvNameCache") << filename << LL_ENDL; + llifstream name_cache_stream(filename); + if(name_cache_stream.is_open()) + { + LLAvatarNameCache::importFile(name_cache_stream); + } + + if (!gCacheName) return; + + std::string name_cache; + name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); + llifstream cache_file(name_cache); + if(cache_file.is_open()) + { + if(gCacheName->importFile(cache_file)) return; + } +} + +void LLAppViewer::saveNameCache() + { + // display names cache + std::string filename = + gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "avatar_name_cache.xml"); + llofstream name_cache_stream(filename); + if(name_cache_stream.is_open()) + { + LLAvatarNameCache::exportFile(name_cache_stream); +} + + if (!gCacheName) return; + + std::string name_cache; + name_cache = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "name.cache"); + llofstream cache_file(name_cache); + if(cache_file.is_open()) + { + gCacheName->exportFile(cache_file); + } +} + +/*! @brief This class is an LLFrameTimer that can be created with + an elapsed time that starts counting up from the given value + rather than 0.0. + + Otherwise it behaves the same way as LLFrameTimer. +*/ +class LLFrameStatsTimer : public LLFrameTimer +{ +public: + LLFrameStatsTimer(F64 elapsed_already = 0.0) + : LLFrameTimer() + { + mStartTime -= elapsed_already; + } +}; + +static LLFastTimer::DeclareTimer FTM_AUDIO_UPDATE("Update Audio"); +static LLFastTimer::DeclareTimer FTM_CLEANUP("Cleanup"); +static LLFastTimer::DeclareTimer FTM_IDLE_CB("Idle Callbacks"); +static LLFastTimer::DeclareTimer FTM_LOD_UPDATE("Update LOD"); +static LLFastTimer::DeclareTimer FTM_OBJECTLIST_UPDATE("Update Objectlist"); +static LLFastTimer::DeclareTimer FTM_REGION_UPDATE("Update Region"); +static LLFastTimer::DeclareTimer FTM_WORLD_UPDATE("Update World"); +static LLFastTimer::DeclareTimer FTM_NETWORK("Network"); + +/////////////////////////////////////////////////////// +// idle() +// +// Called every time the window is not doing anything. +// Receive packets, update statistics, and schedule a redisplay. +/////////////////////////////////////////////////////// +void LLAppViewer::idle() +{ + LLMemType mt_idle(LLMemType::MTYPE_IDLE); + pingMainloopTimeout("Main:Idle"); + + // Update frame timers + static LLTimer idle_timer; + + LLFrameTimer::updateFrameTime(); + LLFrameTimer::updateFrameCount(); + LLEventTimer::updateClass(); + LLCriticalDamp::updateInterpolants(); + LLMortician::updateClass(); + F32 dt_raw = idle_timer.getElapsedTimeAndResetF32(); + + // Cap out-of-control frame times + // Too low because in menus, swapping, debugger, etc. + // Too high because idle called with no objects in view, etc. + const F32 MIN_FRAME_RATE = 1.f; + const F32 MAX_FRAME_RATE = 200.f; + + F32 frame_rate_clamped = 1.f / dt_raw; + frame_rate_clamped = llclamp(frame_rate_clamped, MIN_FRAME_RATE, MAX_FRAME_RATE); + gFrameDTClamped = 1.f / frame_rate_clamped; + + // Global frame timer + // Smoothly weight toward current frame + gFPSClamped = (frame_rate_clamped + (4.f * gFPSClamped)) / 5.f; + + F32 qas = gSavedSettings.getF32("QuitAfterSeconds"); + if (qas > 0.f) + { + if (gRenderStartTime.getElapsedTimeF32() > qas) + { + LLAppViewer::instance()->forceQuit(); + } + } + + // debug setting to quit after N seconds of being AFK - 0 to never do this + F32 qas_afk = gSavedSettings.getF32("QuitAfterSecondsOfAFK"); + if (qas_afk > 0.f) + { + // idle time is more than setting + if ( gAwayTriggerTimer.getElapsedTimeF32() > qas_afk ) + { + // go ahead and just quit gracefully + LLAppViewer::instance()->requestQuit(); + } + } + + // Must wait until both have avatar object and mute list, so poll + // here. + request_initial_instant_messages(); + + /////////////////////////////////// + // + // Special case idle if still starting up + // + if (LLStartUp::getStartupState() < STATE_STARTED) + { + // Skip rest if idle startup returns false (essentially, no world yet) + gGLActive = TRUE; + if (!idle_startup()) + { + gGLActive = FALSE; + return; + } + gGLActive = FALSE; + } + + + F32 yaw = 0.f; // radians + + if (!gDisconnected) + { + LLFastTimer t(FTM_NETWORK); + // Update spaceserver timeinfo + LLWorld::getInstance()->setSpaceTimeUSec(LLWorld::getInstance()->getSpaceTimeUSec() + (U32)(dt_raw * SEC_TO_MICROSEC)); + + + ////////////////////////////////////// + // + // Update simulator agent state + // + + if (gSavedSettings.getBOOL("RotateRight")) + { + gAgent.moveYaw(-1.f); + } + + { + LLFastTimer t(FTM_AGENT_AUTOPILOT); + // Handle automatic walking towards points + gAgentPilot.updateTarget(); + gAgent.autoPilot(&yaw); + } + + static LLFrameTimer agent_update_timer; + static U32 last_control_flags; + + // When appropriate, update agent location to the simulator. + F32 agent_update_time = agent_update_timer.getElapsedTimeF32(); + BOOL flags_changed = gAgent.controlFlagsDirty() || (last_control_flags != gAgent.getControlFlags()); + + if (flags_changed || (agent_update_time > (1.0f / (F32) AGENT_UPDATES_PER_SECOND))) + { + LLFastTimer t(FTM_AGENT_UPDATE); + // Send avatar and camera info + last_control_flags = gAgent.getControlFlags(); + send_agent_update(TRUE); + agent_update_timer.reset(); + } + } + + ////////////////////////////////////// + // + // Manage statistics + // + // + { + // Initialize the viewer_stats_timer with an already elapsed time + // of SEND_STATS_PERIOD so that the initial stats report will + // be sent immediately. + static LLFrameStatsTimer viewer_stats_timer(SEND_STATS_PERIOD); + reset_statistics(); + + // Update session stats every large chunk of time + // *FIX: (???) SAMANTHA + if (viewer_stats_timer.getElapsedTimeF32() >= SEND_STATS_PERIOD && !gDisconnected) + { + llinfos << "Transmitting sessions stats" << llendl; + send_stats(); + viewer_stats_timer.reset(); + } + + // Print the object debugging stats + static LLFrameTimer object_debug_timer; + if (object_debug_timer.getElapsedTimeF32() > 5.f) + { + object_debug_timer.reset(); + if (gObjectList.mNumDeadObjectUpdates) + { + llinfos << "Dead object updates: " << gObjectList.mNumDeadObjectUpdates << llendl; + gObjectList.mNumDeadObjectUpdates = 0; + } + if (gObjectList.mNumUnknownKills) + { + llinfos << "Kills on unknown objects: " << gObjectList.mNumUnknownKills << llendl; + gObjectList.mNumUnknownKills = 0; + } + if (gObjectList.mNumUnknownUpdates) + { + llinfos << "Unknown object updates: " << gObjectList.mNumUnknownUpdates << llendl; + gObjectList.mNumUnknownUpdates = 0; + } + + // ViewerMetrics FPS piggy-backing on the debug timer. + // The 5-second interval is nice for this purpose. If the object debug + // bit moves or is disabled, please give this a suitable home. + LLViewerAssetStatsFF::record_fps_main(gFPSClamped); + } + } + + if (!gDisconnected) + { + LLFastTimer t(FTM_NETWORK); + + //////////////////////////////////////////////// + // + // Network processing + // + // NOTE: Starting at this point, we may still have pointers to "dead" objects + // floating throughout the various object lists. + // + idleNameCache(); + + idleNetwork(); + + + // Check for away from keyboard, kick idle agents. + idle_afk_check(); + + // Update statistics for this frame + update_statistics(gFrameCount); + } + + //////////////////////////////////////// + // + // Handle the regular UI idle callbacks as well as + // hover callbacks + // + + { +// LLFastTimer t(FTM_IDLE_CB); + + // Do event notifications if necessary. Yes, we may want to move this elsewhere. + gEventNotifier.update(); + + gIdleCallbacks.callFunctions(); + gInventory.idleNotifyObservers(); + } + + // Metrics logging (LLViewerAssetStats, etc.) + { + static LLTimer report_interval; + + // *TODO: Add configuration controls for this + if (report_interval.getElapsedTimeF32() >= app_metrics_interval) + { + metricsSend(! gDisconnected); + report_interval.reset(); + } + } + + if (gDisconnected) + { + return; + } + + gViewerWindow->updateUI(); + + /////////////////////////////////////// + // Agent and camera movement + // + LLCoordGL current_mouse = gViewerWindow->getCurrentMouse(); + + { + // After agent and camera moved, figure out if we need to + // deselect objects. + LLSelectMgr::getInstance()->deselectAllIfTooFar(); + + } + + { + // Handle pending gesture processing + static LLFastTimer::DeclareTimer ftm("Agent Position"); + LLFastTimer t(ftm); + LLGestureMgr::instance().update(); + + gAgent.updateAgentPosition(gFrameDTClamped, yaw, current_mouse.mX, current_mouse.mY); + } + + { + LLFastTimer t(FTM_OBJECTLIST_UPDATE); + + if (!(logoutRequestSent() && hasSavedFinalSnapshot())) + { + gObjectList.update(gAgent, *LLWorld::getInstance()); + } + } + + ////////////////////////////////////// + // + // Deletes objects... + // Has to be done after doing idleUpdates (which can kill objects) + // + + { + LLFastTimer t(FTM_CLEANUP); + gObjectList.cleanDeadObjects(); + LLDrawable::cleanupDeadDrawables(); + } + + // + // After this point, in theory we should never see a dead object + // in the various object/drawable lists. + // + + ////////////////////////////////////// + // + // Update/send HUD effects + // + // At this point, HUD effects may clean up some references to + // dead objects. + // + + { + static LLFastTimer::DeclareTimer ftm("HUD Effects"); + LLFastTimer t(ftm); + LLSelectMgr::getInstance()->updateEffects(); + LLHUDManager::getInstance()->cleanupEffects(); + LLHUDManager::getInstance()->sendEffects(); + } + + //////////////////////////////////////// + // + // Unpack layer data that we've received + // + + { + LLFastTimer t(FTM_NETWORK); + gVLManager.unpackData(); + } + + ///////////////////////// + // + // Update surfaces, and surface textures as well. + // + + LLWorld::getInstance()->updateVisibilities(); + { + const F32 max_region_update_time = .001f; // 1ms + LLFastTimer t(FTM_REGION_UPDATE); + LLWorld::getInstance()->updateRegions(max_region_update_time); + } + + ///////////////////////// + // + // Update weather effects + // + if (!gNoRender) + { + LLWorld::getInstance()->updateClouds(gFrameDTClamped); + gSky.propagateHeavenlyBodies(gFrameDTClamped); // moves sun, moon, and planets + + // Update wind vector + LLVector3 wind_position_region; + static LLVector3 average_wind; + + LLViewerRegion *regionp; + regionp = LLWorld::getInstance()->resolveRegionGlobal(wind_position_region, gAgent.getPositionGlobal()); // puts agent's local coords into wind_position + if (regionp) + { + gWindVec = regionp->mWind.getVelocity(wind_position_region); + + // Compute average wind and use to drive motion of water + + average_wind = regionp->mWind.getAverage(); + F32 cloud_density = regionp->mCloudLayer.getDensityRegion(wind_position_region); + + gSky.setCloudDensityAtAgent(cloud_density); + gSky.setWind(average_wind); + //LLVOWater::setWind(average_wind); + } + else + { + gWindVec.setVec(0.0f, 0.0f, 0.0f); + } + } + + ////////////////////////////////////// + // + // Sort and cull in the new renderer are moved to pipeline.cpp + // Here, particles are updated and drawables are moved. + // + + if (!gNoRender) + { + LLFastTimer t(FTM_WORLD_UPDATE); + gPipeline.updateMove(); + + LLWorld::getInstance()->updateParticles(); + } + + if (LLViewerJoystick::getInstance()->getOverrideCamera()) + { + LLViewerJoystick::getInstance()->moveFlycam(); + } + else + { + if (LLToolMgr::getInstance()->inBuildMode()) + { + LLViewerJoystick::getInstance()->moveObjects(); + } + + gAgentCamera.updateCamera(); + } + + // update media focus + LLViewerMediaFocus::getInstance()->update(); + + // objects and camera should be in sync, do LOD calculations now + { + LLFastTimer t(FTM_LOD_UPDATE); + gObjectList.updateApparentAngles(gAgent); + } + + { + LLFastTimer t(FTM_AUDIO_UPDATE); + + if (gAudiop) + { + audio_update_volume(false); + audio_update_listener(); + audio_update_wind(false); + + // this line actually commits the changes we've made to source positions, etc. + const F32 max_audio_decode_time = 0.002f; // 2 ms decode time + gAudiop->idle(max_audio_decode_time); + } + } + + // Handle shutdown process, for example, + // wait for floaters to close, send quit message, + // forcibly quit if it has taken too long + if (mQuitRequested) + { + gGLActive = TRUE; + idleShutdown(); + } +} + +void LLAppViewer::idleShutdown() +{ + // Wait for all modal alerts to get resolved + if (LLModalDialog::activeCount() > 0) + { + return; + } + + // close IM interface + if(gIMMgr) + { + gIMMgr->disconnectAllSessions(); + } + + // Wait for all floaters to get resolved + if (gFloaterView + && !gFloaterView->allChildrenClosed()) + { + return; + } + + if (LLSideTray::getInstance()->notifyChildren(LLSD().with("request","wait_quit"))) + { + return; + } + + + + // ProductEngine: Try moving this code to where we shut down sTextureCache in cleanup() + // *TODO: ugly + static bool saved_teleport_history = false; + if (!saved_teleport_history) + { + saved_teleport_history = true; + LLTeleportHistory::getInstance()->dump(); + LLLocationHistory::getInstance()->save(); // *TODO: find a better place for doing this + return; + } + + static bool saved_snapshot = false; + if (!saved_snapshot) + { + saved_snapshot = true; + saveFinalSnapshot(); + return; + } + + const F32 SHUTDOWN_UPLOAD_SAVE_TIME = 5.f; + + S32 pending_uploads = gAssetStorage->getNumPendingUploads(); + if (pending_uploads > 0 + && gLogoutTimer.getElapsedTimeF32() < SHUTDOWN_UPLOAD_SAVE_TIME + && !logoutRequestSent()) + { + static S32 total_uploads = 0; + // Sometimes total upload count can change during logout. + total_uploads = llmax(total_uploads, pending_uploads); + gViewerWindow->setShowProgress(TRUE); + S32 finished_uploads = total_uploads - pending_uploads; + F32 percent = 100.f * finished_uploads / total_uploads; + gViewerWindow->setProgressPercent(percent); + gViewerWindow->setProgressString(LLTrans::getString("SavingSettings")); + return; + } + + // All floaters are closed. Tell server we want to quit. + if( !logoutRequestSent() ) + { + sendLogoutRequest(); + + // Wait for a LogoutReply message + gViewerWindow->setShowProgress(TRUE); + gViewerWindow->setProgressPercent(100.f); + gViewerWindow->setProgressString(LLTrans::getString("LoggingOut")); + return; + } + + // Make sure that we quit if we haven't received a reply from the server. + if( logoutRequestSent() + && gLogoutTimer.getElapsedTimeF32() > gLogoutMaxTime ) + { + forceQuit(); + return; + } +} + +void LLAppViewer::sendLogoutRequest() +{ + if(!mLogoutRequestSent) + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_LogoutRequest); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gAgent.sendReliableMessage(); + + gLogoutTimer.reset(); + gLogoutMaxTime = LOGOUT_REQUEST_TIME; + mLogoutRequestSent = TRUE; + + if(LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->leaveChannel(); + } + + //Set internal status variables and marker files + gLogoutInProgress = TRUE; + mLogoutMarkerFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,LOGOUT_MARKER_FILE_NAME); + + LLAPRFile outfile ; + outfile.open(mLogoutMarkerFileName, LL_APR_W); + mLogoutMarkerFile = outfile.getFileHandle() ; + if (mLogoutMarkerFile) + { + llinfos << "Created logout marker file " << mLogoutMarkerFileName << llendl; + apr_file_close(mLogoutMarkerFile); + } + else + { + llwarns << "Cannot create logout marker file " << mLogoutMarkerFileName << llendl; + } + } +} + +void LLAppViewer::idleNameCache() +{ + // Neither old nor new name cache can function before agent has a region + LLViewerRegion* region = gAgent.getRegion(); + if (!region) return; + + // deal with any queued name requests and replies. + gCacheName->processPending(); + + // Can't run the new cache until we have the list of capabilities + // for the agent region, and can therefore decide whether to use + // display names or fall back to the old name system. + if (!region->capabilitiesReceived()) return; + + // Agent may have moved to a different region, so need to update cap URL + // for name lookups. Can't do this in the cap grant code, as caps are + // granted to neighbor regions before the main agent gets there. Can't + // do it in the move-into-region code because cap not guaranteed to be + // granted yet, for example on teleport. + bool had_capability = LLAvatarNameCache::hasNameLookupURL(); + std::string name_lookup_url; + name_lookup_url.reserve(128); // avoid a memory allocation below + name_lookup_url = region->getCapability("GetDisplayNames"); + bool have_capability = !name_lookup_url.empty(); + if (have_capability) + { + // we have support for display names, use it + U32 url_size = name_lookup_url.size(); + // capabilities require URLs with slashes before query params: + // https://:/cap//?ids= + // but the caps are granted like: + // https://:/cap/ + if (url_size > 0 && name_lookup_url[url_size-1] != '/') + { + name_lookup_url += '/'; + } + LLAvatarNameCache::setNameLookupURL(name_lookup_url); + } + else + { + // Display names not available on this region + LLAvatarNameCache::setNameLookupURL( std::string() ); + } + + // Error recovery - did we change state? + if (had_capability != have_capability) + { + // name tags are persistant on screen, so make sure they refresh + LLVOAvatar::invalidateNameTags(); + } + + LLAvatarNameCache::idle(); +} + +// +// Handle messages, and all message related stuff +// + +#define TIME_THROTTLE_MESSAGES + +#ifdef TIME_THROTTLE_MESSAGES +#define CHECK_MESSAGES_DEFAULT_MAX_TIME .020f // 50 ms = 50 fps (just for messages!) +static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; +#endif + +static LLFastTimer::DeclareTimer FTM_IDLE_NETWORK("Idle Network"); + +void LLAppViewer::idleNetwork() +{ + LLMemType mt_in(LLMemType::MTYPE_IDLE_NETWORK); + pingMainloopTimeout("idleNetwork"); + + gObjectList.mNumNewObjects = 0; + S32 total_decoded = 0; + + if (!gSavedSettings.getBOOL("SpeedTest")) + { + LLFastTimer t(FTM_IDLE_NETWORK); // decode + + LLTimer check_message_timer; + // Read all available packets from network + const S64 frame_count = gFrameCount; // U32->S64 + F32 total_time = 0.0f; + + while (gMessageSystem->checkAllMessages(frame_count, gServicePump)) + { + if (gDoDisconnect) + { + // We're disconnecting, don't process any more messages from the server + // We're usually disconnecting due to either network corruption or a + // server going down, so this is OK. + break; + } + + total_decoded++; + gPacketsIn++; + + if (total_decoded > MESSAGE_MAX_PER_FRAME) + { + break; + } + +#ifdef TIME_THROTTLE_MESSAGES + // Prevent slow packets from completely destroying the frame rate. + // This usually happens due to clumps of avatars taking huge amount + // of network processing time (which needs to be fixed, but this is + // a good limit anyway). + total_time = check_message_timer.getElapsedTimeF32(); + if (total_time >= CheckMessagesMaxTime) + break; +#endif + } + + // Handle per-frame message system processing. + gMessageSystem->processAcks(); + +#ifdef TIME_THROTTLE_MESSAGES + if (total_time >= CheckMessagesMaxTime) + { + // Increase CheckMessagesMaxTime so that we will eventually catch up + CheckMessagesMaxTime *= 1.035f; // 3.5% ~= x2 in 20 frames, ~8x in 60 frames + } + else + { + // Reset CheckMessagesMaxTime to default value + CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; + } +#endif + + + + // we want to clear the control after sending out all necessary agent updates + gAgent.resetControlFlags(); + + // Decode enqueued messages... + S32 remaining_possible_decodes = MESSAGE_MAX_PER_FRAME - total_decoded; + + if( remaining_possible_decodes <= 0 ) + { + llinfos << "Maxed out number of messages per frame at " << MESSAGE_MAX_PER_FRAME << llendl; + } + + if (gPrintMessagesThisFrame) + { + llinfos << "Decoded " << total_decoded << " msgs this frame!" << llendl; + gPrintMessagesThisFrame = FALSE; + } + } + LLViewerStats::getInstance()->mNumNewObjectsStat.addValue(gObjectList.mNumNewObjects); + + // Retransmit unacknowledged packets. + gXferManager->retransmitUnackedPackets(); + gAssetStorage->checkForTimeouts(); + gViewerThrottle.updateDynamicThrottle(); + + // Check that the circuit between the viewer and the agent's current + // region is still alive + LLViewerRegion *agent_region = gAgent.getRegion(); + if (agent_region && (LLStartUp::getStartupState()==STATE_STARTED)) + { + LLUUID this_region_id = agent_region->getRegionID(); + bool this_region_alive = agent_region->isAlive(); + if ((mAgentRegionLastAlive && !this_region_alive) // newly dead + && (mAgentRegionLastID == this_region_id)) // same region + { + forceDisconnect(LLTrans::getString("AgentLostConnection")); + } + mAgentRegionLastID = this_region_id; + mAgentRegionLastAlive = this_region_alive; + } +} + +void LLAppViewer::disconnectViewer() +{ + if (gDisconnected) + { + return; + } + // + // Cleanup after quitting. + // + // Save snapshot for next time, if we made it through initialization + + llinfos << "Disconnecting viewer!" << llendl; + + // Dump our frame statistics + + // Remember if we were flying + gSavedSettings.setBOOL("FlyingAtExit", gAgent.getFlying() ); + + // Un-minimize all windows so they don't get saved minimized + if (!gNoRender) + { + if (gFloaterView) + { + gFloaterView->restoreAll(); + } + } + + if (LLSelectMgr::getInstance()) + { + LLSelectMgr::getInstance()->deselectAll(); + } + + // save inventory if appropriate + gInventory.cache(gInventory.getRootFolderID(), gAgent.getID()); + if (gInventory.getLibraryRootFolderID().notNull() + && gInventory.getLibraryOwnerID().notNull()) + { + gInventory.cache( + gInventory.getLibraryRootFolderID(), + gInventory.getLibraryOwnerID()); + } + + saveNameCache(); + + // close inventory interface, close all windows + LLFloaterInventory::cleanup(); + + gAgentWearables.cleanup(); + gAgentCamera.cleanup(); + // Also writes cached agent settings to gSavedSettings + gAgent.cleanup(); + + // This is where we used to call gObjectList.destroy() and then delete gWorldp. + // Now we just ask the LLWorld singleton to cleanly shut down. + if(LLWorld::instanceExists()) + { + LLWorld::getInstance()->destroyClass(); + } + + // call all self-registered classes + LLDestroyClassList::instance().fireCallbacks(); + + cleanup_xfer_manager(); + gDisconnected = TRUE; + + // Pass the connection state to LLUrlEntryParcel not to attempt + // parcel info requests while disconnected. + LLUrlEntryParcel::setDisconnected(gDisconnected); +} + +void LLAppViewer::forceErrorLLError() +{ + llerrs << "This is an llerror" << llendl; +} + +void LLAppViewer::forceErrorBreakpoint() +{ +#ifdef LL_WINDOWS + DebugBreak(); +#endif + return; +} + +void LLAppViewer::forceErrorBadMemoryAccess() +{ + S32* crash = NULL; + *crash = 0xDEADBEEF; + return; +} + +void LLAppViewer::forceErrorInfiniteLoop() +{ + while(true) + { + ; + } + return; +} + +void LLAppViewer::forceErrorSoftwareException() +{ + // *FIX: Any way to insure it won't be handled? + throw; +} + +void LLAppViewer::forceErrorDriverCrash() +{ + glDeleteTextures(1, NULL); +} + +void LLAppViewer::initMainloopTimeout(const std::string& state, F32 secs) +{ + if(!mMainloopTimeout) + { + mMainloopTimeout = new LLWatchdogTimeout(); + resumeMainloopTimeout(state, secs); + } +} + +void LLAppViewer::destroyMainloopTimeout() +{ + if(mMainloopTimeout) + { + delete mMainloopTimeout; + mMainloopTimeout = NULL; + } +} + +void LLAppViewer::resumeMainloopTimeout(const std::string& state, F32 secs) +{ + if(mMainloopTimeout) + { + if(secs < 0.0f) + { + secs = gSavedSettings.getF32("MainloopTimeoutDefault"); + } + + mMainloopTimeout->setTimeout(secs); + mMainloopTimeout->start(state); + } +} + +void LLAppViewer::pauseMainloopTimeout() +{ + if(mMainloopTimeout) + { + mMainloopTimeout->stop(); + } +} + +void LLAppViewer::pingMainloopTimeout(const std::string& state, F32 secs) +{ +// if(!restoreErrorTrap()) +// { +// llwarns << "!!!!!!!!!!!!! Its an error trap!!!!" << state << llendl; +// } + + if(mMainloopTimeout) + { + if(secs < 0.0f) + { + secs = gSavedSettings.getF32("MainloopTimeoutDefault"); + } + + mMainloopTimeout->setTimeout(secs); + mMainloopTimeout->ping(state); + } +} + +void LLAppViewer::handleLoginComplete() +{ + gLoggedInTime.start(); + initMainloopTimeout("Mainloop Init"); + + // Store some data to DebugInfo in case of a freeze. + gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel(); + + gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor(); + gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); + gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch(); + gDebugInfo["ClientInfo"]["BuildVersion"] = LLVersionInfo::getBuild(); + + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if ( parcel && parcel->getMusicURL()[0]) + { + gDebugInfo["ParcelMusicURL"] = parcel->getMusicURL(); + } + if ( parcel && parcel->getMediaURL()[0]) + { + gDebugInfo["ParcelMediaURL"] = parcel->getMediaURL(); + } + + gDebugInfo["SettingsFilename"] = gSavedSettings.getString("ClientSettingsFile"); + gDebugInfo["CAFilename"] = gDirUtilp->getCAFile(); + gDebugInfo["ViewerExePath"] = gDirUtilp->getExecutablePathAndName(); + gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath(); + + if(gAgent.getRegion()) + { + gDebugInfo["CurrentSimHost"] = gAgent.getRegionHost().getHostName(); + gDebugInfo["CurrentRegion"] = gAgent.getRegion()->getName(); + } + + if(LLAppViewer::instance()->mMainloopTimeout) + { + gDebugInfo["MainloopTimeoutState"] = LLAppViewer::instance()->mMainloopTimeout->getState(); + } + + mOnLoginCompleted(); + + writeDebugInfo(); +} + +// *TODO - generalize this and move DSO wrangling to a helper class -brad +void LLAppViewer::loadEventHostModule(S32 listen_port) +{ + std::string dso_name = +#if LL_WINDOWS + "lleventhost.dll"; +#elif LL_DARWIN + "liblleventhost.dylib"; +#else + "liblleventhost.so"; +#endif + + std::string dso_path = gDirUtilp->findFile(dso_name, + gDirUtilp->getAppRODataDir(), + gDirUtilp->getExecutableDir()); + + if(dso_path == "") + { + llerrs << "QAModeEventHost requested but module \"" << dso_name << "\" not found!" << llendl; + return; + } + + LL_INFOS("eventhost") << "Found lleventhost at '" << dso_path << "'" << LL_ENDL; +#if ! defined(LL_WINDOWS) + { + std::string outfile("/tmp/lleventhost.file.out"); + std::string command("file '" + dso_path + "' > '" + outfile + "' 2>&1"); + int rc = system(command.c_str()); + if (rc != 0) + { + LL_WARNS("eventhost") << command << " ==> " << rc << ':' << LL_ENDL; + } + else + { + LL_INFOS("eventhost") << command << ':' << LL_ENDL; + } + { + std::ifstream reader(outfile.c_str()); + std::string line; + while (std::getline(reader, line)) + { + size_t len = line.length(); + if (len && line[len-1] == '\n') + line.erase(len-1); + LL_INFOS("eventhost") << line << LL_ENDL; + } + } + remove(outfile.c_str()); + } +#endif // LL_WINDOWS + + apr_dso_handle_t * eventhost_dso_handle = NULL; + apr_pool_t * eventhost_dso_memory_pool = NULL; + + //attempt to load the shared library + apr_pool_create(&eventhost_dso_memory_pool, NULL); + apr_status_t rv = apr_dso_load(&eventhost_dso_handle, + dso_path.c_str(), + eventhost_dso_memory_pool); + llassert_always(! ll_apr_warn_status(rv, eventhost_dso_handle)); + llassert_always(eventhost_dso_handle != NULL); + + int (*ll_plugin_start_func)(LLSD const &) = NULL; + rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll_plugin_start_func, eventhost_dso_handle, "ll_plugin_start"); + + llassert_always(! ll_apr_warn_status(rv, eventhost_dso_handle)); + llassert_always(ll_plugin_start_func != NULL); + + LLSD args; + args["listen_port"] = listen_port; + + int status = ll_plugin_start_func(args); + + if(status != 0) + { + llerrs << "problem loading eventhost plugin, status: " << status << llendl; + } + + mPlugins.insert(eventhost_dso_handle); +} + +void LLAppViewer::launchUpdater() +{ + LLSD query_map = LLSD::emptyMap(); + // *TODO place os string in a global constant +#if LL_WINDOWS + query_map["os"] = "win"; +#elif LL_DARWIN + query_map["os"] = "mac"; +#elif LL_LINUX + query_map["os"] = "lnx"; +#elif LL_SOLARIS + query_map["os"] = "sol"; +#endif + // *TODO change userserver to be grid on both viewer and sim, since + // userserver no longer exists. + query_map["userserver"] = LLGridManager::getInstance()->getGridLabel(); + query_map["channel"] = LLVersionInfo::getChannel(); + // *TODO constantize this guy + // *NOTE: This URL is also used in win_setup/lldownloader.cpp + LLURI update_url = LLURI::buildHTTP("secondlife.com", 80, "update.php", query_map); + + if(LLAppViewer::sUpdaterInfo) + { + delete LLAppViewer::sUpdaterInfo; + } + LLAppViewer::sUpdaterInfo = new LLAppViewer::LLUpdaterInfo() ; + + // if a sim name was passed in via command line parameter (typically through a SLURL) + if ( LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION ) + { + // record the location to start at next time + gSavedSettings.setString( "NextLoginLocation", LLStartUp::getStartSLURL().getSLURLString()); + }; + +#if LL_WINDOWS + LLAppViewer::sUpdaterInfo->mUpdateExePath = gDirUtilp->getTempFilename(); + if (LLAppViewer::sUpdaterInfo->mUpdateExePath.empty()) + { + delete LLAppViewer::sUpdaterInfo ; + LLAppViewer::sUpdaterInfo = NULL ; + + // We're hosed, bail + LL_WARNS("AppInit") << "LLDir::getTempFilename() failed" << LL_ENDL; + return; + } + + LLAppViewer::sUpdaterInfo->mUpdateExePath += ".exe"; + + std::string updater_source = gDirUtilp->getAppRODataDir(); + updater_source += gDirUtilp->getDirDelimiter(); + updater_source += "updater.exe"; + + LL_DEBUGS("AppInit") << "Calling CopyFile source: " << updater_source + << " dest: " << LLAppViewer::sUpdaterInfo->mUpdateExePath + << LL_ENDL; + + + if (!CopyFileA(updater_source.c_str(), LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), FALSE)) + { + delete LLAppViewer::sUpdaterInfo ; + LLAppViewer::sUpdaterInfo = NULL ; + + LL_WARNS("AppInit") << "Unable to copy the updater!" << LL_ENDL; + + return; + } + + LLAppViewer::sUpdaterInfo->mParams << "-url \"" << update_url.asString() << "\""; + + LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << " " << LLAppViewer::sUpdaterInfo->mParams.str() << LL_ENDL; + + //Explicitly remove the marker file, otherwise we pass the lock onto the child process and things get weird. + LLAppViewer::instance()->removeMarkerFile(); // In case updater fails + + // *NOTE:Mani The updater is spawned as the last thing before the WinMain exit. + // see LLAppViewerWin32.cpp + +#elif LL_DARWIN + LLAppViewer::sUpdaterInfo->mUpdateExePath = "'"; + LLAppViewer::sUpdaterInfo->mUpdateExePath += gDirUtilp->getAppRODataDir(); + LLAppViewer::sUpdaterInfo->mUpdateExePath += "/mac-updater.app/Contents/MacOS/mac-updater' -url \""; + LLAppViewer::sUpdaterInfo->mUpdateExePath += update_url.asString(); + LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -name \""; + LLAppViewer::sUpdaterInfo->mUpdateExePath += LLAppViewer::instance()->getSecondLifeTitle(); + LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -bundleid \""; + LLAppViewer::sUpdaterInfo->mUpdateExePath += LL_VERSION_BUNDLE_ID; + LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" &"; + + LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << LL_ENDL; + + // Run the auto-updater. + system(LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str()); /* Flawfinder: ignore */ + +#elif (LL_LINUX || LL_SOLARIS) && LL_GTK + // we tell the updater where to find the xml containing string + // translations which it can use for its own UI + std::string xml_strings_file = "strings.xml"; + std::vector xui_path_vec = LLUI::getXUIPaths(); + std::string xml_search_paths; + std::vector::const_iterator iter; + // build comma-delimited list of xml paths to pass to updater + for (iter = xui_path_vec.begin(); iter != xui_path_vec.end(); ) + { + std::string this_skin_dir = gDirUtilp->getDefaultSkinDir() + + gDirUtilp->getDirDelimiter() + + (*iter); + llinfos << "Got a XUI path: " << this_skin_dir << llendl; + xml_search_paths.append(this_skin_dir); + ++iter; + if (iter != xui_path_vec.end()) + xml_search_paths.append(","); // comma-delimit + } + // build the overall command-line to run the updater correctly + LLAppViewer::sUpdaterInfo->mUpdateExePath = + gDirUtilp->getExecutableDir() + "/" + "linux-updater.bin" + + " --url \"" + update_url.asString() + "\"" + + " --name \"" + LLAppViewer::instance()->getSecondLifeTitle() + "\"" + + " --dest \"" + gDirUtilp->getAppRODataDir() + "\"" + + " --stringsdir \"" + xml_search_paths + "\"" + + " --stringsfile \"" + xml_strings_file + "\""; + + LL_INFOS("AppInit") << "Calling updater: " + << LLAppViewer::sUpdaterInfo->mUpdateExePath << LL_ENDL; + + // *TODO: we could use the gdk equivalent to ensure the updater + // gets started on the same screen. + GError *error = NULL; + if (!g_spawn_command_line_async(LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), &error)) + { + llerrs << "Failed to launch updater: " + << error->message + << llendl; + } + if (error) { + g_error_free(error); + } +#else + OSMessageBox(LLTrans::getString("MBNoAutoUpdate"), LLStringUtil::null, OSMB_OK); +#endif + + // *REMOVE:Mani - Saving for reference... + // LLAppViewer::instance()->forceQuit(); +} + + +//virtual +void LLAppViewer::setMasterSystemAudioMute(bool mute) +{ + gSavedSettings.setBOOL("MuteAudio", mute); +} + +//virtual +bool LLAppViewer::getMasterSystemAudioMute() +{ + return gSavedSettings.getBOOL("MuteAudio"); +} + +//---------------------------------------------------------------------------- +// Metrics-related methods (static and otherwise) +//---------------------------------------------------------------------------- + +/** + * LLViewerAssetStats collects data on a per-region (as defined by the agent's + * location) so we need to tell it about region changes which become a kind of + * hidden variable/global state in the collectors. For collectors not running + * on the main thread, we need to send a message to move the data over safely + * and cheaply (amortized over a run). + */ +void LLAppViewer::metricsUpdateRegion(U64 region_handle) +{ + if (0 != region_handle) + { + LLViewerAssetStatsFF::set_region_main(region_handle); + if (LLAppViewer::sTextureFetch) + { + // Send a region update message into 'thread1' to get the new region. + LLAppViewer::sTextureFetch->commandSetRegion(region_handle); + } + else + { + // No 'thread1', a.k.a. TextureFetch, so update directly + LLViewerAssetStatsFF::set_region_thread1(region_handle); + } + } +} + + +/** + * Attempts to start a multi-threaded metrics report to be sent back to + * the grid for consumption. + */ +void LLAppViewer::metricsSend(bool enable_reporting) +{ + if (! gViewerAssetStatsMain) + return; + + if (LLAppViewer::sTextureFetch) + { + LLViewerRegion * regionp = gAgent.getRegion(); + + if (enable_reporting && regionp) + { + std::string caps_url = regionp->getCapability("ViewerMetrics"); + + // Make a copy of the main stats to send into another thread. + // Receiving thread takes ownership. + LLViewerAssetStats * main_stats(new LLViewerAssetStats(*gViewerAssetStatsMain)); + + // Send a report request into 'thread1' to get the rest of the data + // and provide some additional parameters while here. + LLAppViewer::sTextureFetch->commandSendMetrics(caps_url, + gAgentSessionID, + gAgentID, + main_stats); + main_stats = 0; // Ownership transferred + } + else + { + LLAppViewer::sTextureFetch->commandDataBreak(); + } + } + + // Reset even if we can't report. Rather than gather up a huge chunk of + // data, we'll keep to our sampling interval and retain the data + // resolution in time. + gViewerAssetStatsMain->reset(); +} + diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index ff7dfccc0a..771419f60a 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -240,6 +240,9 @@ void LLAvatarList::addAvalineItem(const LLUUID& item_id, const LLUUID& session_i LLAvalineListItem* item = new LLAvalineListItem(/*hide_number=*/false); item->setAvatarId(item_id, session_id, true, false); item->setName(item_name); + item->showLastInteractionTime(mShowLastInteractionTime); + item->showSpeakingIndicator(mShowSpeakingIndicator); + item->setOnline(false); addItem(item, item_id); mIDs.push_back(item_id); @@ -286,9 +289,18 @@ void LLAvatarList::refresh() { // *NOTE: If you change the UI to show a different string, // be sure to change the filter code below. - addNewItem(buddy_id, - av_name.mDisplayName.empty() ? waiting_str : av_name.mDisplayName, - LLAvatarTracker::instance().isBuddyOnline(buddy_id)); + if (LLRecentPeople::instance().isAvalineCaller(buddy_id)) + { + const LLSD& call_data = LLRecentPeople::instance().getData(buddy_id); + addAvalineItem(buddy_id, call_data["session_id"].asUUID(), call_data["call_number"].asString()); + } + else + { + addNewItem(buddy_id, + av_name.mDisplayName.empty() ? waiting_str : av_name.mDisplayName, + LLAvatarTracker::instance().isBuddyOnline(buddy_id)); + } + modified = true; nadded++; } @@ -440,7 +452,7 @@ void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, BOOL is BOOL LLAvatarList::handleRightMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); - if ( mContextMenu ) + if ( mContextMenu && !isAvalineItemSelected()) { uuid_vec_t selected_uuids; getSelectedUUIDs(selected_uuids); @@ -449,6 +461,21 @@ BOOL LLAvatarList::handleRightMouseDown(S32 x, S32 y, MASK mask) return handled; } +bool LLAvatarList::isAvalineItemSelected() +{ + std::vector selected_items; + getSelectedItems(selected_items); + std::vector::iterator it = selected_items.begin(); + + for(; it != selected_items.end(); ++it) + { + if (dynamic_cast(*it)) + return true; + } + + return false; +} + void LLAvatarList::setVisible(BOOL visible) { if ( visible == FALSE && mContextMenu ) diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h index cacbcf7244..4814a88a79 100644 --- a/indra/newview/llavatarlist.h +++ b/indra/newview/llavatarlist.h @@ -112,6 +112,8 @@ protected: private: + bool isAvalineItemSelected(); + bool mIgnoreOnlineStatus; bool mShowLastInteractionTime; bool mDirty; diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index 4645a2c6e7..0c0df14ac1 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -1,1772 +1,1850 @@ -/** - * @file llbottomtray.cpp - * @brief LLBottomTray class implementation - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" // must be first include - -#define LLBOTTOMTRAY_CPP -#include "llbottomtray.h" - -// library includes -#include "llfloaterreg.h" -#include "llflyoutbutton.h" -#include "lllayoutstack.h" -#include "llnotifications.h" -#include "llnotificationsutil.h" -#include "lltexteditor.h" - -// newview includes -#include "llagent.h" -#include "llagentcamera.h" -#include "llavataractions.h" -#include "llchiclet.h" -#include "llfloatercamera.h" -#include "llhints.h" -#include "llimfloater.h" // for LLIMFloater -#include "llnearbychatbar.h" -#include "llsidetray.h" -#include "llspeakbutton.h" -#include "llsplitbutton.h" -#include "llsyswellwindow.h" -#include "lltoolmgr.h" -#include "llviewerparcelmgr.h" - -#include "llviewerwindow.h" -#include "llsdserialize.h" - -// Distance from mouse down on which drag'n'drop should be started. -#define DRAG_START_DISTANCE 3 - -static const std::string SORTING_DATA_FILE_NAME = "bottomtray_buttons_order.xml"; - -LLDefaultChildRegistry::Register bottomtray_button("bottomtray_button"); - -// LLBottomtrayButton methods - -// virtual -BOOL LLBottomtrayButton::handleHover(S32 x, S32 y, MASK mask) -{ - if (mCanDrag) - { - S32 screenX, screenY; - localPointToScreen(x, y, &screenX, &screenY); - // pass hover to bottomtray - LLBottomTray::getInstance()->onDraggableButtonHover(screenX, screenY); - return TRUE; - } - else - { - return LLButton::handleHover(x, y, mask); - } -} -//virtual -BOOL LLBottomtrayButton::handleMouseUp(S32 x, S32 y, MASK mask) -{ - if (mCanDrag) - { - S32 screenX, screenY; - localPointToScreen(x, y, &screenX, &screenY); - // pass mouse up to bottomtray - LLBottomTray::getInstance()->onDraggableButtonMouseUp(this, screenX, screenY); - } - return LLButton::handleMouseUp(x, y, mask); -} -//virtual -BOOL LLBottomtrayButton::handleMouseDown(S32 x, S32 y, MASK mask) -{ - if (mCanDrag) - { - S32 screenX, screenY; - localPointToScreen(x, y, &screenX, &screenY); - // pass mouse up to bottomtray - LLBottomTray::getInstance()->onDraggableButtonMouseDown(this, screenX, screenY); - } - return LLButton::handleMouseDown(x, y, mask); -} - -static void update_build_button_enable_state() -{ - bool can_edit = LLToolMgr::getInstance()->canEdit(); - - LLBottomTray::getInstance()->getChildView("build_btn")->setEnabled(can_edit); -} - -// Build time optimization, generate extern template once in .cpp file -template class LLBottomTray* LLSingleton::getInstance(); - -namespace -{ - const std::string& PANEL_CHICLET_NAME = "chiclet_list_panel"; - - S32 get_panel_min_width(LLLayoutStack* stack, LLView* panel) - { - S32 minimal_width = 0; - llassert(stack); - if ( stack && panel && panel->getVisible() ) - { - stack->getPanelMinSize(panel->getName(), &minimal_width); - } - return minimal_width; - } - - S32 get_panel_max_width(LLLayoutStack* stack, LLPanel* panel) - { - S32 max_width = 0; - llassert(stack); - if ( stack && panel && panel->getVisible() ) - { - stack->getPanelMaxSize(panel->getName(), &max_width); - } - return max_width; - } - - S32 get_curr_width(LLUICtrl* ctrl) - { - S32 cur_width = 0; - if ( ctrl && ctrl->getVisible() ) - { - cur_width = ctrl->getRect().getWidth(); - } - return cur_width; - } -} - -class LLBottomTrayLite - : public LLPanel -{ -public: - LLBottomTrayLite() - : mNearbyChatBar(NULL), - mChatBarContainer(NULL), - mGesturePanel(NULL) - { - mFactoryMap["chat_bar"] = LLCallbackMap(LLBottomTray::createNearbyChatBar, NULL); - buildFromFile("panel_bottomtray_lite.xml"); - } - - BOOL postBuild() - { - mNearbyChatBar = findChild("chat_bar"); - mChatBarContainer = getChild("chat_bar_layout_panel"); - mGesturePanel = getChild("gesture_panel"); - - // Hide "show_nearby_chat" button - if (mNearbyChatBar) - { - LLLineEditor* chat_box = mNearbyChatBar->getChatBox(); - LLUICtrl* show_btn = mNearbyChatBar->getChild("show_nearby_chat"); - S32 delta_width = show_btn->getRect().getWidth(); - show_btn->setVisible(FALSE); - chat_box->reshape(chat_box->getRect().getWidth() + delta_width, chat_box->getRect().getHeight()); - } - return TRUE; - } - - void onFocusLost() - { - if (gAgentCamera.cameraMouselook()) - { - LLBottomTray::getInstance()->setVisible(FALSE); - } - } - - LLNearbyChatBar* mNearbyChatBar; - LLLayoutPanel* mChatBarContainer; - LLPanel* mGesturePanel; -}; - -LLBottomTray::LLBottomTray(const LLSD&) -: mChicletPanel(NULL), - mSpeakPanel(NULL), - mSpeakBtn(NULL), - mNearbyChatBar(NULL), - mChatBarContainer(NULL), - mToolbarStack(NULL), - mMovementButton(NULL), - mResizeState(RS_NORESIZE), - mBottomTrayContextMenu(NULL), - mCamButton(NULL), - mBottomTrayLite(NULL), - mIsInLiteMode(false), - mDragStarted(false), - mDraggedItem(NULL), - mLandingTab(NULL), - mCheckForDrag(false) -{ - // Firstly add ourself to IMSession observers, so we catch session events - // before chiclets do that. - LLIMMgr::getInstance()->addSessionObserver(this); - - mFactoryMap["chat_bar"] = LLCallbackMap(LLBottomTray::createNearbyChatBar, NULL); - - buildFromFile("panel_bottomtray.xml"); - - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("CameraPresets.ChangeView", boost::bind(&LLFloaterCamera::onClickCameraItem, _2)); - - //this is to fix a crash that occurs because LLBottomTray is a singleton - //and thus is deleted at the end of the viewers lifetime, but to be cleanly - //destroyed LLBottomTray requires some subsystems that are long gone - //LLUI::getRootView()->addChild(this); - - { - mBottomTrayLite = new LLBottomTrayLite(); - mBottomTrayLite->setFollowsAll(); - mBottomTrayLite->setVisible(FALSE); - } - - mImageDragIndication = LLUI::getUIImage(getString("DragIndicationImageName")); - mDesiredNearbyChatWidth = mNearbyChatBar ? mNearbyChatBar->getRect().getWidth() : 0; -} - -LLBottomTray::~LLBottomTray() -{ - if (!LLSingleton::destroyed()) - { - LLIMMgr::getInstance()->removeSessionObserver(this); - } - - if (mNearbyChatBar) - { - // store custom width of chatbar panel. - S32 custom_width = mChatBarContainer->getRect().getWidth(); - gSavedSettings.setS32("ChatBarCustomWidth", custom_width); - } - - // emulate previous floater behavior to be hidden on startup. - // override effect of save_visibility=true. - // this attribute is necessary to button.initial_callback=Button.SetFloaterToggle works properly: - // i.g when floater changes its visibility - button changes its toggle state. - getChild("build_btn")->setControlValue(false); - getChild("search_btn")->setControlValue(false); - getChild("world_map_btn")->setControlValue(false); -} - -// *TODO Vadim: why void* ? -void* LLBottomTray::createNearbyChatBar(void* userdata) -{ - return new LLNearbyChatBar(); -} - -LLNearbyChatBar* LLBottomTray::getNearbyChatBar() -{ - return mIsInLiteMode ? mBottomTrayLite->mNearbyChatBar : mNearbyChatBar; -} - -LLIMChiclet* LLBottomTray::createIMChiclet(const LLUUID& session_id) -{ - LLIMChiclet::EType im_chiclet_type = LLIMChiclet::getIMSessionType(session_id); - - switch (im_chiclet_type) - { - case LLIMChiclet::TYPE_IM: - return getChicletPanel()->createChiclet(session_id); - case LLIMChiclet::TYPE_GROUP: - return getChicletPanel()->createChiclet(session_id); - case LLIMChiclet::TYPE_AD_HOC: - return getChicletPanel()->createChiclet(session_id); - case LLIMChiclet::TYPE_UNKNOWN: - break; - } - - return NULL; -} - -//virtual -void LLBottomTray::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) -{ - if (!getChicletPanel()) return; - - LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id); - if (!session) return; - - // no need to spawn chiclets for participants in P2P calls called through Avaline - if (session->isP2P() && session->isOtherParticipantAvaline()) return; - - if (getChicletPanel()->findChiclet(session_id)) return; - - LLIMChiclet* chiclet = createIMChiclet(session_id); - if(chiclet) - { - chiclet->setIMSessionName(name); - chiclet->setOtherParticipantId(other_participant_id); - - LLIMFloater::onIMChicletCreated(session_id); - - } - else - { - llerrs << "Could not create chiclet" << llendl; - } -} - -//virtual -void LLBottomTray::sessionRemoved(const LLUUID& session_id) -{ - if(getChicletPanel()) - { - // IM floater should be closed when session removed and associated chiclet closed - LLIMFloater* iMfloater = LLFloaterReg::findTypedInstance( - "impanel", session_id); - if (iMfloater != NULL) - { - iMfloater->closeFloater(); - } - - getChicletPanel()->removeChiclet(session_id); - } -} - -void LLBottomTray::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) -{ - //this is only needed in case of outgoing ad-hoc/group chat sessions - LLChicletPanel* chiclet_panel = getChicletPanel(); - if (chiclet_panel) - { - //it should be ad-hoc im chiclet or group im chiclet - LLChiclet* chiclet = chiclet_panel->findChiclet(old_session_id); - if (chiclet) chiclet->setSessionId(new_session_id); - } -} - -S32 LLBottomTray::getTotalUnreadIMCount() -{ - return getChicletPanel()->getTotalUnreadIMCount(); -} - -// virtual -void LLBottomTray::onChange(EStatusType status, const std::string &channelURI, bool proximal) -{ - // Time it takes to connect to voice channel might be pretty long, - // so don't expect user login or STATUS_VOICE_ENABLED to be followed by STATUS_JOINED. - BOOL enable = FALSE; - - switch (status) - { - // Do not add STATUS_VOICE_ENABLED because voice chat is - // inactive until STATUS_JOINED - case STATUS_JOINED: - enable = TRUE; - break; - default: - enable = FALSE; - break; - } - - // We have to enable/disable right and left parts of speak button separately (EXT-4648) - mSpeakBtn->setSpeakBtnEnabled(enable); - // skipped to avoid button blinking - if (status != STATUS_JOINING && status!= STATUS_LEFT_CHANNEL) - { - mSpeakBtn->setFlyoutBtnEnabled(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking()); - } -} - -void LLBottomTray::onMouselookModeOut() -{ - mIsInLiteMode = false; - mBottomTrayLite->setVisible(FALSE); - mNearbyChatBar->getChatBox()->setText(mBottomTrayLite->mNearbyChatBar->getChatBox()->getText()); - setVisible(TRUE); -} - -void LLBottomTray::onMouselookModeIn() -{ - setVisible(FALSE); - - // Attach the lite bottom tray - if (getParent() && mBottomTrayLite->getParent() != getParent()) - getParent()->addChild(mBottomTrayLite); - - mBottomTrayLite->setShape(getLocalRect()); - mBottomTrayLite->mNearbyChatBar->getChatBox()->setText(mNearbyChatBar->getChatBox()->getText()); - mBottomTrayLite->mGesturePanel->setVisible(gSavedSettings.getBOOL("ShowGestureButton")); - - mIsInLiteMode = true; -} - -//virtual -// setVisible used instead of onVisibilityChange, since LLAgent calls it on entering/leaving mouselook mode. -// If bottom tray is already visible in mouselook mode, then onVisibilityChange will not be called from setVisible(true), -void LLBottomTray::setVisible(BOOL visible) -{ - if (mIsInLiteMode) - { - mBottomTrayLite->setVisible(visible); - } - else - { - LLPanel::setVisible(visible); - } -} - -S32 LLBottomTray::notifyParent(const LLSD& info) -{ - if(info.has("well_empty")) // implementation of EXT-3397 - { - const std::string chiclet_name = info["well_name"]; - - // only "im_well" or "notification_well" names are expected. - // They are set in panel_bottomtray.xml in & - llassert("im_well" == chiclet_name || "notification_well" == chiclet_name); - - BOOL should_be_visible = !info["well_empty"]; - showWellButton("im_well" == chiclet_name ? RS_IM_WELL : RS_NOTIFICATION_WELL, should_be_visible); - return 1; - } - - if (info.has("action") && info["action"] == "resize") - { - const std::string& name = info["view_name"]; - - // expected only resize of nearby chatbar - if (mChatBarContainer->getName() != name) return LLPanel::notifyParent(info); - - const S32 new_width = info["new_width"]; - - processChatbarCustomization(new_width); - - return 2; - } - return LLPanel::notifyParent(info); -} - -void LLBottomTray::showBottomTrayContextMenu(S32 x, S32 y, MASK mask) -{ - // We should show BottomTrayContextMenu in last turn - if (mBottomTrayContextMenu && !LLMenuGL::sMenuContainer->getVisibleMenu()) - { - //there are no other context menu (IM chiclet etc ), so we can show BottomTrayContextMenu - - updateContextMenu(x, y, mask); - mBottomTrayContextMenu->buildDrawLabels(); - mBottomTrayContextMenu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, mBottomTrayContextMenu, x, y); - - } -} - -void LLBottomTray::updateContextMenu(S32 x, S32 y, MASK mask) -{ - LLUICtrl* edit_box = mNearbyChatBar->getChild("chat_box"); - - S32 local_x = x - mChatBarContainer->getRect().mLeft - edit_box->getRect().mLeft; - S32 local_y = y - mChatBarContainer->getRect().mBottom - edit_box->getRect().mBottom; - - bool in_edit_box = edit_box->pointInView(local_x, local_y); - - mBottomTrayContextMenu->setItemVisible("Separator", in_edit_box); - mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Cut", in_edit_box); - mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Copy", in_edit_box); - mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Paste", in_edit_box); - mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Delete", in_edit_box); - mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Select_All", in_edit_box); -} - -void LLBottomTray::showGestureButton(BOOL visible) -{ - setTrayButtonVisibleIfPossible(RS_BUTTON_GESTURES, visible); -} - -void LLBottomTray::showMoveButton(BOOL visible) -{ - setTrayButtonVisibleIfPossible(RS_BUTTON_MOVEMENT, visible); -} - -void LLBottomTray::showCameraButton(BOOL visible) -{ - setTrayButtonVisibleIfPossible(RS_BUTTON_CAMERA, visible); -} - -void LLBottomTray::showSnapshotButton(BOOL visible) -{ - setTrayButtonVisibleIfPossible(RS_BUTTON_SNAPSHOT, visible); -} - -void LLBottomTray::toggleMovementControls() -{ - if (mMovementButton) - mMovementButton->onCommit(); -} - -void LLBottomTray::toggleCameraControls() -{ - if (mCamButton) - mCamButton->onCommit(); -} - -BOOL LLBottomTray::postBuild() -{ - LLHints::registerHintTarget("bottom_tray", LLView::getHandle()); - LLHints::registerHintTarget("dest_guide_btn", getChild("destination_btn")->getHandle()); - LLHints::registerHintTarget("avatar_picker_btn", getChild("avatar_btn")->getHandle()); - - LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("NearbyChatBar.Action", boost::bind(&LLBottomTray::onContextMenuItemClicked, this, _2)); - LLUICtrl::EnableCallbackRegistry::currentRegistrar().add("NearbyChatBar.EnableMenuItem", boost::bind(&LLBottomTray::onContextMenuItemEnabled, this, _2)); - - mBottomTrayContextMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_bottomtray.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - gMenuHolder->addChild(mBottomTrayContextMenu); - - mNearbyChatBar = findChild("chat_bar"); - LLHints::registerHintTarget("chat_bar", mNearbyChatBar->LLView::getHandle()); - - mChatBarContainer = getChild("chat_bar_layout_panel"); - - mToolbarStack = getChild("toolbar_stack"); - mMovementButton = getChild("movement_btn"); - LLHints::registerHintTarget("move_btn", mMovementButton->getHandle()); - mCamButton = getChild("camera_btn"); - setRightMouseDownCallback(boost::bind(&LLBottomTray::showBottomTrayContextMenu,this, _2, _3,_4)); - - mSpeakPanel = getChild("speak_panel"); - mSpeakBtn = getChild("talk"); - - // Both parts of speak button should be initially disabled because - // it takes some time between logging in to world and connecting to voice channel. - mSpeakBtn->setSpeakBtnEnabled(false); - mSpeakBtn->setFlyoutBtnEnabled(false); - - // Localization tool doesn't understand custom buttons like - mSpeakBtn->setSpeakToolTip( getString("SpeakBtnToolTip") ); - mSpeakBtn->setShowToolTip( getString("VoiceControlBtnToolTip") ); - - // Registering Chat Bar to receive Voice client status change notifications. - LLVoiceClient::getInstance()->addObserver(this); - - mNearbyChatBar->getChatBox()->setContextMenu(NULL); - - mChicletPanel = getChild("chiclet_list"); - - initResizeStateContainers(); - - setButtonsControlsAndListeners(); - - initButtonsVisibility(); - - // update wells visibility: - showWellButton(RS_IM_WELL, !LLIMWellWindow::getInstance()->isWindowEmpty()); - showWellButton(RS_NOTIFICATION_WELL, !LLNotificationWellWindow::getInstance()->isWindowEmpty()); - - loadButtonsOrder(); - - LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(boost::bind(&update_build_button_enable_state)); - - return TRUE; -} - -//Drag-n-drop - -void LLBottomTray::onDraggableButtonMouseDown(LLUICtrl* ctrl, S32 x, S32 y) -{ - if (ctrl == NULL) return; - LLView* parent_view = ctrl->getParent(); - if(parent_view != NULL) - { - // we actually drag'n'drop panel (not button) in code, so have to find a parent - // of button which called this method on mouse down. - LLPanel* parent = dynamic_cast(parent_view); - // It may happen that we clicked not usual button, but button inside widget(speak, gesture) - // so we'll need to get a level higher to reach layout panel as a parent. - if(parent == NULL) parent = dynamic_cast(parent_view->getParent()); - if (parent && parent->getVisible()) - { - mDraggedItem = parent; - mCheckForDrag = true; - mStartX = x; - mStartY = y; - } - } -} - -LLPanel* LLBottomTray::findChildPanelByLocalCoords(S32 x, S32 y) -{ - LLPanel* ctrl = 0; - S32 screenX, screenY; - const child_list_t* list = mToolbarStack->getChildList(); - - localPointToScreen(x, y, &screenX, &screenY); - - // look for a child panel which contains the point (screenX, screenY) in it's rectangle - for (child_list_const_iter_t i = list->begin(); i != list->end(); ++i) - { - LLRect rect; - localRectToScreen((*i)->getRect(), &rect); - - if (rect.pointInRect(screenX, screenY)) - { - ctrl = dynamic_cast(*i); - break; - } - } - - return ctrl; -} - -void LLBottomTray::onDraggableButtonHover(S32 x, S32 y) -{ - // if mouse down on draggable item was done, check whether we should start DnD - if (mCheckForDrag) - { - // Start drag'n'drop if mouse cursor was dragged away frome mouse down location enough - if(sqrt((float)((mStartX-x)*(mStartX-x)+(mStartY-y)*(mStartY-y))) > DRAG_START_DISTANCE) - { - mDragStarted = true; - mCheckForDrag = false; - } - } - if (mDragStarted) - { - // Check whether the cursor is over draggable area, find which panel it is and set is as - // landing tab for drag'n'drop - if(isCursorOverDraggableArea(x, y)) - { - LLPanel* panel = findChildPanelByLocalCoords(x,y); - if (panel && panel != mDraggedItem) mLandingTab = panel; - gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROWDRAG); - } - else - { - gViewerWindow->getWindow()->setCursor(UI_CURSOR_NO); - } - } -} - -bool LLBottomTray::isCursorOverDraggableArea(S32 x, S32 y) -{ - bool result = getRect().pointInRect(x, y); - result = result && mNearbyChatBar->calcScreenRect().mRight < x; - result = result && mChicletPanel->calcScreenRect().mRight > x; - return result; -} - -void LLBottomTray::updateButtonsOrdersAfterDnD() -{ - // *TODO: change implementation of this method to support simplify it - // (and according to future possible changes in the way button order is saved between sessions). - state_object_map_t::const_iterator it = mStateProcessedObjectMap.begin(); - state_object_map_t::const_iterator it_end = mStateProcessedObjectMap.end(); - // Speak button is currently the only draggable button not in mStateProcessedObjectMap, - // so if dragged_state is not found in that map, it should be RS_BUTTON_SPEAK. Change this code if any other - // exclusions from mStateProcessedObjectMap will become draggable. - EResizeState dragged_state = RS_BUTTON_SPEAK; - EResizeState landing_state = RS_NORESIZE; - bool landing_state_found = false; - // Find states for dragged item and landing tab - for (; it != it_end; ++it) - { - if (it->second == mDraggedItem) - { - dragged_state = it->first; - } - else if (it->second == mLandingTab) - { - landing_state = it->first; - landing_state_found = true; - } - } - - // Update order of buttons according to drag'n'drop - mButtonsOrder.erase(std::find(mButtonsOrder.begin(), mButtonsOrder.end(), dragged_state)); - if (!landing_state_found && mLandingTab == getChild(PANEL_CHICLET_NAME)) - { - mButtonsOrder.push_back(dragged_state); - } - else - { - if (!landing_state_found) landing_state = RS_BUTTON_SPEAK; - mButtonsOrder.insert(std::find(mButtonsOrder.begin(), mButtonsOrder.end(), landing_state), dragged_state); - } - // Synchronize button process order with their order - resize_state_vec_t::const_iterator it1 = mButtonsOrder.begin(); - const resize_state_vec_t::const_iterator it_end1 = mButtonsOrder.end(); - resize_state_vec_t::iterator it2 = mButtonsProcessOrder.begin(); - for (; it1 != it_end1; ++it1) - { - // Skip Speak because it is not in mButtonsProcessOrder(it's the reason why mButtonsOrder was introduced). - // If any other draggable items will be added to bottomtray later, they should also be skipped here. - if (*it1 != RS_BUTTON_SPEAK) - { - *it2 = *it1; - ++it2; - } - } - - saveButtonsOrder(); -} - -void LLBottomTray::saveButtonsOrder() -{ - std::string user_dir = gDirUtilp->getLindenUserDir(); - if (user_dir.empty()) return; - - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); - LLSD settings_llsd; - int i = 0; - const resize_state_vec_t::const_iterator it_end = mButtonsOrder.end(); - // we use numbers as keys for map which is saved in file and contains resize states as its values - for (resize_state_vec_t::const_iterator it = mButtonsOrder.begin(); it != it_end; ++it, i++) - { - std::string str = llformat("%d", i); - settings_llsd[str] = *it; - } - llofstream file; - file.open(filename); - LLSDSerialize::toPrettyXML(settings_llsd, file); -} - -void LLBottomTray::loadButtonsOrder() -{ - // load per-resident sorting information - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); - - LLSD settings_llsd; - llifstream file; - file.open(filename); - if (!file.is_open()) return; - - LLSDSerialize::fromXML(settings_llsd, file); - - - mButtonsOrder.clear(); - mButtonsProcessOrder.clear(); - int i = 0; - // getting button order from file - for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); - iter != settings_llsd.endMap(); ++iter, ++i) - { - std::string str = llformat("%d", i); - EResizeState state = (EResizeState)settings_llsd[str].asInteger(); - mButtonsOrder.push_back(state); - // RS_BUTTON_SPEAK is skipped, because it shouldn't be in mButtonsProcessOrder (it does not hide or shrink). - if (state != RS_BUTTON_SPEAK) - { - mButtonsProcessOrder.push_back(state); - } - } - - // There are other panels in layout stack order of which is not saved. Also, panels order of which is saved, - // are already in layout stack but in wrong order. The most convenient way to place them is moving them - // to front one by one (because in this case we don't have to pass the panel before which we want to insert our - // panel to movePanel()). So panels are moved in order from the end of mButtonsOrder vector(reverse iterator is used). - const resize_state_vec_t::const_reverse_iterator it_end = mButtonsOrder.rend(); - // placing panels in layout stack according to button order which we loaded in previous for - for (resize_state_vec_t::const_reverse_iterator it = mButtonsOrder.rbegin(); it != it_end; ++it, ++i) - { - LLPanel* panel_to_move = *it == RS_BUTTON_SPEAK ? mSpeakPanel : mStateProcessedObjectMap[*it]; - mToolbarStack->movePanel(panel_to_move, NULL, true); // prepend - } - // Nearbychat is not stored in order settings file, but it must be the first of the panels, so moving it - // manually here - mToolbarStack->movePanel(mChatBarContainer, NULL, true); -} - -void LLBottomTray::onDraggableButtonMouseUp(LLUICtrl* ctrl, S32 x, S32 y) -{ - //if mouse up happened over area where drop is possible, change order of buttons - if (mLandingTab != NULL && mDraggedItem != NULL && mDragStarted) - { - if(isCursorOverDraggableArea(x, y)) - { - // change order of panels in layout stack - mToolbarStack->movePanel(mDraggedItem, (LLPanel*)mLandingTab); - // change order of buttons in order vectors - updateButtonsOrdersAfterDnD(); - } - } - gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW); - mDragStarted = false; - mDraggedItem = NULL; - mLandingTab = NULL; - mCheckForDrag = false; -} - -void LLBottomTray::draw() -{ - LLPanel::draw(); - if (mLandingTab) - { - static S32 w = mImageDragIndication->getWidth(); - static S32 h = mImageDragIndication->getHeight(); - LLRect rect = mLandingTab->calcScreenRect(); - mImageDragIndication->draw(rect.mLeft - w/2, rect.getHeight(), w, h); - } - getChild("show_profile_btn")->setToggleState(LLAvatarActions::profileVisible(gAgent.getID())); - - LLPanel* panel = LLSideTray::getInstance()->getPanel("panel_people"); - if (panel && panel->isInVisibleChain()) - { - getChild("show_people_button")->setToggleState(true); - } - else - { - getChild("show_people_button")->setToggleState(false); - } - - LLFloater* help_browser = (LLFloaterReg::findInstance("help_browser")); - bool help_floater_visible = (help_browser && help_browser->isInVisibleChain()); - - getChild("show_help_btn")->setToggleState(help_floater_visible); - - -} - -bool LLBottomTray::onContextMenuItemEnabled(const LLSD& userdata) -{ - std::string item = userdata.asString(); - LLLineEditor* edit_box = mNearbyChatBar->findChild("chat_box"); - - if (item == "can_cut") - { - return edit_box->canCut(); - } - else if (item == "can_copy") - { - return edit_box->canCopy(); - } - else if (item == "can_paste") - { - return edit_box->canPaste(); - } - else if (item == "can_delete") - { - return edit_box->canDoDelete(); - } - else if (item == "can_select_all") - { - return edit_box->canSelectAll() && (edit_box->getLength()>0); - } - return true; -} - - -void LLBottomTray::onContextMenuItemClicked(const LLSD& userdata) -{ - std::string item = userdata.asString(); - LLLineEditor* edit_box = mNearbyChatBar->findChild("chat_box"); - - if (item == "cut") - { - edit_box->cut(); - } - else if (item == "copy") - { - edit_box->copy(); - } - else if (item == "paste") - { - edit_box->paste(); - edit_box->setFocus(TRUE); - } - else if (item == "delete") - { - edit_box->doDelete(); - } - else if (item == "select_all") - { - edit_box->selectAll(); - } -} - -void LLBottomTray::log(LLView* panel, const std::string& descr) -{ - if (NULL == panel) return; - LLView* layout = panel->getParent(); - lldebugs << descr << ": " - << "panel: " << panel->getName() - << ", rect: " << panel->getRect() - - - << "layout: " << layout->getName() - << ", rect: " << layout->getRect() - << llendl - ; -} - -void LLBottomTray::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - static S32 debug_calling_number = 0; - lldebugs << "**************************************** " << ++debug_calling_number << llendl; - - S32 current_width = getRect().getWidth(); - S32 delta_width = width - current_width; - lldebugs << "Reshaping: " - << ", width: " << width - << ", cur width: " << current_width - << ", delta_width: " << delta_width - << ", called_from_parent: " << called_from_parent - << llendl; - - if (mNearbyChatBar) log(mNearbyChatBar, "before"); - if (mChicletPanel) log(mChicletPanel, "before"); - - // stores width size on which bottom tray is less than width required by its children. EXT-991 - static S32 extra_shrink_width = 0; - bool should_be_reshaped = true; - - if (mChicletPanel && mToolbarStack && mNearbyChatBar) - { - // Firstly, update layout stack to ensure we deal with correct panel sizes. - { - BOOL saved_anim = mToolbarStack->getAnimate(); - // Set chiclet panel to be autoresized by default. - mToolbarStack->updatePanelAutoResize(PANEL_CHICLET_NAME, TRUE); - // Disable animation to prevent layout updating in several frames. - mToolbarStack->setAnimate(FALSE); - // Force the updating of layout to reset panels collapse factor. - mToolbarStack->updateLayout(); - // Restore animate state. - mToolbarStack->setAnimate(saved_anim); - } - - // bottom tray is narrowed - if (delta_width < 0) - { - if (extra_shrink_width > 0) - { - // is world rect was extra shrunk and decreasing again only update this value - // to delta_width negative - extra_shrink_width -= delta_width; // use "-=" because delta_width is negative - should_be_reshaped = false; - } - else - { - extra_shrink_width = processWidthDecreased(delta_width); - - // increase new width to extra_shrink_width value to not reshape less than bottom tray minimum - width += extra_shrink_width; - } - } - // bottom tray is widen - else - { - if (extra_shrink_width > delta_width) - { - // Less than minimum width is more than increasing (delta_width) - // only reduce it value and make no reshape - extra_shrink_width -= delta_width; - should_be_reshaped = false; - } - else - { - if (extra_shrink_width > 0) - { - // If we have some extra shrink width let's reduce delta_width & width - delta_width -= extra_shrink_width; - width -= extra_shrink_width; - extra_shrink_width = 0; - } - processWidthIncreased(delta_width); - } - } - } - - if (should_be_reshaped) - { - lldebugs << "Reshape all children with width: " << width << llendl; - LLPanel::reshape(width, height, called_from_parent); - } - - if (mNearbyChatBar) log(mNearbyChatBar, "after"); - if (mChicletPanel) log(mChicletPanel, "after"); - - - // Restore width of the chatbar on first reshape. - // we can not to do this from postBuild because reshape is called from parent view on startup - // creation after it and reset width according to resize logic. - static bool needs_restore_custom_state = true; - if (mChatBarContainer && needs_restore_custom_state) - { - // restore custom width of chatbar panel. - S32 new_width = gSavedSettings.getS32("ChatBarCustomWidth"); - if (new_width > 0) - { - mDesiredNearbyChatWidth = new_width; - processChatbarCustomization(new_width); - mChatBarContainer->reshape(new_width, mChatBarContainer->getRect().getHeight()); - } - needs_restore_custom_state = false; - } - -} - -S32 LLBottomTray::processWidthDecreased(S32 delta_width) -{ - bool still_should_be_processed = true; - - const S32 chiclet_panel_width = mChicletPanel->getParent()->getRect().getWidth(); - const S32 chiclet_panel_min_width = mChicletPanel->getMinWidth(); - - // There are four steps of processing width decrease. If in one of them required width was reached, - // further are not needed. - // 1. Decreasing width of chiclet panel. - if (chiclet_panel_width > chiclet_panel_min_width) - { - // we have some space to decrease chiclet panel - S32 panel_delta_min = chiclet_panel_width - chiclet_panel_min_width; - - S32 delta_panel = llmin(-delta_width, panel_delta_min); - - lldebugs << "delta_width: " << delta_width - << ", panel_delta_min: " << panel_delta_min - << ", delta_panel: " << delta_panel - << llendl; - - // is chiclet panel width enough to process resizing? - delta_width += panel_delta_min; - - still_should_be_processed = delta_width < 0; - - mChicletPanel->getParent()->reshape(mChicletPanel->getParent()->getRect().getWidth() - delta_panel, mChicletPanel->getParent()->getRect().getHeight()); - log(mChicletPanel, "after processing panel decreasing via chiclet panel"); - - lldebugs << "RS_CHICLET_PANEL" - << ", delta_width: " << delta_width - << llendl; - } - - S32 buttons_freed_width = 0; - // 2. Decreasing width of buttons. - if (still_should_be_processed) - { - processShrinkButtons(delta_width, buttons_freed_width); - } - // 3. Decreasing width of nearby chat. - const S32 chatbar_panel_min_width = get_panel_min_width(mToolbarStack, mChatBarContainer); - const S32 chatbar_panel_width = mChatBarContainer->getRect().getWidth(); - if (still_should_be_processed && chatbar_panel_width > chatbar_panel_min_width) - { - // we have some space to decrease chatbar panel - S32 panel_delta_min = chatbar_panel_width - chatbar_panel_min_width; - - S32 delta_panel = llmin(-delta_width, panel_delta_min); - - // whether chatbar panel width is enough to process resizing? - delta_width += panel_delta_min; - - still_should_be_processed = delta_width < 0; - - // chatbar should only be shrunk here, not stretched - if(delta_panel > 0) - { - mChatBarContainer->reshape(mNearbyChatBar->getRect().getWidth() - delta_panel, mChatBarContainer->getRect().getHeight()); - } - - log(mNearbyChatBar, "after processing panel decreasing via nearby chatbar panel"); - - lldebugs << "RS_CHATBAR_INPUT" - << ", delta_panel: " << delta_panel - << ", delta_width: " << delta_width - << llendl; - } - - S32 extra_shrink_width = 0; - // 4. Hiding buttons if needed. - if (still_should_be_processed) - { - processHideButtons(delta_width, buttons_freed_width); - - if (delta_width < 0) - { - extra_shrink_width = -delta_width; - llwarns << "There is no enough width to reshape all children: " - << extra_shrink_width << llendl; - } - - if (buttons_freed_width > 0) - { - S32 nearby_needed_width = mDesiredNearbyChatWidth - mNearbyChatBar->getRect().getWidth(); - if (nearby_needed_width > 0) - { - S32 compensative_width = nearby_needed_width > buttons_freed_width ? buttons_freed_width : nearby_needed_width; - log(mNearbyChatBar, "before applying compensative width"); - mChatBarContainer->reshape(mChatBarContainer->getRect().getWidth() + compensative_width, mChatBarContainer->getRect().getHeight() ); - log(mNearbyChatBar, "after applying compensative width"); - lldebugs << buttons_freed_width << llendl; - } - } - } - - return extra_shrink_width; -} - -void LLBottomTray::processWidthIncreased(S32 delta_width) -{ - if (delta_width <= 0) return; - - const S32 chiclet_panel_width = mChicletPanel->getParent()->getRect().getWidth(); - static const S32 chiclet_panel_min_width = mChicletPanel->getMinWidth(); - - const S32 available_width_chiclet = chiclet_panel_width - chiclet_panel_min_width; - - // how many room we have to show hidden buttons - S32 total_available_width = delta_width + available_width_chiclet; - - lldebugs << "Processing extending, available width:" - << ", chiclets - " << available_width_chiclet - << ", total - " << total_available_width - << llendl; - - S32 available_width = total_available_width; - - processShowButtons(available_width); - - // if we have to show/extend some buttons but resized delta width is not enough... - S32 processed_width = total_available_width - available_width; - if (processed_width > delta_width) - { - // ... let's shrink nearby chat & chiclet panels - S32 required_to_process_width = processed_width; - - // 1. use delta width of resizing - required_to_process_width -= delta_width; - - // 2. use width available via decreasing of chiclet panel - if (required_to_process_width > 0) - { - mChicletPanel->getParent()->reshape(mChicletPanel->getParent()->getRect().getWidth() - required_to_process_width, mChicletPanel->getParent()->getRect().getHeight()); - log(mChicletPanel, "after applying compensative width for chiclets: "); - lldebugs << required_to_process_width << llendl; - } - - } - - // shown buttons take some space, rest should be processed by nearby chatbar & chiclet panels - delta_width -= processed_width; - - - // how many space can nearby chatbar take? - S32 chatbar_panel_width_ = mChatBarContainer->getRect().getWidth(); - if (delta_width > 0 && chatbar_panel_width_ < mDesiredNearbyChatWidth) - { - S32 delta_panel_max = mDesiredNearbyChatWidth - chatbar_panel_width_; - S32 delta_panel = llmin(delta_width, delta_panel_max); - lldebugs << "Unprocesed delta width: " << delta_width - << ", can be applied to chatbar: " << delta_panel_max - << ", will be applied: " << delta_panel - << llendl; - - delta_width -= delta_panel_max; - mChatBarContainer->reshape(chatbar_panel_width_ + delta_panel, mChatBarContainer->getRect().getHeight()); - log(mNearbyChatBar, "applied unprocessed delta width"); - } - if (delta_width > 0) - { - processExtendButtons(delta_width); - } -} - -void LLBottomTray::processShowButtons(S32& available_width) -{ - // process buttons from left to right - resize_state_vec_t::const_iterator it = mButtonsProcessOrder.begin(); - const resize_state_vec_t::const_iterator it_end = mButtonsProcessOrder.end(); - - for (; it != it_end; ++it) - { - // is there available space? - if (available_width <= 0) break; - - // try to show next button - processShowButton(*it, available_width); - } -} - -bool LLBottomTray::processShowButton(EResizeState shown_object_type, S32& available_width) -{ - lldebugs << "Trying to show object type: " << shown_object_type << llendl; - llassert(mStateProcessedObjectMap[shown_object_type] != NULL); - - LLPanel* panel = mStateProcessedObjectMap[shown_object_type]; - if (NULL == panel) - { - lldebugs << "There is no object to process for state: " << shown_object_type << llendl; - return false; - } - bool can_be_shown = canButtonBeShown(shown_object_type); - if (can_be_shown) - { - //validate if we have enough room to show this button - const S32 required_width = panel->getRect().getWidth(); - can_be_shown = available_width >= required_width; - if (can_be_shown) - { - available_width -= required_width; - - setTrayButtonVisible(shown_object_type, true); - - lldebugs << "processed object type: " << shown_object_type - << ", rest available width: " << available_width - << llendl; - mResizeState &= ~shown_object_type; - } - } - return can_be_shown; -} - -void LLBottomTray::processHideButtons(S32& required_width, S32& buttons_freed_width) -{ - // process buttons from right to left - resize_state_vec_t::const_reverse_iterator it = mButtonsProcessOrder.rbegin(); - const resize_state_vec_t::const_reverse_iterator it_end = mButtonsProcessOrder.rend(); - - for (; it != it_end; ++it) - { - // is it still necessary to hide a button? - if (required_width >= 0) break; - - // try to hide next button - processHideButton(*it, required_width, buttons_freed_width); - } -} - -void LLBottomTray::processHideButton(EResizeState processed_object_type, S32& required_width, S32& buttons_freed_width) -{ - lldebugs << "Trying to hide object type: " << processed_object_type << llendl; - llassert(mStateProcessedObjectMap[processed_object_type] != NULL); - - LLPanel* panel = mStateProcessedObjectMap[processed_object_type]; - if (NULL == panel) - { - lldebugs << "There is no object to process for state: " << processed_object_type << llendl; - return; - } - - if (panel->getVisible()) - { - required_width += panel->getRect().getWidth(); - - if (required_width > 0) - { - buttons_freed_width += required_width; - } - - setTrayButtonVisible(processed_object_type, false); - - mResizeState |= processed_object_type; - - lldebugs << "processing object type: " << processed_object_type - << ", buttons_freed_width: " << buttons_freed_width - << llendl; - } -} - -void LLBottomTray::processShrinkButtons(S32& required_width, S32& buttons_freed_width) -{ - // process buttons from right to left - resize_state_vec_t::const_reverse_iterator it = mButtonsProcessOrder.rbegin(); - const resize_state_vec_t::const_reverse_iterator it_end = mButtonsProcessOrder.rend(); - - // iterate through buttons in the mButtonsProcessOrder first - for (; it != it_end; ++it) - { - // is it still necessary to hide a button? - if (required_width >= 0) break; - - // try to shrink next button - processShrinkButton(*it, required_width); - } - - // then shrink Speak button - if (required_width < 0) - { - - S32 panel_min_width = 0; - std::string panel_name = mSpeakPanel->getName(); - bool success = mToolbarStack->getPanelMinSize(panel_name, &panel_min_width); - if (!success) - { - lldebugs << "Panel was not found to get its min width: " << panel_name << llendl; - } - else - { - S32 panel_width = mSpeakPanel->getRect().getWidth(); - S32 possible_shrink_width = panel_width - panel_min_width; - - if (possible_shrink_width > 0) - { - mSpeakBtn->setLabelVisible(false); - mSpeakPanel->reshape(panel_width - possible_shrink_width, mSpeakPanel->getRect().getHeight()); - - required_width += possible_shrink_width; - - if (required_width > 0) - { - buttons_freed_width += required_width; - } - - lldebugs << "Shrunk Speak button panel: " << panel_name - << ", shrunk width: " << possible_shrink_width - << ", rest width to process: " << required_width - << llendl; - } - } - } -} - -void LLBottomTray::processShrinkButton(EResizeState processed_object_type, S32& required_width) -{ - llassert(mStateProcessedObjectMap[processed_object_type] != NULL); - LLPanel* panel = mStateProcessedObjectMap[processed_object_type]; - if (NULL == panel) - { - lldebugs << "There is no object to process for type: " << processed_object_type << llendl; - return; - } - - if (panel->getVisible()) - { - S32 panel_width = panel->getRect().getWidth(); - S32 panel_min_width = 0; - std::string panel_name = panel->getName(); - bool success = mToolbarStack->getPanelMinSize(panel_name, &panel_min_width); - S32 possible_shrink_width = panel_width - panel_min_width; - - if (!success) - { - lldebugs << "Panel was not found to get its min width: " << panel_name << llendl; - } - // we have some space to free by shrinking the button - else if (possible_shrink_width > 0) - { - // let calculate real width to shrink - - // 1. apply all possible width - required_width += possible_shrink_width; - - // 2. it it is too much... - if (required_width > 0) - { - // reduce applied shrunk width to the excessive value. - possible_shrink_width -= required_width; - required_width = 0; - } - panel->reshape(panel_width - possible_shrink_width, panel->getRect().getHeight()); - - lldebugs << "Shrunk panel: " << panel_name - << ", shrunk width: " << possible_shrink_width - << ", rest width to process: " << required_width - << llendl; - } - } -} - - -void LLBottomTray::processExtendButtons(S32& available_width) -{ - // do not allow extending any buttons if we have some buttons hidden via resize - if (mResizeState & RS_BUTTONS_CAN_BE_HIDDEN) return; - - // process buttons from left to right - resize_state_vec_t::const_iterator it = mButtonsProcessOrder.begin(); - const resize_state_vec_t::const_iterator it_end = mButtonsProcessOrder.end(); - - // iterate through buttons in the mButtonsProcessOrder first - for (; it != it_end; ++it) - { - // is there available space? - if (available_width <= 0) break; - - // try to extend next button - processExtendButton(*it, available_width); - } - - const S32 chiclet_panel_width = mChicletPanel->getParent()->getRect().getWidth(); - static const S32 chiclet_panel_min_width = mChicletPanel->getMinWidth(); - const S32 available_width_chiclet = chiclet_panel_width - chiclet_panel_min_width; - - // then try to extend Speak button - if (available_width > 0 || available_width_chiclet > 0) - { - S32 panel_max_width = mObjectDefaultWidthMap[RS_BUTTON_SPEAK]; - S32 panel_width = mSpeakPanel->getRect().getWidth(); - S32 possible_extend_width = panel_max_width - panel_width; - - if (possible_extend_width >= 0 && possible_extend_width <= available_width + available_width_chiclet) // HACK: this button doesn't change size so possible_extend_width will be 0 - { - mSpeakBtn->setLabelVisible(true); - mSpeakPanel->reshape(panel_max_width, mSpeakPanel->getRect().getHeight()); - log(mSpeakBtn, "speak button is extended"); - - if( available_width > possible_extend_width) - { - available_width -= possible_extend_width; - } - else - { - S32 required_width = possible_extend_width - available_width; - available_width = 0; - mChicletPanel->getParent()->reshape(mChicletPanel->getParent()->getRect().getWidth() - required_width, mChicletPanel->getParent()->getRect().getHeight()); - } - lldebugs << "Extending Speak button panel: " << mSpeakPanel->getName() - << ", extended width: " << possible_extend_width - << ", rest width to process: " << available_width - << llendl; - } - } -} - -void LLBottomTray::processExtendButton(EResizeState processed_object_type, S32& available_width) -{ - llassert(mStateProcessedObjectMap[processed_object_type] != NULL); - LLPanel* panel = mStateProcessedObjectMap[processed_object_type]; - if (NULL == panel) - { - lldebugs << "There is no object to process for type: " << processed_object_type << llendl; - return; - } - - if (!panel->getVisible()) return; - - S32 panel_max_width = mObjectDefaultWidthMap[processed_object_type]; - S32 panel_width = panel->getRect().getWidth(); - S32 possible_extend_width = panel_max_width - panel_width; - - if (possible_extend_width > 0) - { - // let calculate real width to extend - - // 1. apply all possible width - available_width -= possible_extend_width; - - // 2. it it is too much... - if (available_width < 0) - { - // reduce applied extended width to the excessive value. - possible_extend_width += available_width; - available_width = 0; - } - panel->reshape(panel_width + possible_extend_width, panel->getRect().getHeight()); - - lldebugs << "Extending panel: " << panel->getName() - << ", extended width: " << possible_extend_width - << ", rest width to process: " << available_width - << llendl; - } -} - -bool LLBottomTray::canButtonBeShown(EResizeState processed_object_type) const -{ - // 0. Check if passed button was previously hidden on resize - bool can_be_shown = mResizeState & processed_object_type; - if (can_be_shown) - { - // Yes, it was. Lets now check that all buttons before it (that can be hidden on resize) - // are already shown - - // process buttons in direct order (from left to right) - resize_state_vec_t::const_iterator it = mButtonsProcessOrder.begin(); - const resize_state_vec_t::const_iterator it_end = mButtonsProcessOrder.end(); - - // 1. Find and accumulate all buttons types before one passed into the method. - MASK buttons_before_mask = RS_NORESIZE; - for (; it != it_end; ++it) - { - const EResizeState button_type = *it; - if (button_type == processed_object_type) break; - - buttons_before_mask |= button_type; - } - - // 2. Check if some previous buttons are still hidden on resize - can_be_shown = !(buttons_before_mask & mResizeState); - } - return can_be_shown; -} - -void LLBottomTray::initResizeStateContainers() -{ - // init map with objects should be processed for each type - mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_GESTURES, getChild("gesture_panel"))); - mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_MOVEMENT, getChild("movement_panel"))); - mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_CAMERA, getChild("cam_panel"))); - mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_SNAPSHOT, getChild("snapshot_panel"))); - mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_BUILD, getChild("build_btn_panel"))); - mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_SEARCH, getChild("search_btn_panel"))); - mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_WORLD_MAP, getChild("world_map_btn_panel"))); - mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_MINI_MAP, getChild("mini_map_btn_panel"))); - - // init an order of processed buttons - mButtonsProcessOrder.push_back(RS_BUTTON_GESTURES); - mButtonsProcessOrder.push_back(RS_BUTTON_MOVEMENT); - mButtonsProcessOrder.push_back(RS_BUTTON_CAMERA); - mButtonsProcessOrder.push_back(RS_BUTTON_SNAPSHOT); - mButtonsProcessOrder.push_back(RS_BUTTON_BUILD); - mButtonsProcessOrder.push_back(RS_BUTTON_SEARCH); - mButtonsProcessOrder.push_back(RS_BUTTON_WORLD_MAP); - mButtonsProcessOrder.push_back(RS_BUTTON_MINI_MAP); - - mButtonsOrder.push_back(RS_BUTTON_SPEAK); - mButtonsOrder.insert(mButtonsOrder.end(), mButtonsProcessOrder.begin(), mButtonsProcessOrder.end()); - - // init default widths - - // process buttons that can be hidden on resize... - resize_state_vec_t::const_iterator it = mButtonsProcessOrder.begin(); - const resize_state_vec_t::const_iterator it_end = mButtonsProcessOrder.end(); - - for (; it != it_end; ++it) - { - const EResizeState button_type = *it; - // is there an appropriate object? - llassert(mStateProcessedObjectMap.count(button_type) > 0); - if (0 == mStateProcessedObjectMap.count(button_type)) continue; - - // set default width for it. - mObjectDefaultWidthMap[button_type] = mStateProcessedObjectMap[button_type]->getRect().getWidth(); - } - - // ... and add Speak button because it also can be shrunk. - mObjectDefaultWidthMap[RS_BUTTON_SPEAK] = mSpeakPanel->getRect().getWidth(); - -} - -// this method must be called before restoring of the chat entry field on startup -// because it resets chatbar's width according to resize logic. -void LLBottomTray::initButtonsVisibility() -{ - setVisibleAndFitWidths(RS_BUTTON_GESTURES, gSavedSettings.getBOOL("ShowGestureButton")); - setVisibleAndFitWidths(RS_BUTTON_MOVEMENT, gSavedSettings.getBOOL("ShowMoveButton")); - setVisibleAndFitWidths(RS_BUTTON_CAMERA, gSavedSettings.getBOOL("ShowCameraButton")); - setVisibleAndFitWidths(RS_BUTTON_SNAPSHOT, gSavedSettings.getBOOL("ShowSnapshotButton")); - setVisibleAndFitWidths(RS_BUTTON_BUILD, gSavedSettings.getBOOL("ShowBuildButton")); - setVisibleAndFitWidths(RS_BUTTON_SEARCH, gSavedSettings.getBOOL("ShowSearchButton")); - setVisibleAndFitWidths(RS_BUTTON_WORLD_MAP, gSavedSettings.getBOOL("ShowWorldMapButton")); - setVisibleAndFitWidths(RS_BUTTON_MINI_MAP, gSavedSettings.getBOOL("ShowMiniMapButton")); -} - -void LLBottomTray::setButtonsControlsAndListeners() -{ - gSavedSettings.getControl("ShowGestureButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_GESTURES, _2)); - gSavedSettings.getControl("ShowMoveButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_MOVEMENT, _2)); - gSavedSettings.getControl("ShowCameraButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_CAMERA, _2)); - gSavedSettings.getControl("ShowSnapshotButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_SNAPSHOT, _2)); - gSavedSettings.getControl("ShowBuildButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_BUILD, _2)); - gSavedSettings.getControl("ShowSearchButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_SEARCH, _2)); - gSavedSettings.getControl("ShowWorldMapButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_WORLD_MAP, _2)); - gSavedSettings.getControl("ShowMiniMapButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_MINI_MAP, _2)); - - - LLButton* build_btn = getChild("build_btn"); - // set control name for Build button. It is not enough to link it with Button.SetFloaterToggle in xml - std::string vis_control_name = LLFloaterReg::declareVisibilityControl("build"); - // Set the button control value (toggle state) to the floater visibility control (Sets the value as well) - build_btn->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name)); -} - -bool LLBottomTray::toggleShowButton(LLBottomTray::EResizeState button_type, const LLSD& new_visibility) -{ - if (LLBottomTray::instanceExists()) - { - LLBottomTray::getInstance()->setTrayButtonVisibleIfPossible(button_type, new_visibility.asBoolean()); - } - return true; -} - -void LLBottomTray::setTrayButtonVisible(EResizeState shown_object_type, bool visible) -{ - llassert(mStateProcessedObjectMap[shown_object_type] != NULL); - LLPanel* panel = mStateProcessedObjectMap[shown_object_type]; - if (NULL == panel) - { - lldebugs << "There is no object to show for state: " << shown_object_type << llendl; - return; - } - - panel->setVisible(visible); -} - -void LLBottomTray::setTrayButtonVisibleIfPossible(EResizeState shown_object_type, bool visible, bool raise_notification) -{ - if (!setVisibleAndFitWidths(shown_object_type, visible) && visible && raise_notification) - { - LLNotificationsUtil::add("BottomTrayButtonCanNotBeShown", - LLSD(), - LLSD(), - LLNotificationFunctorRegistry::instance().DONOTHING); - } -} - -bool LLBottomTray::setVisibleAndFitWidths(EResizeState object_type, bool visible) -{ - LLPanel* cur_panel = mStateProcessedObjectMap[object_type]; - if (NULL == cur_panel) - { - lldebugs << "There is no object to process for state: " << object_type << llendl; - return false; - } - - bool is_set = true; - - if (visible) - { - // Assume that only chiclet panel can be auto-resized - const S32 available_width = - mChicletPanel->getParent()->getRect().getWidth() - mChicletPanel->getMinWidth(); - - S32 preferred_width = mObjectDefaultWidthMap[object_type]; - S32 current_width = cur_panel->getRect().getWidth(); - S32 result_width = 0; - bool decrease_width = false; - - // Mark this button to be shown - mResizeState |= object_type; - - if (preferred_width > 0 && available_width >= preferred_width) - { - result_width = preferred_width; - } - else if (available_width >= current_width) - { - result_width = current_width; - } - else - { - // Calculate the possible shrunk width as difference between current and minimal widths - const S32 chatbar_shrunk_width = - mChatBarContainer->getRect().getWidth() - get_panel_min_width(mToolbarStack, mChatBarContainer); - - S32 sum_of_min_widths = get_panel_min_width(mToolbarStack, mSpeakPanel); - S32 sum_of_curr_widths = get_curr_width(mSpeakPanel); - - resize_state_vec_t::const_iterator it = mButtonsProcessOrder.begin(); - const resize_state_vec_t::const_iterator it_end = mButtonsProcessOrder.end(); - - for (; it != it_end; ++it) - { - LLPanel * cur_panel = mStateProcessedObjectMap[*it]; - sum_of_min_widths += get_panel_min_width(mToolbarStack, cur_panel); - sum_of_curr_widths += get_curr_width(cur_panel); - } - - const S32 possible_shrunk_width = - chatbar_shrunk_width + (sum_of_curr_widths - sum_of_min_widths); - - // Minimal width of current panel - S32 minimal_width = 0; - mToolbarStack->getPanelMinSize(cur_panel->getName(), &minimal_width); - - if ( (available_width + possible_shrunk_width) >= minimal_width) - { - // There is enough space for minimal width, but set the result_width - // to preferred_width so buttons widths decreasing will be done in predefined order - result_width = (preferred_width > 0) ? preferred_width : current_width; - decrease_width = true; - } - else - { - // Nothing can be done, give up... - return false; - } - } - - if (result_width != current_width) - { - cur_panel->reshape(result_width, cur_panel->getRect().getHeight()); - current_width = result_width; - } - - is_set = processShowButton(object_type, current_width); - - // Shrink buttons if needed - if (is_set && decrease_width) - { - processWidthDecreased( -result_width); - } - } - else - { - const S32 delta_width = get_curr_width(cur_panel); - - setTrayButtonVisible(object_type, false); - - // Mark button NOT to show while future bottom tray extending - mResizeState &= ~object_type; - - // Extend other buttons if need - if (delta_width) - { - processWidthIncreased(delta_width); - } - } - return is_set; -} - -void LLBottomTray::showWellButton(EResizeState object_type, bool visible) -{ - llassert( ((RS_NOTIFICATION_WELL | RS_IM_WELL) & object_type) == object_type ); - - const std::string panel_name = RS_IM_WELL == object_type ? "im_well_panel" : "notification_well_panel"; - - LLView * panel = getChild(panel_name); - - // if necessary visibility is set nothing to do here - if (panel->getVisible() == (BOOL)visible) return; - - S32 panel_width = panel->getRect().getWidth(); - panel->setVisible(visible); - - if (visible) - { - // method assumes that input param is a negative value - processWidthDecreased(-panel_width); - } - else - { - processWidthIncreased(panel_width); - } -} - -void LLBottomTray::processChatbarCustomization(S32 new_width) -{ - if (NULL == mNearbyChatBar) return; - - const S32 delta_width = mChatBarContainer->getRect().getWidth() - new_width; - - if (delta_width == 0) return; - - mDesiredNearbyChatWidth = new_width; - - LLView * chiclet_layout_panel = mChicletPanel->getParent(); - const S32 chiclet_min_width = get_panel_min_width(mToolbarStack, chiclet_layout_panel); - const S32 chiclet_panel_width = chiclet_layout_panel->getRect().getWidth(); - const S32 available_chiclet_shrink_width = chiclet_panel_width - chiclet_min_width; - llassert(available_chiclet_shrink_width >= 0); - - if (delta_width > 0) // panel gets narrowly - { - S32 total_possible_width = delta_width + available_chiclet_shrink_width; - processShowButtons(total_possible_width); - processExtendButtons(total_possible_width); - } - // here (delta_width < 0) // panel gets wider - else //if (-delta_width > available_chiclet_shrink_width) - { - S32 required_width = delta_width + available_chiclet_shrink_width; - S32 buttons_freed_width = 0; - processShrinkButtons(required_width, buttons_freed_width); - processHideButtons(required_width, buttons_freed_width); - } -} - -//EOF +/** + * @file llbottomtray.cpp + * @brief LLBottomTray class implementation + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" // must be first include + +#define LLBOTTOMTRAY_CPP +#include "llbottomtray.h" + +// library includes +#include "llfloaterreg.h" +#include "llflyoutbutton.h" +#include "lllayoutstack.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" +#include "lltexteditor.h" + +// newview includes +#include "llagent.h" +#include "llagentcamera.h" +#include "llavataractions.h" +#include "llchiclet.h" +#include "llfloatercamera.h" +#include "llhints.h" +#include "llimfloater.h" // for LLIMFloater +#include "llnearbychatbar.h" +#include "llsidetray.h" +#include "llspeakbutton.h" +#include "llsplitbutton.h" +#include "llsyswellwindow.h" +#include "lltoolmgr.h" +#include "llviewerparcelmgr.h" + +#include "llviewerwindow.h" +#include "llsdserialize.h" + +// Distance from mouse down on which drag'n'drop should be started. +#define DRAG_START_DISTANCE 3 + +static const std::string SORTING_DATA_FILE_NAME = "bottomtray_buttons_order.xml"; + +LLDefaultChildRegistry::Register bottomtray_button("bottomtray_button"); + +// LLBottomtrayButton methods + +// virtual +BOOL LLBottomtrayButton::handleHover(S32 x, S32 y, MASK mask) +{ + if (mCanDrag) + { + // pass hover to bottomtray + S32 screenX, screenY; + localPointToScreen(x, y, &screenX, &screenY); + LLBottomTray::getInstance()->onDraggableButtonHover(screenX, screenY); + + return TRUE; + } + else + { + return LLButton::handleHover(x, y, mask); + } +} +//virtual +BOOL LLBottomtrayButton::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if (mCanDrag) + { + S32 screenX, screenY; + localPointToScreen(x, y, &screenX, &screenY); + // pass mouse up to bottomtray + LLBottomTray::getInstance()->onDraggableButtonMouseUp(this, screenX, screenY); + } + return LLButton::handleMouseUp(x, y, mask); +} +//virtual +BOOL LLBottomtrayButton::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (mCanDrag) + { + S32 screenX, screenY; + localPointToScreen(x, y, &screenX, &screenY); + // pass mouse up to bottomtray + LLBottomTray::getInstance()->onDraggableButtonMouseDown(this, screenX, screenY); + } + return LLButton::handleMouseDown(x, y, mask); +} + +static void update_build_button_enable_state() +{ + bool can_edit = LLToolMgr::getInstance()->canEdit(); + + LLBottomTray::getInstance()->getChildView("build_btn")->setEnabled(can_edit); +} + +// Build time optimization, generate extern template once in .cpp file +template class LLBottomTray* LLSingleton::getInstance(); + +namespace +{ + const std::string& PANEL_CHICLET_NAME = "chiclet_list_panel"; + + S32 get_panel_min_width(LLLayoutStack* stack, LLView* panel) + { + S32 minimal_width = 0; + llassert(stack); + if ( stack && panel && panel->getVisible() ) + { + stack->getPanelMinSize(panel->getName(), &minimal_width); + } + return minimal_width; + } + + S32 get_panel_max_width(LLLayoutStack* stack, LLPanel* panel) + { + S32 max_width = 0; + llassert(stack); + if ( stack && panel && panel->getVisible() ) + { + stack->getPanelMaxSize(panel->getName(), &max_width); + } + return max_width; + } + + S32 get_curr_width(LLUICtrl* ctrl) + { + S32 cur_width = 0; + if ( ctrl && ctrl->getVisible() ) + { + cur_width = ctrl->getRect().getWidth(); + } + return cur_width; + } +} + +class LLBottomTrayLite + : public LLPanel +{ +public: + LLBottomTrayLite() + : mNearbyChatBar(NULL), + mChatBarContainer(NULL), + mGesturePanel(NULL) + { + mFactoryMap["chat_bar"] = LLCallbackMap(LLBottomTray::createNearbyChatBar, NULL); + buildFromFile("panel_bottomtray_lite.xml"); + } + + BOOL postBuild() + { + mNearbyChatBar = findChild("chat_bar"); + mChatBarContainer = getChild("chat_bar_layout_panel"); + mGesturePanel = getChild("gesture_panel"); + + // Hide "show_nearby_chat" button + if (mNearbyChatBar) + { + LLLineEditor* chat_box = mNearbyChatBar->getChatBox(); + LLUICtrl* show_btn = mNearbyChatBar->getChild("show_nearby_chat"); + S32 delta_width = show_btn->getRect().getWidth(); + show_btn->setVisible(FALSE); + chat_box->reshape(chat_box->getRect().getWidth() + delta_width, chat_box->getRect().getHeight()); + } + return TRUE; + } + + void onFocusLost() + { + if (gAgentCamera.cameraMouselook()) + { + LLBottomTray::getInstance()->setVisible(FALSE); + } + } + + LLNearbyChatBar* mNearbyChatBar; + LLLayoutPanel* mChatBarContainer; + LLPanel* mGesturePanel; +}; + +LLBottomTray::LLBottomTray(const LLSD&) +: mChicletPanel(NULL), + mSpeakPanel(NULL), + mSpeakBtn(NULL), + mNearbyChatBar(NULL), + mChatBarContainer(NULL), + mNearbyCharResizeHandlePanel(NULL), + mToolbarStack(NULL), + mMovementButton(NULL), + mResizeState(RS_NORESIZE), + mBottomTrayContextMenu(NULL), + mCamButton(NULL), + mBottomTrayLite(NULL), + mIsInLiteMode(false), + mDragStarted(false), + mDraggedItem(NULL), + mLandingTab(NULL), + mCheckForDrag(false) +{ + // Firstly add ourself to IMSession observers, so we catch session events + // before chiclets do that. + LLIMMgr::getInstance()->addSessionObserver(this); + + mFactoryMap["chat_bar"] = LLCallbackMap(LLBottomTray::createNearbyChatBar, NULL); + + buildFromFile("panel_bottomtray.xml"); + + LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("CameraPresets.ChangeView", boost::bind(&LLFloaterCamera::onClickCameraItem, _2)); + + //this is to fix a crash that occurs because LLBottomTray is a singleton + //and thus is deleted at the end of the viewers lifetime, but to be cleanly + //destroyed LLBottomTray requires some subsystems that are long gone + //LLUI::getRootView()->addChild(this); + + { + mBottomTrayLite = new LLBottomTrayLite(); + mBottomTrayLite->setFollowsAll(); + mBottomTrayLite->setVisible(FALSE); + } + + mImageDragIndication = LLUI::getUIImage(getString("DragIndicationImageName")); + mDesiredNearbyChatWidth = mNearbyChatBar ? mNearbyChatBar->getRect().getWidth() : 0; +} + +LLBottomTray::~LLBottomTray() +{ + if (!LLSingleton::destroyed()) + { + LLIMMgr::getInstance()->removeSessionObserver(this); + } + + if (mNearbyChatBar) + { + // store custom width of chatbar panel. + S32 custom_width = mChatBarContainer->getRect().getWidth(); + gSavedSettings.setS32("ChatBarCustomWidth", custom_width); + } + + // emulate previous floater behavior to be hidden on startup. + // override effect of save_visibility=true. + // this attribute is necessary to button.initial_callback=Button.SetFloaterToggle works properly: + // i.g when floater changes its visibility - button changes its toggle state. + getChild("build_btn")->setControlValue(false); + getChild("search_btn")->setControlValue(false); + getChild("world_map_btn")->setControlValue(false); +} + +// *TODO Vadim: why void* ? +void* LLBottomTray::createNearbyChatBar(void* userdata) +{ + return new LLNearbyChatBar(); +} + +LLNearbyChatBar* LLBottomTray::getNearbyChatBar() +{ + return mIsInLiteMode ? mBottomTrayLite->mNearbyChatBar : mNearbyChatBar; +} + +LLIMChiclet* LLBottomTray::createIMChiclet(const LLUUID& session_id) +{ + LLIMChiclet::EType im_chiclet_type = LLIMChiclet::getIMSessionType(session_id); + + switch (im_chiclet_type) + { + case LLIMChiclet::TYPE_IM: + return getChicletPanel()->createChiclet(session_id); + case LLIMChiclet::TYPE_GROUP: + return getChicletPanel()->createChiclet(session_id); + case LLIMChiclet::TYPE_AD_HOC: + return getChicletPanel()->createChiclet(session_id); + case LLIMChiclet::TYPE_UNKNOWN: + break; + } + + return NULL; +} + +//virtual +void LLBottomTray::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) +{ + if (!getChicletPanel()) return; + + LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id); + if (!session) return; + + // no need to spawn chiclets for participants in P2P calls called through Avaline + if (session->isP2P() && session->isOtherParticipantAvaline()) return; + + if (getChicletPanel()->findChiclet(session_id)) return; + + LLIMChiclet* chiclet = createIMChiclet(session_id); + if(chiclet) + { + chiclet->setIMSessionName(name); + chiclet->setOtherParticipantId(other_participant_id); + + LLIMFloater::onIMChicletCreated(session_id); + + } + else + { + llerrs << "Could not create chiclet" << llendl; + } +} + +//virtual +void LLBottomTray::sessionRemoved(const LLUUID& session_id) +{ + if(getChicletPanel()) + { + // IM floater should be closed when session removed and associated chiclet closed + LLIMFloater* iMfloater = LLFloaterReg::findTypedInstance( + "impanel", session_id); + if (iMfloater != NULL) + { + iMfloater->closeFloater(); + } + + getChicletPanel()->removeChiclet(session_id); + } +} + +void LLBottomTray::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) +{ + //this is only needed in case of outgoing ad-hoc/group chat sessions + LLChicletPanel* chiclet_panel = getChicletPanel(); + if (chiclet_panel) + { + //it should be ad-hoc im chiclet or group im chiclet + LLChiclet* chiclet = chiclet_panel->findChiclet(old_session_id); + if (chiclet) chiclet->setSessionId(new_session_id); + } +} + +S32 LLBottomTray::getTotalUnreadIMCount() +{ + return getChicletPanel()->getTotalUnreadIMCount(); +} + +// virtual +void LLBottomTray::onChange(EStatusType status, const std::string &channelURI, bool proximal) +{ + // Time it takes to connect to voice channel might be pretty long, + // so don't expect user login or STATUS_VOICE_ENABLED to be followed by STATUS_JOINED. + BOOL enable = FALSE; + + switch (status) + { + // Do not add STATUS_VOICE_ENABLED because voice chat is + // inactive until STATUS_JOINED + case STATUS_JOINED: + enable = TRUE; + break; + default: + enable = FALSE; + break; + } + + // We have to enable/disable right and left parts of speak button separately (EXT-4648) + mSpeakBtn->setSpeakBtnEnabled(enable); + // skipped to avoid button blinking + if (status != STATUS_JOINING && status!= STATUS_LEFT_CHANNEL) + { + mSpeakBtn->setFlyoutBtnEnabled(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking()); + } +} + +void LLBottomTray::onMouselookModeOut() +{ + mIsInLiteMode = false; + mBottomTrayLite->setVisible(FALSE); + mNearbyChatBar->getChatBox()->setText(mBottomTrayLite->mNearbyChatBar->getChatBox()->getText()); + setVisible(TRUE); +} + +void LLBottomTray::onMouselookModeIn() +{ + setVisible(FALSE); + + // Attach the lite bottom tray + if (getParent() && mBottomTrayLite->getParent() != getParent()) + getParent()->addChild(mBottomTrayLite); + + mBottomTrayLite->setShape(getLocalRect()); + mBottomTrayLite->mNearbyChatBar->getChatBox()->setText(mNearbyChatBar->getChatBox()->getText()); + mBottomTrayLite->mGesturePanel->setVisible(gSavedSettings.getBOOL("ShowGestureButton")); + + mIsInLiteMode = true; +} + +//virtual +// setVisible used instead of onVisibilityChange, since LLAgent calls it on entering/leaving mouselook mode. +// If bottom tray is already visible in mouselook mode, then onVisibilityChange will not be called from setVisible(true), +void LLBottomTray::setVisible(BOOL visible) +{ + if (mIsInLiteMode) + { + mBottomTrayLite->setVisible(visible); + } + else + { + LLPanel::setVisible(visible); + } +} + +S32 LLBottomTray::notifyParent(const LLSD& info) +{ + if(info.has("well_empty")) // implementation of EXT-3397 + { + const std::string chiclet_name = info["well_name"]; + + // only "im_well" or "notification_well" names are expected. + // They are set in panel_bottomtray.xml in & + llassert("im_well" == chiclet_name || "notification_well" == chiclet_name); + + BOOL should_be_visible = !info["well_empty"]; + showWellButton("im_well" == chiclet_name ? RS_IM_WELL : RS_NOTIFICATION_WELL, should_be_visible); + return 1; + } + + if (info.has("action") && info["action"] == "resize") + { + const std::string& name = info["view_name"]; + + // expected only resize of nearby chatbar + if (mChatBarContainer->getName() != name) return LLPanel::notifyParent(info); + + const S32 new_width = info["new_width"]; + + processChatbarCustomization(new_width); + + return 2; + } + return LLPanel::notifyParent(info); +} + +void LLBottomTray::showBottomTrayContextMenu(S32 x, S32 y, MASK mask) +{ + // We should show BottomTrayContextMenu in last turn + if (mBottomTrayContextMenu && !LLMenuGL::sMenuContainer->getVisibleMenu()) + { + //there are no other context menu (IM chiclet etc ), so we can show BottomTrayContextMenu + + updateContextMenu(x, y, mask); + mBottomTrayContextMenu->buildDrawLabels(); + mBottomTrayContextMenu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, mBottomTrayContextMenu, x, y); + + } +} + +void LLBottomTray::updateContextMenu(S32 x, S32 y, MASK mask) +{ + LLUICtrl* edit_box = mNearbyChatBar->getChild("chat_box"); + + S32 local_x = x - mChatBarContainer->getRect().mLeft - edit_box->getRect().mLeft; + S32 local_y = y - mChatBarContainer->getRect().mBottom - edit_box->getRect().mBottom; + + bool in_edit_box = edit_box->pointInView(local_x, local_y); + + mBottomTrayContextMenu->setItemVisible("Separator", in_edit_box); + mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Cut", in_edit_box); + mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Copy", in_edit_box); + mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Paste", in_edit_box); + mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Delete", in_edit_box); + mBottomTrayContextMenu->setItemVisible("NearbyChatBar_Select_All", in_edit_box); +} + +void LLBottomTray::showGestureButton(BOOL visible) +{ + setTrayButtonVisibleIfPossible(RS_BUTTON_GESTURES, visible); +} + +void LLBottomTray::showMoveButton(BOOL visible) +{ + setTrayButtonVisibleIfPossible(RS_BUTTON_MOVEMENT, visible); +} + +void LLBottomTray::showCameraButton(BOOL visible) +{ + setTrayButtonVisibleIfPossible(RS_BUTTON_CAMERA, visible); +} + +void LLBottomTray::showSnapshotButton(BOOL visible) +{ + setTrayButtonVisibleIfPossible(RS_BUTTON_SNAPSHOT, visible); +} + +void LLBottomTray::showSpeakButton(bool visible) +{ + // Show/hide the button + setTrayButtonVisible(RS_BUTTON_SPEAK, visible); + + // and adjust other panels according to the occupied/freed space. + const S32 panel_width = mSpeakPanel->getRect().getWidth(); + if (visible) + { + processWidthDecreased(-panel_width); + } + else + { + processWidthIncreased(panel_width); + } +} + +void LLBottomTray::toggleMovementControls() +{ + if (mMovementButton) + mMovementButton->onCommit(); +} + +void LLBottomTray::toggleCameraControls() +{ + if (mCamButton) + mCamButton->onCommit(); +} + +BOOL LLBottomTray::postBuild() +{ + LLHints::registerHintTarget("bottom_tray", LLView::getHandle()); + LLHints::registerHintTarget("dest_guide_btn", getChild("destination_btn")->getHandle()); + LLHints::registerHintTarget("avatar_picker_btn", getChild("avatar_btn")->getHandle()); + + LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("NearbyChatBar.Action", boost::bind(&LLBottomTray::onContextMenuItemClicked, this, _2)); + LLUICtrl::EnableCallbackRegistry::currentRegistrar().add("NearbyChatBar.EnableMenuItem", boost::bind(&LLBottomTray::onContextMenuItemEnabled, this, _2)); + + mBottomTrayContextMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_bottomtray.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + gMenuHolder->addChild(mBottomTrayContextMenu); + + mNearbyChatBar = findChild("chat_bar"); + LLHints::registerHintTarget("chat_bar", mNearbyChatBar->LLView::getHandle()); + + mChatBarContainer = getChild("chat_bar_layout_panel"); + mNearbyCharResizeHandlePanel = getChild("chat_bar_resize_handle_panel"); + + mToolbarStack = getChild("toolbar_stack"); + mMovementButton = getChild("movement_btn"); + LLHints::registerHintTarget("move_btn", mMovementButton->getHandle()); + mCamButton = getChild("camera_btn"); + setRightMouseDownCallback(boost::bind(&LLBottomTray::showBottomTrayContextMenu,this, _2, _3,_4)); + + mSpeakPanel = getChild("speak_panel"); + mSpeakBtn = getChild("talk"); + + // Both parts of speak button should be initially disabled because + // it takes some time between logging in to world and connecting to voice channel. + mSpeakBtn->setSpeakBtnEnabled(false); + mSpeakBtn->setFlyoutBtnEnabled(false); + + // Localization tool doesn't understand custom buttons like + mSpeakBtn->setSpeakToolTip( getString("SpeakBtnToolTip") ); + mSpeakBtn->setShowToolTip( getString("VoiceControlBtnToolTip") ); + + // Registering Chat Bar to receive Voice client status change notifications. + LLVoiceClient::getInstance()->addObserver(this); + + mNearbyChatBar->getChatBox()->setContextMenu(NULL); + + mChicletPanel = getChild("chiclet_list"); + + initResizeStateContainers(); + + setButtonsControlsAndListeners(); + + initButtonsVisibility(); + + // update wells visibility: + showWellButton(RS_IM_WELL, !LLIMWellWindow::getInstance()->isWindowEmpty()); + showWellButton(RS_NOTIFICATION_WELL, !LLNotificationWellWindow::getInstance()->isWindowEmpty()); + + loadButtonsOrder(); + + LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(boost::bind(&update_build_button_enable_state)); + + return TRUE; +} + +//Drag-n-drop + +void LLBottomTray::onDraggableButtonMouseDown(LLUICtrl* ctrl, S32 x, S32 y) +{ + if (ctrl == NULL) return; + LLView* parent_view = ctrl->getParent(); + if(parent_view != NULL) + { + // we actually drag'n'drop panel (not button) in code, so have to find a parent + // of button which called this method on mouse down. + LLPanel* parent = dynamic_cast(parent_view); + // It may happen that we clicked not usual button, but button inside widget(speak, gesture) + // so we'll need to get a level higher to reach layout panel as a parent. + if(parent == NULL) parent = dynamic_cast(parent_view->getParent()); + if (parent && parent->getVisible()) + { + mDraggedItem = parent; + mCheckForDrag = true; + mStartX = x; + mStartY = y; + } + } +} + +LLPanel* LLBottomTray::findChildPanelByLocalCoords(S32 x, S32 y) +{ + LLPanel* ctrl = 0; + S32 screenX, screenY; + const child_list_t* list = mToolbarStack->getChildList(); + + localPointToScreen(x, y, &screenX, &screenY); + + // look for a child panel which contains the point (screenX, screenY) in it's rectangle + for (child_list_const_iter_t i = list->begin(); i != list->end(); ++i) + { + LLRect rect; + localRectToScreen((*i)->getRect(), &rect); + + if (rect.pointInRect(screenX, screenY)) + { + ctrl = dynamic_cast(*i); + break; + } + } + + return ctrl; +} + +void LLBottomTray::onDraggableButtonHover(S32 x, S32 y) +{ + // if mouse down on draggable item was done, check whether we should start DnD + if (mCheckForDrag) + { + // Start drag'n'drop if mouse cursor was dragged away frome mouse down location enough + if(sqrt((float)((mStartX-x)*(mStartX-x)+(mStartY-y)*(mStartY-y))) > DRAG_START_DISTANCE) + { + mDragStarted = true; + mCheckForDrag = false; + } + } + if (mDragStarted) + { + // Check whether the cursor is over draggable area, find which panel it is and set is as + // landing tab for drag'n'drop + if(isCursorOverDraggableArea(x, y)) + { + LLPanel* panel = findChildPanelByLocalCoords(x,y); + if (panel && panel != mDraggedItem) mLandingTab = panel; + gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROWDRAG); + } + else + { + gViewerWindow->getWindow()->setCursor(UI_CURSOR_NO); + } + } + else + { + // Reset cursor in case you move your mouse from the drag handle to a button. + getWindow()->setCursor(UI_CURSOR_ARROW); + + } +} + +bool LLBottomTray::isCursorOverDraggableArea(S32 x, S32 y) +{ + // Draggable area lasts from the nearby chat input resize handle + // to the chiclet area (exlusively). + bool result = getRect().pointInRect(x, y); + result = result && mNearbyCharResizeHandlePanel->calcScreenRect().mRight < x; + result = result && mChicletPanel->calcScreenRect().mRight > x; + return result; +} + +void LLBottomTray::updateButtonsOrdersAfterDnD() +{ + // *TODO: change implementation of this method to support simplify it + // (and according to future possible changes in the way button order is saved between sessions). + state_object_map_t::const_iterator it = mStateProcessedObjectMap.begin(); + state_object_map_t::const_iterator it_end = mStateProcessedObjectMap.end(); + EResizeState dragged_state = RS_NORESIZE; + EResizeState landing_state = RS_NORESIZE; + bool landing_state_found = false; + // Find states for dragged item and landing tab + for (; it != it_end; ++it) + { + if (it->second == mDraggedItem) + { + dragged_state = it->first; + } + else if (it->second == mLandingTab) + { + landing_state = it->first; + landing_state_found = true; + } + } + + if (dragged_state == RS_NORESIZE) + { + llwarns << "Cannot determine what button is being dragged" << llendl; + llassert(dragged_state != RS_NORESIZE); + return; + } + + lldebugs << "Will place " << resizeStateToString(dragged_state) + << " before " << resizeStateToString(landing_state) << llendl; + + // Update order of buttons according to drag'n'drop + mButtonsOrder.erase(std::find(mButtonsOrder.begin(), mButtonsOrder.end(), dragged_state)); + if (!landing_state_found && mLandingTab == getChild(PANEL_CHICLET_NAME)) + { + mButtonsOrder.push_back(dragged_state); + } + else + { + if (!landing_state_found) landing_state = RS_BUTTON_SPEAK; // just a random fallback + mButtonsOrder.insert(std::find(mButtonsOrder.begin(), mButtonsOrder.end(), landing_state), dragged_state); + } + + // Synchronize button process order with their order + resize_state_vec_t::const_iterator it1 = mButtonsOrder.begin(); + const resize_state_vec_t::const_iterator it_end1 = mButtonsOrder.end(); + resize_state_vec_t::iterator it2 = mButtonsProcessOrder.begin(); + for (; it1 != it_end1; ++it1) + { + // Skip Speak because it is not in mButtonsProcessOrder(it's the reason why mButtonsOrder was introduced). + // If any other draggable items will be added to bottomtray later, they should also be skipped here. + if (*it1 != RS_BUTTON_SPEAK) + { + *it2 = *it1; + ++it2; + } + } + + saveButtonsOrder(); +} + +void LLBottomTray::saveButtonsOrder() +{ + std::string user_dir = gDirUtilp->getLindenUserDir(); + if (user_dir.empty()) return; + + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); + LLSD settings_llsd; + int i = 0; + const resize_state_vec_t::const_iterator it_end = mButtonsOrder.end(); + // we use numbers as keys for map which is saved in file and contains resize states as its values + for (resize_state_vec_t::const_iterator it = mButtonsOrder.begin(); it != it_end; ++it, i++) + { + std::string str = llformat("%d", i); + settings_llsd[str] = *it; + } + llofstream file; + file.open(filename); + LLSDSerialize::toPrettyXML(settings_llsd, file); +} + +void LLBottomTray::loadButtonsOrder() +{ + // load per-resident sorting information + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SORTING_DATA_FILE_NAME); + + LLSD settings_llsd; + llifstream file; + file.open(filename); + if (!file.is_open()) return; + + LLSDSerialize::fromXML(settings_llsd, file); + + + mButtonsOrder.clear(); + mButtonsProcessOrder.clear(); + int i = 0; + // getting button order from file + for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); + iter != settings_llsd.endMap(); ++iter, ++i) + { + std::string str = llformat("%d", i); + EResizeState state = (EResizeState)settings_llsd[str].asInteger(); + mButtonsOrder.push_back(state); + // RS_BUTTON_SPEAK is skipped, because it shouldn't be in mButtonsProcessOrder (it does not hide or shrink). + if (state != RS_BUTTON_SPEAK) + { + mButtonsProcessOrder.push_back(state); + } + } + + // There are other panels in layout stack order of which is not saved. Also, panels order of which is saved, + // are already in layout stack but in wrong order. The most convenient way to place them is moving them + // to front one by one (because in this case we don't have to pass the panel before which we want to insert our + // panel to movePanel()). So panels are moved in order from the end of mButtonsOrder vector(reverse iterator is used). + const resize_state_vec_t::const_reverse_iterator it_end = mButtonsOrder.rend(); + // placing panels in layout stack according to button order which we loaded in previous for + for (resize_state_vec_t::const_reverse_iterator it = mButtonsOrder.rbegin(); it != it_end; ++it, ++i) + { + LLPanel* panel_to_move = getButtonPanel(*it); + mToolbarStack->movePanel(panel_to_move, NULL, true); // prepend + } + // Nearbychat is not stored in order settings file, but it must be the first of the panels, so moving it + // (along with its drag handle) manually here. + mToolbarStack->movePanel(getChild("chat_bar_resize_handle_panel"), NULL, true); + mToolbarStack->movePanel(mChatBarContainer, NULL, true); +} + +void LLBottomTray::onDraggableButtonMouseUp(LLUICtrl* ctrl, S32 x, S32 y) +{ + //if mouse up happened over area where drop is possible, change order of buttons + if (mLandingTab != NULL && mDraggedItem != NULL && mDragStarted) + { + if(isCursorOverDraggableArea(x, y)) + { + // change order of panels in layout stack + mToolbarStack->movePanel(mDraggedItem, (LLPanel*)mLandingTab); + // change order of buttons in order vectors + updateButtonsOrdersAfterDnD(); + } + } + gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW); + mDragStarted = false; + mDraggedItem = NULL; + mLandingTab = NULL; + mCheckForDrag = false; +} + +void LLBottomTray::draw() +{ + LLPanel::draw(); + if (mLandingTab) + { + static S32 w = mImageDragIndication->getWidth(); + static S32 h = mImageDragIndication->getHeight(); + LLRect rect = mLandingTab->calcScreenRect(); + mImageDragIndication->draw(rect.mLeft - w/2, rect.getHeight(), w, h); + } + getChild("show_profile_btn")->setToggleState(LLAvatarActions::profileVisible(gAgent.getID())); + + LLPanel* panel = LLSideTray::getInstance()->getPanel("panel_people"); + if (panel && panel->isInVisibleChain()) + { + getChild("show_people_button")->setToggleState(true); + } + else + { + getChild("show_people_button")->setToggleState(false); + } + + LLFloater* help_browser = (LLFloaterReg::findInstance("help_browser")); + bool help_floater_visible = (help_browser && help_browser->isInVisibleChain()); + + getChild("show_help_btn")->setToggleState(help_floater_visible); + + +} + +bool LLBottomTray::onContextMenuItemEnabled(const LLSD& userdata) +{ + std::string item = userdata.asString(); + LLLineEditor* edit_box = mNearbyChatBar->findChild("chat_box"); + + if (item == "can_cut") + { + return edit_box->canCut(); + } + else if (item == "can_copy") + { + return edit_box->canCopy(); + } + else if (item == "can_paste") + { + return edit_box->canPaste(); + } + else if (item == "can_delete") + { + return edit_box->canDoDelete(); + } + else if (item == "can_select_all") + { + return edit_box->canSelectAll() && (edit_box->getLength()>0); + } + return true; +} + + +void LLBottomTray::onContextMenuItemClicked(const LLSD& userdata) +{ + std::string item = userdata.asString(); + LLLineEditor* edit_box = mNearbyChatBar->findChild("chat_box"); + + if (item == "cut") + { + edit_box->cut(); + } + else if (item == "copy") + { + edit_box->copy(); + } + else if (item == "paste") + { + edit_box->paste(); + edit_box->setFocus(TRUE); + } + else if (item == "delete") + { + edit_box->doDelete(); + } + else if (item == "select_all") + { + edit_box->selectAll(); + } +} + +void LLBottomTray::log(LLView* panel, const std::string& descr) +{ + if (NULL == panel) return; + LLView* layout = panel->getParent(); + lldebugs << descr << ": " + << "panel: " << panel->getName() + << ", rect: " << panel->getRect() + + + << "layout: " << layout->getName() + << ", rect: " << layout->getRect() + << llendl + ; +} + +void LLBottomTray::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + static S32 debug_calling_number = 0; + lldebugs << "**************************************** " << ++debug_calling_number << llendl; + + S32 current_width = getRect().getWidth(); + S32 delta_width = width - current_width; + lldebugs << "Reshaping: " + << ", width: " << width + << ", cur width: " << current_width + << ", delta_width: " << delta_width + << ", called_from_parent: " << called_from_parent + << llendl; + + if (mNearbyChatBar) log(mNearbyChatBar, "before"); + if (mChicletPanel) log(mChicletPanel, "before"); + + // stores width size on which bottom tray is less than width required by its children. EXT-991 + static S32 extra_shrink_width = 0; + bool should_be_reshaped = true; + + if (mChicletPanel && mToolbarStack && mNearbyChatBar) + { + // Firstly, update layout stack to ensure we deal with correct panel sizes. + { + BOOL saved_anim = mToolbarStack->getAnimate(); + // Set chiclet panel to be autoresized by default. + mToolbarStack->updatePanelAutoResize(PANEL_CHICLET_NAME, TRUE); + // Disable animation to prevent layout updating in several frames. + mToolbarStack->setAnimate(FALSE); + // Force the updating of layout to reset panels collapse factor. + mToolbarStack->updateLayout(); + // Restore animate state. + mToolbarStack->setAnimate(saved_anim); + } + + // bottom tray is narrowed + if (delta_width < 0) + { + if (extra_shrink_width > 0) + { + // is world rect was extra shrunk and decreasing again only update this value + // to delta_width negative + extra_shrink_width -= delta_width; // use "-=" because delta_width is negative + should_be_reshaped = false; + } + else + { + extra_shrink_width = processWidthDecreased(delta_width); + + // increase new width to extra_shrink_width value to not reshape less than bottom tray minimum + width += extra_shrink_width; + } + } + // bottom tray is widen + else + { + if (extra_shrink_width > delta_width) + { + // Less than minimum width is more than increasing (delta_width) + // only reduce it value and make no reshape + extra_shrink_width -= delta_width; + should_be_reshaped = false; + } + else + { + if (extra_shrink_width > 0) + { + // If we have some extra shrink width let's reduce delta_width & width + delta_width -= extra_shrink_width; + width -= extra_shrink_width; + extra_shrink_width = 0; + } + processWidthIncreased(delta_width); + } + } + } + + if (should_be_reshaped) + { + lldebugs << "Reshape all children with width: " << width << llendl; + LLPanel::reshape(width, height, called_from_parent); + } + + if (mNearbyChatBar) log(mNearbyChatBar, "after"); + if (mChicletPanel) log(mChicletPanel, "after"); + + + // Restore width of the chatbar on first reshape. + // we can not to do this from postBuild because reshape is called from parent view on startup + // creation after it and reset width according to resize logic. + static bool needs_restore_custom_state = true; + if (mChatBarContainer && needs_restore_custom_state) + { + // restore custom width of chatbar panel. + S32 new_width = gSavedSettings.getS32("ChatBarCustomWidth"); + if (new_width > 0) + { + mDesiredNearbyChatWidth = new_width; + processChatbarCustomization(new_width); + mChatBarContainer->reshape(new_width, mChatBarContainer->getRect().getHeight()); + } + needs_restore_custom_state = false; + } + +} + +S32 LLBottomTray::processWidthDecreased(S32 delta_width) +{ + bool still_should_be_processed = true; + + const S32 chiclet_panel_width = mChicletPanel->getParent()->getRect().getWidth(); + const S32 chiclet_panel_min_width = mChicletPanel->getMinWidth(); + + // There are four steps of processing width decrease. If in one of them required width was reached, + // further are not needed. + // 1. Decreasing width of chiclet panel. + if (chiclet_panel_width > chiclet_panel_min_width) + { + // we have some space to decrease chiclet panel + S32 panel_delta_min = chiclet_panel_width - chiclet_panel_min_width; + + S32 delta_panel = llmin(-delta_width, panel_delta_min); + + lldebugs << "delta_width: " << delta_width + << ", panel_delta_min: " << panel_delta_min + << ", delta_panel: " << delta_panel + << llendl; + + // is chiclet panel width enough to process resizing? + delta_width += panel_delta_min; + + still_should_be_processed = delta_width < 0; + + mChicletPanel->getParent()->reshape(mChicletPanel->getParent()->getRect().getWidth() - delta_panel, mChicletPanel->getParent()->getRect().getHeight()); + log(mChicletPanel, "after processing panel decreasing via chiclet panel"); + + lldebugs << "RS_CHICLET_PANEL" + << ", delta_width: " << delta_width + << llendl; + } + + S32 buttons_freed_width = 0; + // 2. Decreasing width of buttons. + if (still_should_be_processed) + { + processShrinkButtons(delta_width, buttons_freed_width); + } + // 3. Decreasing width of nearby chat. + const S32 chatbar_panel_min_width = get_panel_min_width(mToolbarStack, mChatBarContainer); + const S32 chatbar_panel_width = mChatBarContainer->getRect().getWidth(); + if (still_should_be_processed && chatbar_panel_width > chatbar_panel_min_width) + { + // we have some space to decrease chatbar panel + S32 panel_delta_min = chatbar_panel_width - chatbar_panel_min_width; + + S32 delta_panel = llmin(-delta_width, panel_delta_min); + + // whether chatbar panel width is enough to process resizing? + delta_width += panel_delta_min; + + still_should_be_processed = delta_width < 0; + + // chatbar should only be shrunk here, not stretched + if(delta_panel > 0) + { + mChatBarContainer->reshape(mNearbyChatBar->getRect().getWidth() - delta_panel, mChatBarContainer->getRect().getHeight()); + } + + log(mNearbyChatBar, "after processing panel decreasing via nearby chatbar panel"); + + lldebugs << "RS_CHATBAR_INPUT" + << ", delta_panel: " << delta_panel + << ", delta_width: " << delta_width + << llendl; + } + + S32 extra_shrink_width = 0; + // 4. Hiding buttons if needed. + if (still_should_be_processed) + { + processHideButtons(delta_width, buttons_freed_width); + + if (delta_width < 0) + { + extra_shrink_width = -delta_width; + llwarns << "There is no enough width to reshape all children: " + << extra_shrink_width << llendl; + } + + if (buttons_freed_width > 0) + { + S32 nearby_needed_width = mDesiredNearbyChatWidth - mNearbyChatBar->getRect().getWidth(); + if (nearby_needed_width > 0) + { + S32 compensative_width = nearby_needed_width > buttons_freed_width ? buttons_freed_width : nearby_needed_width; + log(mNearbyChatBar, "before applying compensative width"); + mChatBarContainer->reshape(mChatBarContainer->getRect().getWidth() + compensative_width, mChatBarContainer->getRect().getHeight() ); + log(mNearbyChatBar, "after applying compensative width"); + lldebugs << buttons_freed_width << llendl; + } + } + } + + return extra_shrink_width; +} + +void LLBottomTray::processWidthIncreased(S32 delta_width) +{ + if (delta_width <= 0) return; + + const S32 chiclet_panel_width = mChicletPanel->getParent()->getRect().getWidth(); + static const S32 chiclet_panel_min_width = mChicletPanel->getMinWidth(); + + const S32 available_width_chiclet = chiclet_panel_width - chiclet_panel_min_width; + + // how many room we have to show hidden buttons + S32 total_available_width = delta_width + available_width_chiclet; + + lldebugs << "Processing extending, available width:" + << ", chiclets - " << available_width_chiclet + << ", total - " << total_available_width + << llendl; + + S32 available_width = total_available_width; + + processShowButtons(available_width); + + // if we have to show/extend some buttons but resized delta width is not enough... + S32 processed_width = total_available_width - available_width; + if (processed_width > delta_width) + { + // ... let's shrink nearby chat & chiclet panels + S32 required_to_process_width = processed_width; + + // 1. use delta width of resizing + required_to_process_width -= delta_width; + + // 2. use width available via decreasing of chiclet panel + if (required_to_process_width > 0) + { + mChicletPanel->getParent()->reshape(mChicletPanel->getParent()->getRect().getWidth() - required_to_process_width, mChicletPanel->getParent()->getRect().getHeight()); + log(mChicletPanel, "after applying compensative width for chiclets: "); + lldebugs << required_to_process_width << llendl; + } + + } + + // shown buttons take some space, rest should be processed by nearby chatbar & chiclet panels + delta_width -= processed_width; + + + // how many space can nearby chatbar take? + S32 chatbar_panel_width_ = mChatBarContainer->getRect().getWidth(); + if (delta_width > 0 && chatbar_panel_width_ < mDesiredNearbyChatWidth) + { + S32 delta_panel_max = mDesiredNearbyChatWidth - chatbar_panel_width_; + S32 delta_panel = llmin(delta_width, delta_panel_max); + lldebugs << "Unprocesed delta width: " << delta_width + << ", can be applied to chatbar: " << delta_panel_max + << ", will be applied: " << delta_panel + << llendl; + + delta_width -= delta_panel_max; + mChatBarContainer->reshape(chatbar_panel_width_ + delta_panel, mChatBarContainer->getRect().getHeight()); + log(mNearbyChatBar, "applied unprocessed delta width"); + } + if (delta_width > 0) + { + processExtendButtons(delta_width); + } +} + +void LLBottomTray::processShowButtons(S32& available_width) +{ + // process buttons from left to right + resize_state_vec_t::const_iterator it = mButtonsProcessOrder.begin(); + const resize_state_vec_t::const_iterator it_end = mButtonsProcessOrder.end(); + + for (; it != it_end; ++it) + { + // is there available space? + if (available_width <= 0) break; + + // try to show next button + processShowButton(*it, available_width); + } +} + +bool LLBottomTray::processShowButton(EResizeState shown_object_type, S32& available_width) +{ + lldebugs << "Trying to show object type: " << shown_object_type << llendl; + + LLPanel* panel = getButtonPanel(shown_object_type); + if (NULL == panel) + { + lldebugs << "There is no object to process for state: " << shown_object_type << llendl; + return false; + } + bool can_be_shown = canButtonBeShown(shown_object_type); + if (can_be_shown) + { + //validate if we have enough room to show this button + const S32 required_width = panel->getRect().getWidth(); + can_be_shown = available_width >= required_width; + if (can_be_shown) + { + available_width -= required_width; + + setTrayButtonVisible(shown_object_type, true); + + lldebugs << "processed object type: " << shown_object_type + << ", rest available width: " << available_width + << llendl; + mResizeState &= ~shown_object_type; + } + } + return can_be_shown; +} + +void LLBottomTray::processHideButtons(S32& required_width, S32& buttons_freed_width) +{ + // process buttons from right to left + resize_state_vec_t::const_reverse_iterator it = mButtonsProcessOrder.rbegin(); + const resize_state_vec_t::const_reverse_iterator it_end = mButtonsProcessOrder.rend(); + + for (; it != it_end; ++it) + { + // is it still necessary to hide a button? + if (required_width >= 0) break; + + // try to hide next button + processHideButton(*it, required_width, buttons_freed_width); + } +} + +void LLBottomTray::processHideButton(EResizeState processed_object_type, S32& required_width, S32& buttons_freed_width) +{ + lldebugs << "Trying to hide object type: " << processed_object_type << llendl; + LLPanel* panel = getButtonPanel(processed_object_type); + if (NULL == panel) + { + lldebugs << "There is no object to process for state: " << processed_object_type << llendl; + return; + } + + if (panel->getVisible()) + { + required_width += panel->getRect().getWidth(); + + if (required_width > 0) + { + buttons_freed_width += required_width; + } + + setTrayButtonVisible(processed_object_type, false); + + mResizeState |= processed_object_type; + + lldebugs << "processing object type: " << processed_object_type + << ", buttons_freed_width: " << buttons_freed_width + << llendl; + } +} + +void LLBottomTray::processShrinkButtons(S32& required_width, S32& buttons_freed_width) +{ + // process buttons from right to left + resize_state_vec_t::const_reverse_iterator it = mButtonsProcessOrder.rbegin(); + const resize_state_vec_t::const_reverse_iterator it_end = mButtonsProcessOrder.rend(); + + // iterate through buttons in the mButtonsProcessOrder first + for (; it != it_end; ++it) + { + // is it still necessary to hide a button? + if (required_width >= 0) break; + + // try to shrink next button + processShrinkButton(*it, required_width); + } + + // then shrink Speak button + if (required_width < 0) + { + S32 panel_min_width = 0; + std::string panel_name = mSpeakPanel->getName(); + bool success = mToolbarStack->getPanelMinSize(panel_name, &panel_min_width); + if (!success) + { + lldebugs << "Panel was not found to get its min width: " << panel_name << llendl; + } + else + { + S32 panel_width = mSpeakPanel->getRect().getWidth(); + S32 possible_shrink_width = panel_width - panel_min_width; + + if (possible_shrink_width > 0) + { + mSpeakBtn->setLabelVisible(false); + mSpeakPanel->reshape(panel_width - possible_shrink_width, mSpeakPanel->getRect().getHeight()); + + required_width += possible_shrink_width; + + if (required_width > 0) + { + buttons_freed_width += required_width; + } + + lldebugs << "Shrunk Speak button panel: " << panel_name + << ", shrunk width: " << possible_shrink_width + << ", rest width to process: " << required_width + << llendl; + } + } + } +} + +void LLBottomTray::processShrinkButton(EResizeState processed_object_type, S32& required_width) +{ + LLPanel* panel = getButtonPanel(processed_object_type); + if (NULL == panel) + { + lldebugs << "There is no object to process for type: " << processed_object_type << llendl; + return; + } + + if (panel->getVisible()) + { + S32 panel_width = panel->getRect().getWidth(); + S32 panel_min_width = 0; + std::string panel_name = panel->getName(); + bool success = mToolbarStack->getPanelMinSize(panel_name, &panel_min_width); + S32 possible_shrink_width = panel_width - panel_min_width; + + if (!success) + { + lldebugs << "Panel was not found to get its min width: " << panel_name << llendl; + } + // we have some space to free by shrinking the button + else if (possible_shrink_width > 0) + { + // let calculate real width to shrink + + // 1. apply all possible width + required_width += possible_shrink_width; + + // 2. it it is too much... + if (required_width > 0) + { + // reduce applied shrunk width to the excessive value. + possible_shrink_width -= required_width; + required_width = 0; + } + panel->reshape(panel_width - possible_shrink_width, panel->getRect().getHeight()); + + lldebugs << "Shrunk panel: " << panel_name + << ", shrunk width: " << possible_shrink_width + << ", rest width to process: " << required_width + << llendl; + } + } +} + + +void LLBottomTray::processExtendButtons(S32& available_width) +{ + // do not allow extending any buttons if we have some buttons hidden via resize + if (mResizeState & RS_BUTTONS_CAN_BE_HIDDEN) return; + + // process buttons from left to right + resize_state_vec_t::const_iterator it = mButtonsProcessOrder.begin(); + const resize_state_vec_t::const_iterator it_end = mButtonsProcessOrder.end(); + + // iterate through buttons in the mButtonsProcessOrder first + for (; it != it_end; ++it) + { + // is there available space? + if (available_width <= 0) break; + + // try to extend next button + processExtendButton(*it, available_width); + } + + const S32 chiclet_panel_width = mChicletPanel->getParent()->getRect().getWidth(); + static const S32 chiclet_panel_min_width = mChicletPanel->getMinWidth(); + const S32 available_width_chiclet = chiclet_panel_width - chiclet_panel_min_width; + + // then try to extend Speak button + if (available_width > 0 || available_width_chiclet > 0) + { + S32 panel_max_width = mObjectDefaultWidthMap[RS_BUTTON_SPEAK]; + S32 panel_width = mSpeakPanel->getRect().getWidth(); + S32 possible_extend_width = panel_max_width - panel_width; + + if (possible_extend_width >= 0 && possible_extend_width <= available_width + available_width_chiclet) // HACK: this button doesn't change size so possible_extend_width will be 0 + { + mSpeakBtn->setLabelVisible(true); + mSpeakPanel->reshape(panel_max_width, mSpeakPanel->getRect().getHeight()); + log(mSpeakBtn, "speak button is extended"); + + if( available_width > possible_extend_width) + { + available_width -= possible_extend_width; + } + else + { + S32 required_width = possible_extend_width - available_width; + available_width = 0; + mChicletPanel->getParent()->reshape(mChicletPanel->getParent()->getRect().getWidth() - required_width, mChicletPanel->getParent()->getRect().getHeight()); + } + lldebugs << "Extending Speak button panel: " << mSpeakPanel->getName() + << ", extended width: " << possible_extend_width + << ", rest width to process: " << available_width + << llendl; + } + } +} + +void LLBottomTray::processExtendButton(EResizeState processed_object_type, S32& available_width) +{ + LLPanel* panel = getButtonPanel(processed_object_type); + if (NULL == panel) + { + lldebugs << "There is no object to process for type: " << processed_object_type << llendl; + return; + } + + if (!panel->getVisible()) return; + + S32 panel_max_width = mObjectDefaultWidthMap[processed_object_type]; + S32 panel_width = panel->getRect().getWidth(); + S32 possible_extend_width = panel_max_width - panel_width; + + if (possible_extend_width > 0) + { + // let calculate real width to extend + + // 1. apply all possible width + available_width -= possible_extend_width; + + // 2. it it is too much... + if (available_width < 0) + { + // reduce applied extended width to the excessive value. + possible_extend_width += available_width; + available_width = 0; + } + panel->reshape(panel_width + possible_extend_width, panel->getRect().getHeight()); + + lldebugs << "Extending panel: " << panel->getName() + << ", extended width: " << possible_extend_width + << ", rest width to process: " << available_width + << llendl; + } +} + +bool LLBottomTray::canButtonBeShown(EResizeState processed_object_type) const +{ + // 0. Check if passed button was previously hidden on resize + bool can_be_shown = mResizeState & processed_object_type; + if (can_be_shown) + { + // Yes, it was. Lets now check that all buttons before it (that can be hidden on resize) + // are already shown + + // process buttons in direct order (from left to right) + resize_state_vec_t::const_iterator it = mButtonsProcessOrder.begin(); + const resize_state_vec_t::const_iterator it_end = mButtonsProcessOrder.end(); + + // 1. Find and accumulate all buttons types before one passed into the method. + MASK buttons_before_mask = RS_NORESIZE; + for (; it != it_end; ++it) + { + const EResizeState button_type = *it; + if (button_type == processed_object_type) break; + + buttons_before_mask |= button_type; + } + + // 2. Check if some previous buttons are still hidden on resize + can_be_shown = !(buttons_before_mask & mResizeState); + } + return can_be_shown; +} + +void LLBottomTray::initResizeStateContainers() +{ + // init map with objects should be processed for each type + mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_SPEAK, getChild("speak_panel"))); + mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_GESTURES, getChild("gesture_panel"))); + mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_MOVEMENT, getChild("movement_panel"))); + mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_CAMERA, getChild("cam_panel"))); + mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_SNAPSHOT, getChild("snapshot_panel"))); + mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_BUILD, getChild("build_btn_panel"))); + mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_SEARCH, getChild("search_btn_panel"))); + mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_WORLD_MAP, getChild("world_map_btn_panel"))); + mStateProcessedObjectMap.insert(std::make_pair(RS_BUTTON_MINI_MAP, getChild("mini_map_btn_panel"))); + + // init an order of processed buttons + mButtonsProcessOrder.push_back(RS_BUTTON_GESTURES); + mButtonsProcessOrder.push_back(RS_BUTTON_MOVEMENT); + mButtonsProcessOrder.push_back(RS_BUTTON_CAMERA); + mButtonsProcessOrder.push_back(RS_BUTTON_SNAPSHOT); + mButtonsProcessOrder.push_back(RS_BUTTON_BUILD); + mButtonsProcessOrder.push_back(RS_BUTTON_SEARCH); + mButtonsProcessOrder.push_back(RS_BUTTON_WORLD_MAP); + mButtonsProcessOrder.push_back(RS_BUTTON_MINI_MAP); + + mButtonsOrder.push_back(RS_BUTTON_SPEAK); + mButtonsOrder.insert(mButtonsOrder.end(), mButtonsProcessOrder.begin(), mButtonsProcessOrder.end()); + + // init default widths + + // process buttons that can be hidden on resize... + resize_state_vec_t::const_iterator it = mButtonsProcessOrder.begin(); + const resize_state_vec_t::const_iterator it_end = mButtonsProcessOrder.end(); + + for (; it != it_end; ++it) + { + const EResizeState button_type = *it; + // is there an appropriate object? + LLPanel* button_panel = getButtonPanel(button_type); + if (!button_panel) continue; + + // set default width for it. + mObjectDefaultWidthMap[button_type] = button_panel->getRect().getWidth(); + } + + // ... and add Speak button because it also can be shrunk. + mObjectDefaultWidthMap[RS_BUTTON_SPEAK] = mSpeakPanel->getRect().getWidth(); +} + +// this method must be called before restoring of the chat entry field on startup +// because it resets chatbar's width according to resize logic. +void LLBottomTray::initButtonsVisibility() +{ + setVisibleAndFitWidths(RS_BUTTON_SPEAK, gSavedSettings.getBOOL("EnableVoiceChat")); + setVisibleAndFitWidths(RS_BUTTON_GESTURES, gSavedSettings.getBOOL("ShowGestureButton")); + setVisibleAndFitWidths(RS_BUTTON_MOVEMENT, gSavedSettings.getBOOL("ShowMoveButton")); + setVisibleAndFitWidths(RS_BUTTON_CAMERA, gSavedSettings.getBOOL("ShowCameraButton")); + setVisibleAndFitWidths(RS_BUTTON_SNAPSHOT, gSavedSettings.getBOOL("ShowSnapshotButton")); + setVisibleAndFitWidths(RS_BUTTON_BUILD, gSavedSettings.getBOOL("ShowBuildButton")); + setVisibleAndFitWidths(RS_BUTTON_SEARCH, gSavedSettings.getBOOL("ShowSearchButton")); + setVisibleAndFitWidths(RS_BUTTON_WORLD_MAP, gSavedSettings.getBOOL("ShowWorldMapButton")); + setVisibleAndFitWidths(RS_BUTTON_MINI_MAP, gSavedSettings.getBOOL("ShowMiniMapButton")); +} + +void LLBottomTray::setButtonsControlsAndListeners() +{ + gSavedSettings.getControl("EnableVoiceChat")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_SPEAK, _2)); + gSavedSettings.getControl("ShowGestureButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_GESTURES, _2)); + gSavedSettings.getControl("ShowMoveButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_MOVEMENT, _2)); + gSavedSettings.getControl("ShowCameraButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_CAMERA, _2)); + gSavedSettings.getControl("ShowSnapshotButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_SNAPSHOT, _2)); + gSavedSettings.getControl("ShowBuildButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_BUILD, _2)); + gSavedSettings.getControl("ShowSearchButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_SEARCH, _2)); + gSavedSettings.getControl("ShowWorldMapButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_WORLD_MAP, _2)); + gSavedSettings.getControl("ShowMiniMapButton")->getSignal()->connect(boost::bind(&LLBottomTray::toggleShowButton, RS_BUTTON_MINI_MAP, _2)); + + + LLButton* build_btn = getChild("build_btn"); + // set control name for Build button. It is not enough to link it with Button.SetFloaterToggle in xml + std::string vis_control_name = LLFloaterReg::declareVisibilityControl("build"); + // Set the button control value (toggle state) to the floater visibility control (Sets the value as well) + build_btn->setControlVariable(LLFloater::getControlGroup()->getControl(vis_control_name)); +} + +bool LLBottomTray::toggleShowButton(LLBottomTray::EResizeState button_type, const LLSD& new_visibility) +{ + if (LLBottomTray::instanceExists()) + { + LLBottomTray::getInstance()->setTrayButtonVisibleIfPossible(button_type, new_visibility.asBoolean()); + } + return true; +} + +void LLBottomTray::setTrayButtonVisible(EResizeState shown_object_type, bool visible) +{ + LLPanel* panel = getButtonPanel(shown_object_type); + if (NULL == panel) + { + lldebugs << "There is no object to show for state: " << shown_object_type << llendl; + return; + } + + panel->setVisible(visible); +} + +void LLBottomTray::setTrayButtonVisibleIfPossible(EResizeState shown_object_type, bool visible, bool raise_notification) +{ + if (!setVisibleAndFitWidths(shown_object_type, visible) && visible && raise_notification) + { + LLNotificationsUtil::add("BottomTrayButtonCanNotBeShown", + LLSD(), + LLSD(), + LLNotificationFunctorRegistry::instance().DONOTHING); + } +} + +bool LLBottomTray::setVisibleAndFitWidths(EResizeState object_type, bool visible) +{ + // The Speak button is treated specially: if voice is enabled, + // the button should be displayed no matter how much space we've got. + if (object_type == RS_BUTTON_SPEAK) + { + showSpeakButton(visible); + return true; + } + + LLPanel* cur_panel = getButtonPanel(object_type); + if (NULL == cur_panel) + { + lldebugs << "There is no object to process for state: " << object_type << llendl; + return false; + } + + bool is_set = true; + + if (visible) + { + // Assume that only chiclet panel can be auto-resized + const S32 available_width = + mChicletPanel->getParent()->getRect().getWidth() - mChicletPanel->getMinWidth(); + + S32 preferred_width = mObjectDefaultWidthMap[object_type]; + S32 current_width = cur_panel->getRect().getWidth(); + S32 result_width = 0; + bool decrease_width = false; + + // Mark this button to be shown + mResizeState |= object_type; + + if (preferred_width > 0 && available_width >= preferred_width) + { + result_width = preferred_width; + } + else if (available_width >= current_width) + { + result_width = current_width; + } + else + { + // Calculate the possible shrunk width as difference between current and minimal widths + const S32 chatbar_shrunk_width = + mChatBarContainer->getRect().getWidth() - get_panel_min_width(mToolbarStack, mChatBarContainer); + + S32 sum_of_min_widths = get_panel_min_width(mToolbarStack, mSpeakPanel); + S32 sum_of_curr_widths = get_curr_width(mSpeakPanel); + + resize_state_vec_t::const_iterator it = mButtonsProcessOrder.begin(); + const resize_state_vec_t::const_iterator it_end = mButtonsProcessOrder.end(); + + for (; it != it_end; ++it) + { + LLPanel* cur_panel = getButtonPanel(*it); + sum_of_min_widths += get_panel_min_width(mToolbarStack, cur_panel); + sum_of_curr_widths += get_curr_width(cur_panel); + } + + const S32 possible_shrunk_width = + chatbar_shrunk_width + (sum_of_curr_widths - sum_of_min_widths); + + // Minimal width of current panel + S32 minimal_width = 0; + mToolbarStack->getPanelMinSize(cur_panel->getName(), &minimal_width); + + if ( (available_width + possible_shrunk_width) >= minimal_width) + { + // There is enough space for minimal width, but set the result_width + // to preferred_width so buttons widths decreasing will be done in predefined order + result_width = (preferred_width > 0) ? preferred_width : current_width; + decrease_width = true; + } + else + { + // Nothing can be done, give up... + return false; + } + } + + if (result_width != current_width) + { + cur_panel->reshape(result_width, cur_panel->getRect().getHeight()); + current_width = result_width; + } + + is_set = processShowButton(object_type, current_width); + + // Shrink buttons if needed + if (is_set && decrease_width) + { + processWidthDecreased( -result_width); + } + } + else + { + const S32 delta_width = get_curr_width(cur_panel); + + setTrayButtonVisible(object_type, false); + + // Mark button NOT to show while future bottom tray extending + mResizeState &= ~object_type; + + // Extend other buttons if need + if (delta_width) + { + processWidthIncreased(delta_width); + } + } + return is_set; +} + +LLPanel* LLBottomTray::getButtonPanel(EResizeState button_type) +{ + // Don't use the operator[] because it inserts a NULL value if the key is not found. + if (mStateProcessedObjectMap.count(button_type) == 0) + { + llwarns << "Cannot find a panel for " << resizeStateToString(button_type) << llendl; + llassert(mStateProcessedObjectMap.count(button_type) == 1); + return NULL; + } + + return mStateProcessedObjectMap[button_type]; +} + +void LLBottomTray::showWellButton(EResizeState object_type, bool visible) +{ + llassert( ((RS_NOTIFICATION_WELL | RS_IM_WELL) & object_type) == object_type ); + + const std::string panel_name = RS_IM_WELL == object_type ? "im_well_panel" : "notification_well_panel"; + + LLView * panel = getChild(panel_name); + + // if necessary visibility is set nothing to do here + if (panel->getVisible() == (BOOL)visible) return; + + S32 panel_width = panel->getRect().getWidth(); + panel->setVisible(visible); + + if (visible) + { + // method assumes that input param is a negative value + processWidthDecreased(-panel_width); + } + else + { + processWidthIncreased(panel_width); + } +} + +void LLBottomTray::processChatbarCustomization(S32 new_width) +{ + if (NULL == mNearbyChatBar) return; + + const S32 delta_width = mChatBarContainer->getRect().getWidth() - new_width; + + if (delta_width == 0) return; + + mDesiredNearbyChatWidth = new_width; + + LLView * chiclet_layout_panel = mChicletPanel->getParent(); + const S32 chiclet_min_width = get_panel_min_width(mToolbarStack, chiclet_layout_panel); + const S32 chiclet_panel_width = chiclet_layout_panel->getRect().getWidth(); + const S32 available_chiclet_shrink_width = chiclet_panel_width - chiclet_min_width; + llassert(available_chiclet_shrink_width >= 0); + + if (delta_width > 0) // panel gets narrowly + { + S32 total_possible_width = delta_width + available_chiclet_shrink_width; + processShowButtons(total_possible_width); + processExtendButtons(total_possible_width); + } + // here (delta_width < 0) // panel gets wider + else //if (-delta_width > available_chiclet_shrink_width) + { + S32 required_width = delta_width + available_chiclet_shrink_width; + S32 buttons_freed_width = 0; + processShrinkButtons(required_width, buttons_freed_width); + processHideButtons(required_width, buttons_freed_width); + } +} + +// static +std::string LLBottomTray::resizeStateToString(EResizeState state) +{ + switch (state) + { + case RS_NORESIZE: return "RS_NORESIZE"; + case RS_CHICLET_PANEL: return "RS_CHICLET_PANEL"; + case RS_CHATBAR_INPUT: return "RS_CHATBAR_INPUT"; + case RS_BUTTON_SNAPSHOT: return "RS_BUTTON_SNAPSHOT"; + case RS_BUTTON_CAMERA: return "RS_BUTTON_CAMERA"; + case RS_BUTTON_MOVEMENT: return "RS_BUTTON_MOVEMENT"; + case RS_BUTTON_GESTURES: return "RS_BUTTON_GESTURES"; + case RS_BUTTON_SPEAK: return "RS_BUTTON_SPEAK"; + case RS_IM_WELL: return "RS_IM_WELL"; + case RS_NOTIFICATION_WELL: return "RS_NOTIFICATION_WELL"; + case RS_BUTTON_BUILD: return "RS_BUTTON_BUILD"; + case RS_BUTTON_SEARCH: return "RS_BUTTON_SEARCH"; + case RS_BUTTON_WORLD_MAP: return "RS_BUTTON_WORLD_MAP"; + case RS_BUTTON_MINI_MAP: return "RS_BUTTON_MINI_MAP"; + case RS_BUTTONS_CAN_BE_HIDDEN: return "RS_BUTTONS_CAN_BE_HIDDEN"; + // No default to track additions. + } + return "UNKNOWN_BUTTON"; +} + +//EOF diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index dc98170049..04e5f5e9e0 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -116,6 +116,7 @@ public: void showMoveButton(BOOL visible); void showCameraButton(BOOL visible); void showSnapshotButton(BOOL visible); + void showSpeakButton(bool visible); void toggleMovementControls(); void toggleCameraControls(); @@ -390,6 +391,13 @@ private: */ bool setVisibleAndFitWidths(EResizeState object_type, bool visible); + /** + * Get panel containing the given button. + * + * @see mStateProcessedObjectMap + */ + LLPanel* getButtonPanel(EResizeState button_type); + /** * Shows/hides panel with specified well button (IM or Notification) * @@ -409,12 +417,21 @@ private: */ void processChatbarCustomization(S32 new_width); + /// Get button name for debugging. + static std::string resizeStateToString(EResizeState state); + /// Buttons automatically hidden due to lack of space. MASK mResizeState; + /** + * Mapping of button types to the layout panels the buttons are wrapped in. + * + * Used by getButtonPanel(). + */ typedef std::map state_object_map_t; state_object_map_t mStateProcessedObjectMap; + /// Default (maximum) widths of the layout panels. typedef std::map state_object_width_map_t; state_object_width_map_t mObjectDefaultWidthMap; @@ -424,6 +441,7 @@ private: * Contains order in which child buttons should be processed in show/hide, extend/shrink methods. */ resize_state_vec_t mButtonsProcessOrder; + /** * Contains order in which child buttons are shown. * It traces order of all bottomtray buttons that may change place via drag'n'drop and should @@ -451,6 +469,7 @@ protected: LLSpeakButton* mSpeakBtn; LLNearbyChatBar* mNearbyChatBar; LLLayoutPanel* mChatBarContainer; + LLPanel* mNearbyCharResizeHandlePanel; LLLayoutStack* mToolbarStack; LLMenuGL* mBottomTrayContextMenu; LLButton* mCamButton; diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp index 252c7b51ae..c95b046707 100644 --- a/indra/newview/llfloaterauction.cpp +++ b/indra/newview/llfloaterauction.cpp @@ -351,8 +351,8 @@ void LLFloaterAuction::doResetParcel() body["media_height"] = (S32) 0; body["auto_scale"] = (S32) 0; body["media_loop"] = (S32) 0; - body["obscure_media"] = (S32) 0; - body["obscure_music"] = (S32) 0; + body["obscure_media"] = (S32) 0; // OBSOLETE - no longer used + body["obscure_music"] = (S32) 0; // OBSOLETE - no longer used body["media_id"] = LLUUID::null; body["group_id"] = MAINTENANCE_GROUP_ID; // Use maintenance group body["pass_price"] = (S32) 10; // Defaults to $10 diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 370bf05bf7..364fbad193 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -220,6 +220,8 @@ BOOL LLFloaterTools::postBuild() mRadioGroupEdit = getChild("edit_radio_group"); mBtnGridOptions = getChild("Options..."); mTitleMedia = getChild("title_media"); + mBtnLink = getChild("link_btn"); + mBtnUnlink = getChild("unlink_btn"); mCheckSelectIndividual = getChild("checkbox edit linked parts"); getChild("checkbox edit linked parts")->setValue((BOOL)gSavedSettings.getBOOL("EditLinkedParts")); @@ -315,6 +317,9 @@ LLFloaterTools::LLFloaterTools(const LLSD& key) mBtnRotateReset(NULL), mBtnRotateRight(NULL), + mBtnLink(NULL), + mBtnUnlink(NULL), + mBtnDelete(NULL), mBtnDuplicate(NULL), mBtnDuplicateInPlace(NULL), @@ -341,7 +346,7 @@ LLFloaterTools::LLFloaterTools(const LLSD& key) mNeedMediaTitle(TRUE) { gFloaterTools = this; - + setAutoFocus(FALSE); mFactoryMap["General"] = LLCallbackMap(createPanelPermissions, this);//LLPanelPermissions mFactoryMap["Object"] = LLCallbackMap(createPanelObject, this);//LLPanelObject @@ -366,6 +371,9 @@ LLFloaterTools::LLFloaterTools(const LLSD& key) mCommitCallbackRegistrar.add("BuildTool.DeleteMedia", boost::bind(&LLFloaterTools::onClickBtnDeleteMedia,this)); mCommitCallbackRegistrar.add("BuildTool.EditMedia", boost::bind(&LLFloaterTools::onClickBtnEditMedia,this)); + mCommitCallbackRegistrar.add("BuildTool.LinkObjects", boost::bind(&LLSelectMgr::linkObjects, LLSelectMgr::getInstance())); + mCommitCallbackRegistrar.add("BuildTool.UnlinkObjects", boost::bind(&LLSelectMgr::unlinkObjects, LLSelectMgr::getInstance())); + } LLFloaterTools::~LLFloaterTools() @@ -566,6 +574,12 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask) bool linked_parts = gSavedSettings.getBOOL("EditLinkedParts"); getChildView("RenderingCost")->setVisible( !linked_parts && (edit_visible || focus_visible || move_visible) && sShowObjectCost); + mBtnLink->setVisible(edit_visible); + mBtnUnlink->setVisible(edit_visible); + + mBtnLink->setEnabled(LLSelectMgr::instance().enableLinkObjects()); + mBtnUnlink->setEnabled(LLSelectMgr::instance().enableUnlinkObjects()); + if (mCheckSelectIndividual) { mCheckSelectIndividual->setVisible(edit_visible); diff --git a/indra/newview/llfloatertools.h b/indra/newview/llfloatertools.h index 87c3d2ab47..fd81a75397 100644 --- a/indra/newview/llfloatertools.h +++ b/indra/newview/llfloatertools.h @@ -135,6 +135,8 @@ public: LLRadioGroup* mRadioGroupEdit; LLCheckBoxCtrl *mCheckSelectIndividual; + LLButton* mBtnLink; + LLButton* mBtnUnlink; LLCheckBoxCtrl* mCheckSnapToGrid; LLButton* mBtnGridOptions; diff --git a/indra/newview/llfriendcard.cpp b/indra/newview/llfriendcard.cpp index e9f1e3bc22..70e789f490 100644 --- a/indra/newview/llfriendcard.cpp +++ b/indra/newview/llfriendcard.cpp @@ -28,6 +28,7 @@ #include "llfriendcard.h" +#include "llagent.h" #include "llavatarnamecache.h" #include "llinventory.h" #include "llinventoryfunctions.h" @@ -290,58 +291,6 @@ void LLFriendCardsManager::syncFriendCardsFolders() boost::bind(&LLFriendCardsManager::ensureFriendsFolderExists, this)); } -void LLFriendCardsManager::collectFriendsLists(folderid_buddies_map_t& folderBuddiesMap) const -{ - folderBuddiesMap.clear(); - - static bool syncronize_friends_folders = true; - if (syncronize_friends_folders) - { - // Checks whether "Friends" and "Friends/All" folders exist in "Calling Cards" folder, - // fetches their contents if needed and synchronizes it with buddies list. - // If the folders are not found they are created. - LLFriendCardsManager::instance().syncFriendCardsFolders(); - syncronize_friends_folders = false; - } - - - LLInventoryModel::cat_array_t* listFolders; - LLInventoryModel::item_array_t* items; - - // get folders in the Friend folder. Items should be NULL due to Cards should be in lists. - gInventory.getDirectDescendentsOf(findFriendFolderUUIDImpl(), listFolders, items); - - if (NULL == listFolders) - return; - - LLInventoryModel::cat_array_t::const_iterator itCats; // to iterate Friend Lists (categories) - LLInventoryModel::item_array_t::const_iterator itBuddy; // to iterate Buddies in each List - LLInventoryModel::cat_array_t* fakeCatsArg; - for (itCats = listFolders->begin(); itCats != listFolders->end(); ++itCats) - { - if (items) - items->clear(); - - // *HACK: Only Friends/All content will be shown for now - // *TODO: Remove this hack, implement sorting if it will be needded by spec. - if ((*itCats)->getUUID() != findFriendAllSubfolderUUIDImpl()) - continue; - - gInventory.getDirectDescendentsOf((*itCats)->getUUID(), fakeCatsArg, items); - - if (NULL == items) - continue; - - uuid_vec_t buddyUUIDs; - for (itBuddy = items->begin(); itBuddy != items->end(); ++itBuddy) - { - buddyUUIDs.push_back((*itBuddy)->getCreatorUUID()); - } - - folderBuddiesMap.insert(make_pair((*itCats)->getUUID(), buddyUUIDs)); - } -} - /************************************************************************/ /* Private Methods */ @@ -499,23 +448,43 @@ void LLFriendCardsManager::syncFriendsFolder() LLAvatarTracker::buddy_map_t all_buddies; LLAvatarTracker::instance().copyBuddyList(all_buddies); - // 1. Remove Friend Cards for non-friends + // 1. Check if own calling card exists LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; - gInventory.collectDescendents(findFriendAllSubfolderUUIDImpl(), cats, items, LLInventoryModel::EXCLUDE_TRASH); + LLUUID friends_all_folder_id = findFriendAllSubfolderUUIDImpl(); + gInventory.collectDescendents(friends_all_folder_id, cats, items, LLInventoryModel::EXCLUDE_TRASH); + bool own_callingcard_found = false; LLInventoryModel::item_array_t::const_iterator it; for (it = items.begin(); it != items.end(); ++it) { - lldebugs << "Check if buddy is in list: " << (*it)->getName() << " " << (*it)->getCreatorUUID() << llendl; - if (NULL == get_ptr_in_map(all_buddies, (*it)->getCreatorUUID())) + if ((*it)->getCreatorUUID() == gAgentID) { - lldebugs << "NONEXISTS, so remove it" << llendl; - removeFriendCardFromInventory((*it)->getCreatorUUID()); + own_callingcard_found = true; + break; } } + // Create own calling card if it was not found in Friends/All folder + if (!own_callingcard_found) + { + LLAvatarName av_name; + LLAvatarNameCache::get( gAgentID, &av_name ); + + create_inventory_item(gAgentID, + gAgent.getSessionID(), + friends_all_folder_id, + LLTransactionID::tnull, + av_name.getCompleteName(), + gAgentID.asString(), + LLAssetType::AT_CALLINGCARD, + LLInventoryType::IT_CALLINGCARD, + NOT_WEARABLE, + PERM_MOVE | PERM_TRANSFER, + NULL); + } + // 2. Add missing Friend Cards for friends LLAvatarTracker::buddy_map_t::const_iterator buddy_it = all_buddies.begin(); llinfos << "try to build friends, count: " << all_buddies.size() << llendl; diff --git a/indra/newview/llfriendcard.h b/indra/newview/llfriendcard.h index b7f0bada14..48a9f70079 100644 --- a/indra/newview/llfriendcard.h +++ b/indra/newview/llfriendcard.h @@ -79,19 +79,6 @@ public: */ void syncFriendCardsFolders(); - /*! - * \brief - * Collects folders' IDs with the buddies' IDs in the Inventory Calling Card/Friends folder. - * - * \param folderBuddiesMap - * map into collected data will be put. It will be cleared before adding new data. - * - * Each item in the out map is a pair where first is an LLViewerInventoryCategory UUID, - * second is a vector with UUID of Avatars from this folder. - * - */ - void collectFriendsLists(folderid_buddies_map_t& folderBuddiesMap) const; - private: typedef boost::function callback_t; diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 61d0a150b7..ba9bea02b9 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -483,9 +483,6 @@ bool LLInventoryCollectFunctor::itemTransferCommonlyAllowed(const LLInventoryIte switch(item->getType()) { - case LLAssetType::AT_CALLINGCARD: - return false; - break; case LLAssetType::AT_OBJECT: case LLAssetType::AT_BODYPART: case LLAssetType::AT_CLOTHING: diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index af8fdb17cf..a7059eb519 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -373,17 +373,19 @@ BOOL LLMuteList::remove(const LLMute& mute, U32 flags) // Must be after erase. setLoaded(); // why is this here? -MG } - - // Clean up any legacy mutes - string_set_t::iterator legacy_it = mLegacyMutes.find(mute.mName); - if (legacy_it != mLegacyMutes.end()) + else { - // Database representation of legacy mute is UUID null. - LLMute mute(LLUUID::null, *legacy_it, LLMute::BY_NAME); - updateRemove(mute); - mLegacyMutes.erase(legacy_it); - // Must be after erase. - setLoaded(); // why is this here? -MG + // Clean up any legacy mutes + string_set_t::iterator legacy_it = mLegacyMutes.find(mute.mName); + if (legacy_it != mLegacyMutes.end()) + { + // Database representation of legacy mute is UUID null. + LLMute mute(LLUUID::null, *legacy_it, LLMute::BY_NAME); + updateRemove(mute); + mLegacyMutes.erase(legacy_it); + // Must be after erase. + setLoaded(); // why is this here? -MG + } } return found; @@ -607,7 +609,8 @@ BOOL LLMuteList::isMuted(const LLUUID& id, const std::string& name, U32 flags) c } // empty names can't be legacy-muted - if (name.empty()) return FALSE; + bool avatar = mute_object && mute_object->isAvatar(); + if (name.empty() || avatar) return FALSE; // Look in legacy pile string_set_t::const_iterator legacy_it = mLegacyMutes.find(name); diff --git a/indra/newview/llpanellandaudio.cpp b/indra/newview/llpanellandaudio.cpp index 5a943bc61d..f9730d9b71 100644 --- a/indra/newview/llpanellandaudio.cpp +++ b/indra/newview/llpanellandaudio.cpp @@ -91,9 +91,6 @@ BOOL LLPanelLandAudio::postBuild() mMusicURLEdit = getChild("music_url"); childSetCommitCallback("music_url", onCommitAny, this); - mMusicUrlCheck = getChild("hide_music_url"); - childSetCommitCallback("hide_music_url", onCommitAny, this); - return TRUE; } @@ -117,9 +114,6 @@ void LLPanelLandAudio::refresh() mCheckSoundLocal->set( parcel->getSoundLocal() ); mCheckSoundLocal->setEnabled( can_change_media ); - mMusicUrlCheck->set( parcel->getObscureMusic() ); - mMusicUrlCheck->setEnabled( can_change_media ); - bool allow_voice = parcel->getParcelFlagAllowVoice(); LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion(); @@ -148,13 +142,6 @@ void LLPanelLandAudio::refresh() mCheckParcelEnableVoice->set(allow_voice); mCheckParcelVoiceLocal->set(!parcel->getParcelFlagUseEstateVoiceChannel()); - // don't display urls if you're not able to change it - // much requested change in forums so people can't 'steal' urls - // NOTE: bug#2009 means this is still vunerable - however, bug - // should be closed since this bug opens up major security issues elsewhere. - bool obscure_music = ! can_change_media && parcel->getObscureMusic(); - - mMusicURLEdit->setDrawAsterixes(obscure_music); mMusicURLEdit->setText(parcel->getMusicURL()); mMusicURLEdit->setEnabled( can_change_media ); } @@ -173,7 +160,6 @@ void LLPanelLandAudio::onCommitAny(LLUICtrl*, void *userdata) // Extract data from UI BOOL sound_local = self->mCheckSoundLocal->get(); std::string music_url = self->mMusicURLEdit->getText(); - U8 obscure_music = self->mMusicUrlCheck->get(); BOOL voice_enabled = self->mCheckParcelEnableVoice->get(); BOOL voice_estate_chan = !self->mCheckParcelVoiceLocal->get(); @@ -186,7 +172,6 @@ void LLPanelLandAudio::onCommitAny(LLUICtrl*, void *userdata) parcel->setParcelFlag(PF_USE_ESTATE_VOICE_CHAN, voice_estate_chan); parcel->setParcelFlag(PF_SOUND_LOCAL, sound_local); parcel->setMusicURL(music_url); - parcel->setObscureMusic(obscure_music); // Send current parcel data upstream to server LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel ); diff --git a/indra/newview/llpanellandmedia.cpp b/indra/newview/llpanellandmedia.cpp index f17defda55..b3adfac8a2 100644 --- a/indra/newview/llpanellandmedia.cpp +++ b/indra/newview/llpanellandmedia.cpp @@ -68,8 +68,7 @@ LLPanelLandMedia::LLPanelLandMedia(LLParcelSelectionHandle& parcel) mMediaSizeCtrlLabel(NULL), mMediaTextureCtrl(NULL), mMediaAutoScaleCheck(NULL), - mMediaLoopCheck(NULL), - mMediaUrlCheck(NULL) + mMediaLoopCheck(NULL) { } @@ -94,9 +93,6 @@ BOOL LLPanelLandMedia::postBuild() mMediaLoopCheck = getChild("media_loop"); childSetCommitCallback("media_loop", onCommitAny, this ); - mMediaUrlCheck = getChild("hide_media_url"); - childSetCommitCallback("hide_media_url", onCommitAny, this ); - mMediaURLEdit = getChild("media_url"); childSetCommitCallback("media_url", onCommitAny, this ); @@ -153,25 +149,6 @@ void LLPanelLandMedia::refresh() mMediaTypeCombo->setEnabled( can_change_media ); getChild("mime_type")->setValue(mime_type); - mMediaUrlCheck->set( parcel->getObscureMedia() ); - mMediaUrlCheck->setEnabled( can_change_media ); - - // don't display urls if you're not able to change it - // much requested change in forums so people can't 'steal' urls - // NOTE: bug#2009 means this is still vunerable - however, bug - // should be closed since this bug opens up major security issues elsewhere. - bool obscure_media = ! can_change_media && parcel->getObscureMedia(); - - // Special code to disable asterixes for html type - if(mime_type == "text/html") - { - obscure_media = false; - mMediaUrlCheck->set( 0 ); - mMediaUrlCheck->setEnabled( false ); - } - - mMediaURLEdit->setDrawAsterixes( obscure_media ); - mMediaAutoScaleCheck->set( parcel->getMediaAutoScale () ); mMediaAutoScaleCheck->setEnabled ( can_change_media ); @@ -301,7 +278,6 @@ void LLPanelLandMedia::onCommitAny(LLUICtrl*, void *userdata) std::string mime_type = self->getChild("mime_type")->getValue().asString(); U8 media_auto_scale = self->mMediaAutoScaleCheck->get(); U8 media_loop = self->mMediaLoopCheck->get(); - U8 obscure_media = self->mMediaUrlCheck->get(); S32 media_width = (S32)self->mMediaWidthCtrl->get(); S32 media_height = (S32)self->mMediaHeightCtrl->get(); LLUUID media_id = self->mMediaTextureCtrl->getImageAssetID(); @@ -321,7 +297,6 @@ void LLPanelLandMedia::onCommitAny(LLUICtrl*, void *userdata) parcel->setMediaID(media_id); parcel->setMediaAutoScale ( media_auto_scale ); parcel->setMediaLoop ( media_loop ); - parcel->setObscureMedia( obscure_media ); // Send current parcel data upstream to server LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel ); diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 903cf4780d..b472698a49 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -1,1183 +1,1183 @@ -/** - * @file llpanellogin.cpp - * @brief Login dialog and logo display - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llpanellogin.h" - -#include "indra_constants.h" // for key and mask constants -#include "llfloaterreg.h" -#include "llfontgl.h" -#include "llmd5.h" -#include "llsecondlifeurls.h" -#include "v4color.h" - -#include "llappviewer.h" -#include "llbutton.h" -#include "llcheckboxctrl.h" -#include "llcommandhandler.h" // for secondlife:///app/login/ -#include "llcombobox.h" -#include "llcurl.h" -#include "llviewercontrol.h" -#include "llfloaterpreference.h" -#include "llfocusmgr.h" -#include "lllineeditor.h" -#include "llnotificationsutil.h" -#include "llsecapi.h" -#include "llstartup.h" -#include "lltextbox.h" -#include "llui.h" -#include "lluiconstants.h" -#include "llslurl.h" -#include "llversioninfo.h" -#include "llviewerhelp.h" -#include "llviewertexturelist.h" -#include "llviewermenu.h" // for handle_preferences() -#include "llviewernetwork.h" -#include "llviewerwindow.h" // to link into child list -#include "lluictrlfactory.h" -#include "llhttpclient.h" -#include "llweb.h" -#include "llmediactrl.h" -#include "llrootview.h" - -#include "llfloatertos.h" -#include "lltrans.h" -#include "llglheaders.h" -#include "llpanelloginlistener.h" - -#if LL_WINDOWS -#pragma warning(disable: 4355) // 'this' used in initializer list -#endif // LL_WINDOWS - -#include "llsdserialize.h" - -const S32 BLACK_BORDER_HEIGHT = 160; -const S32 MAX_PASSWORD = 16; - -LLPanelLogin *LLPanelLogin::sInstance = NULL; -BOOL LLPanelLogin::sCapslockDidNotification = FALSE; - - -class LLLoginRefreshHandler : public LLCommandHandler -{ -public: - // don't allow from external browsers - LLLoginRefreshHandler() : LLCommandHandler("login_refresh", UNTRUSTED_BLOCK) { } - bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) - { - if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) - { - LLPanelLogin::loadLoginPage(); - } - return true; - } -}; - -LLLoginRefreshHandler gLoginRefreshHandler; - - -// helper class that trys to download a URL from a web site and calls a method -// on parent class indicating if the web server is working or not -class LLIamHereLogin : public LLHTTPClient::Responder -{ - private: - LLIamHereLogin( LLPanelLogin* parent ) : - mParent( parent ) - {} - - LLPanelLogin* mParent; - - public: - static boost::intrusive_ptr< LLIamHereLogin > build( LLPanelLogin* parent ) - { - return boost::intrusive_ptr< LLIamHereLogin >( new LLIamHereLogin( parent ) ); - }; - - virtual void setParent( LLPanelLogin* parentIn ) - { - mParent = parentIn; - }; - - // We don't actually expect LLSD back, so need to override completedRaw - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - completed(status, reason, LLSD()); // will call result() or error() - } - - virtual void result( const LLSD& content ) - { - if ( mParent ) - mParent->setSiteIsAlive( true ); - }; - - virtual void error( U32 status, const std::string& reason ) - { - if ( mParent ) - mParent->setSiteIsAlive( false ); - }; -}; - -// this is global and not a class member to keep crud out of the header file -namespace { - boost::intrusive_ptr< LLIamHereLogin > gResponsePtr = 0; -}; - - -//--------------------------------------------------------------------------- -// Public methods -//--------------------------------------------------------------------------- -LLPanelLogin::LLPanelLogin(const LLRect &rect, - BOOL show_server, - void (*callback)(S32 option, void* user_data), - void *cb_data) -: LLPanel(), - mLogoImage(), - mCallback(callback), - mCallbackData(cb_data), - mHtmlAvailable( TRUE ), - mListener(new LLPanelLoginListener(this)) -{ - setBackgroundVisible(FALSE); - setBackgroundOpaque(TRUE); - - // instance management - if (LLPanelLogin::sInstance) - { - llwarns << "Duplicate instance of login view deleted" << llendl; - // Don't leave bad pointer in gFocusMgr - gFocusMgr.setDefaultKeyboardFocus(NULL); - - delete LLPanelLogin::sInstance; - } - - mPasswordModified = FALSE; - LLPanelLogin::sInstance = this; - - LLView* login_holder = gViewerWindow->getLoginPanelHolder(); - if (login_holder) - { - login_holder->addChild(this); - } - - // Logo - mLogoImage = LLUI::getUIImage("startup_logo"); - - buildFromFile( "panel_login.xml"); - - // Legacy login web page is hidden under the menu bar. - // Adjust reg-in-client web browser widget to not be hidden. - if (gSavedSettings.getBOOL("RegInClient")) - { - reshape(rect.getWidth(), rect.getHeight() - MENU_BAR_HEIGHT); - } - else - { - reshape(rect.getWidth(), rect.getHeight()); - } - - getChild("password_edit")->setKeystrokeCallback(onPassKey, this); - - // change z sort of clickable text to be behind buttons - //sendChildToBack(getChildView("channel_text")); - sendChildToBack(getChildView("forgot_password_text")); - - if(LLStartUp::getStartSLURL().getType() != LLSLURL::LOCATION) - { - LLSLURL slurl(gSavedSettings.getString("LoginLocation")); - LLStartUp::setStartSLURL(slurl); - } - updateLocationCombo(false); - - gSavedSettings.getControl("SessionSettingsFile")->getSignal()->connect(boost::bind(&onModeChange)); - - LLComboBox* server_choice_combo = sInstance->getChild("server_combo"); - server_choice_combo->setCommitCallback(onSelectServer, NULL); - server_choice_combo->setFocusLostCallback(boost::bind(onServerComboLostFocus, _1)); - updateServerCombo(); - - childSetAction("connect_btn", onClickConnect, this); - - getChild("login")->setDefaultBtn("connect_btn"); - - std::string channel = LLVersionInfo::getChannel(); - std::string version = llformat("%s (%d)", - LLVersionInfo::getShortVersion().c_str(), - LLVersionInfo::getBuild()); - //LLTextBox* channel_text = getChild("channel_text"); - //channel_text->setTextArg("[CHANNEL]", channel); // though not displayed - //channel_text->setTextArg("[VERSION]", version); - //channel_text->setClickedCallback(onClickVersion, this); - - LLTextBox* forgot_password_text = getChild("forgot_password_text"); - forgot_password_text->setClickedCallback(onClickForgotPassword, NULL); - - LLTextBox* create_new_account_text = getChild("create_new_account_text"); - create_new_account_text->setClickedCallback(onClickNewAccount, NULL); - - LLTextBox* need_help_text = getChild("login_help"); - need_help_text->setClickedCallback(onClickHelp, NULL); - - // get the web browser control - LLMediaCtrl* web_browser = getChild("login_html"); - web_browser->addObserver(this); - - // Clear the browser's cache to avoid any potential for the cache messing up the login screen. - web_browser->clearCache(); - - reshapeBrowser(); - - // kick off a request to grab the url manually - gResponsePtr = LLIamHereLogin::build( this ); - - LLHTTPClient::head( LLGridManager::getInstance()->getLoginPage(), gResponsePtr ); - - // Show last logged in user favorites in "Start at" combo. - addUsersWithFavoritesToUsername(); - getChild("username_combo")->setTextChangedCallback(boost::bind(&LLPanelLogin::addFavoritesToStartLocation, this)); - - updateLocationCombo(false); - -} - -void LLPanelLogin::addUsersWithFavoritesToUsername() -{ - LLComboBox* combo = getChild("username_combo"); - if (!combo) return; - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); - LLSD fav_llsd; - llifstream file; - file.open(filename); - if (!file.is_open()) return; - LLSDSerialize::fromXML(fav_llsd, file); - for (LLSD::map_const_iterator iter = fav_llsd.beginMap(); - iter != fav_llsd.endMap(); ++iter) - { - combo->add(iter->first); - } -} - -void LLPanelLogin::addFavoritesToStartLocation() -{ - LLComboBox* combo = getChild("start_location_combo"); - if (!combo) return; - int num_items = combo->getItemCount(); - for (int i = num_items - 1; i > 2; i--) - { - combo->remove(i); - } - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); - LLSD fav_llsd; - llifstream file; - file.open(filename); - if (!file.is_open()) return; - LLSDSerialize::fromXML(fav_llsd, file); - for (LLSD::map_const_iterator iter = fav_llsd.beginMap(); - iter != fav_llsd.endMap(); ++iter) - { - if(iter->first != getChild("username_combo")->getSimple()) continue; - combo->addSeparator(); - LLSD user_llsd = iter->second; - for (LLSD::array_const_iterator iter1 = user_llsd.beginArray(); - iter1 != user_llsd.endArray(); ++iter1) - { - std::string label = (*iter1)["name"].asString(); - std::string value = (*iter1)["slurl"].asString(); - if(label != "" && value != "") - { - combo->add(label, value); - } - } - break; - } -} - -// force the size to be correct (XML doesn't seem to be sufficient to do this) -// (with some padding so the other login screen doesn't show through) -void LLPanelLogin::reshapeBrowser() -{ - LLMediaCtrl* web_browser = getChild("login_html"); - LLRect rect = gViewerWindow->getWindowRectScaled(); - LLRect html_rect; - html_rect.setCenterAndSize( - rect.getCenterX() - 2, rect.getCenterY() + 40, - rect.getWidth() + 6, rect.getHeight() - 78 ); - web_browser->setRect( html_rect ); - web_browser->reshape( html_rect.getWidth(), html_rect.getHeight(), TRUE ); - reshape( rect.getWidth(), rect.getHeight(), 1 ); -} - -void LLPanelLogin::setSiteIsAlive( bool alive ) -{ - LLMediaCtrl* web_browser = getChild("login_html"); - // if the contents of the site was retrieved - if ( alive ) - { - if ( web_browser ) - { - loadLoginPage(); - - // mark as available - mHtmlAvailable = TRUE; - } - } - else - // the site is not available (missing page, server down, other badness) - { - if ( web_browser ) - { - // hide browser control (revealing default one) - web_browser->setVisible( FALSE ); - - // mark as unavailable - mHtmlAvailable = FALSE; - } - } -} - - -LLPanelLogin::~LLPanelLogin() -{ - LLPanelLogin::sInstance = NULL; - - // tell the responder we're not here anymore - if ( gResponsePtr ) - gResponsePtr->setParent( 0 ); - - //// We know we're done with the image, so be rid of it. - //gTextureList.deleteImage( mLogoImage ); - - // Controls having keyboard focus by default - // must reset it on destroy. (EXT-2748) - gFocusMgr.setDefaultKeyboardFocus(NULL); -} - -// virtual -void LLPanelLogin::draw() -{ - glPushMatrix(); - { - F32 image_aspect = 1.333333f; - F32 view_aspect = (F32)getRect().getWidth() / (F32)getRect().getHeight(); - // stretch image to maintain aspect ratio - if (image_aspect > view_aspect) - { - glTranslatef(-0.5f * (image_aspect / view_aspect - 1.f) * getRect().getWidth(), 0.f, 0.f); - glScalef(image_aspect / view_aspect, 1.f, 1.f); - } - - S32 width = getRect().getWidth(); - S32 height = getRect().getHeight(); - - if ( mHtmlAvailable ) - { - if (getChild("login_widgets")->getVisible()) - { - // draw a background box in black - gl_rect_2d( 0, height - 264, width, 264, LLColor4::black ); - // draw the bottom part of the background image - // just the blue background to the native client UI - mLogoImage->draw(0, -264, width + 8, mLogoImage->getHeight()); - } - } - else - { - // the HTML login page is not available so default to the original screen - S32 offscreen_part = height / 3; - mLogoImage->draw(0, -offscreen_part, width, height+offscreen_part); - }; - } - glPopMatrix(); - - LLPanel::draw(); -} - -// virtual -BOOL LLPanelLogin::handleKeyHere(KEY key, MASK mask) -{ - if ( KEY_F1 == key ) - { - LLViewerHelp* vhelp = LLViewerHelp::getInstance(); - vhelp->showTopic(vhelp->f1HelpTopic()); - return TRUE; - } - - return LLPanel::handleKeyHere(key, mask); -} - -// virtual -void LLPanelLogin::setFocus(BOOL b) -{ - if(b != hasFocus()) - { - if(b) - { - LLPanelLogin::giveFocus(); - } - else - { - LLPanel::setFocus(b); - } - } -} - -// static -void LLPanelLogin::giveFocus() -{ - if( sInstance ) - { - // Grab focus and move cursor to first blank input field - std::string username = sInstance->getChild("username_combo")->getValue().asString(); - std::string pass = sInstance->getChild("password_edit")->getValue().asString(); - - BOOL have_username = !username.empty(); - BOOL have_pass = !pass.empty(); - - LLLineEditor* edit = NULL; - LLComboBox* combo = NULL; - if (have_username && !have_pass) - { - // User saved his name but not his password. Move - // focus to password field. - edit = sInstance->getChild("password_edit"); - } - else - { - // User doesn't have a name, so start there. - combo = sInstance->getChild("username_combo"); - } - - if (edit) - { - edit->setFocus(TRUE); - edit->selectAll(); - } - else if (combo) - { - combo->setFocus(TRUE); - } - } -} - -// static -void LLPanelLogin::showLoginWidgets() -{ - // *NOTE: Mani - This may or may not be obselete code. - // It seems to be part of the defunct? reg-in-client project. - sInstance->getChildView("login_widgets")->setVisible( true); - LLMediaCtrl* web_browser = sInstance->getChild("login_html"); - sInstance->reshapeBrowser(); - // *TODO: Append all the usual login parameters, like first_login=Y etc. - std::string splash_screen_url = LLGridManager::getInstance()->getLoginPage(); - web_browser->navigateTo( splash_screen_url, "text/html" ); - LLUICtrl* username_combo = sInstance->getChild("username_combo"); - username_combo->setFocus(TRUE); -} - -// static -void LLPanelLogin::show(const LLRect &rect, - BOOL show_server, - void (*callback)(S32 option, void* user_data), - void* callback_data) -{ - new LLPanelLogin(rect, show_server, callback, callback_data); - - if( !gFocusMgr.getKeyboardFocus() ) - { - // Grab focus and move cursor to first enabled control - sInstance->setFocus(TRUE); - } - - // Make sure that focus always goes here (and use the latest sInstance that was just created) - gFocusMgr.setDefaultKeyboardFocus(sInstance); -} - -// static -void LLPanelLogin::setFields(LLPointer credential, - BOOL remember) -{ - if (!sInstance) - { - llwarns << "Attempted fillFields with no login view shown" << llendl; - return; - } - LL_INFOS("Credentials") << "Setting login fields to " << *credential << LL_ENDL; - - LLSD identifier = credential->getIdentifier(); - if((std::string)identifier["type"] == "agent") - { - std::string firstname = identifier["first_name"].asString(); - std::string lastname = identifier["last_name"].asString(); - std::string login_id = firstname; - if (!lastname.empty() && lastname != "Resident") - { - // support traditional First Last name SLURLs - login_id += " "; - login_id += lastname; - } - sInstance->getChild("username_combo")->setLabel(login_id); - } - else if((std::string)identifier["type"] == "account") - { - sInstance->getChild("username_combo")->setLabel((std::string)identifier["account_name"]); - } - else - { - sInstance->getChild("username_combo")->setLabel(std::string()); - } - sInstance->addFavoritesToStartLocation(); - // if the password exists in the credential, set the password field with - // a filler to get some stars - LLSD authenticator = credential->getAuthenticator(); - LL_INFOS("Credentials") << "Setting authenticator field " << authenticator["type"].asString() << LL_ENDL; - if(authenticator.isMap() && - authenticator.has("secret") && - (authenticator["secret"].asString().size() > 0)) - { - - // This is a MD5 hex digest of a password. - // We don't actually use the password input field, - // fill it with MAX_PASSWORD characters so we get a - // nice row of asterixes. - const std::string filler("123456789!123456"); - sInstance->getChild("password_edit")->setValue(std::string("123456789!123456")); - } - else - { - sInstance->getChild("password_edit")->setValue(std::string()); - } - sInstance->getChild("remember_check")->setValue(remember); -} - - -// static -void LLPanelLogin::getFields(LLPointer& credential, - BOOL& remember) -{ - if (!sInstance) - { - llwarns << "Attempted getFields with no login view shown" << llendl; - return; - } - - // load the credential so we can pass back the stored password or hash if the user did - // not modify the password field. - - credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); - - LLSD identifier = LLSD::emptyMap(); - LLSD authenticator = LLSD::emptyMap(); - - if(credential.notNull()) - { - authenticator = credential->getAuthenticator(); - } - - std::string username = sInstance->getChild("username_combo")->getValue().asString(); - LLStringUtil::trim(username); - std::string password = sInstance->getChild("password_edit")->getValue().asString(); - - LL_INFOS2("Credentials", "Authentication") << "retrieving username:" << username << LL_ENDL; - // determine if the username is a first/last form or not. - size_t separator_index = username.find_first_of(' '); - if (separator_index == username.npos - && !LLGridManager::getInstance()->isSystemGrid()) - { - LL_INFOS2("Credentials", "Authentication") << "account: " << username << LL_ENDL; - // single username, so this is a 'clear' identifier - identifier["type"] = CRED_IDENTIFIER_TYPE_ACCOUNT; - identifier["account_name"] = username; - - if (LLPanelLogin::sInstance->mPasswordModified) - { - authenticator = LLSD::emptyMap(); - // password is plaintext - authenticator["type"] = CRED_AUTHENTICATOR_TYPE_CLEAR; - authenticator["secret"] = password; - } - } - else - { - // Be lenient in terms of what separators we allow for two-word names - // and allow legacy users to login with firstname.lastname - separator_index = username.find_first_of(" ._"); - std::string first = username.substr(0, separator_index); - std::string last; - if (separator_index != username.npos) - { - last = username.substr(separator_index+1, username.npos); - LLStringUtil::trim(last); - } - else - { - // ...on Linden grids, single username users as considered to have - // last name "Resident" - // *TODO: Make login.cgi support "account_name" like above - last = "Resident"; - } - - if (last.find_first_of(' ') == last.npos) - { - LL_INFOS2("Credentials", "Authentication") << "agent: " << username << LL_ENDL; - // traditional firstname / lastname - identifier["type"] = CRED_IDENTIFIER_TYPE_AGENT; - identifier["first_name"] = first; - identifier["last_name"] = last; - - if (LLPanelLogin::sInstance->mPasswordModified) - { - authenticator = LLSD::emptyMap(); - authenticator["type"] = CRED_AUTHENTICATOR_TYPE_HASH; - authenticator["algorithm"] = "md5"; - LLMD5 pass((const U8 *)password.c_str()); - char md5pass[33]; /* Flawfinder: ignore */ - pass.hex_digest(md5pass); - authenticator["secret"] = md5pass; - } - } - } - credential = gSecAPIHandler->createCredential(LLGridManager::getInstance()->getGrid(), identifier, authenticator); - remember = sInstance->getChild("remember_check")->getValue(); -} - -// static -BOOL LLPanelLogin::isGridComboDirty() -{ - BOOL user_picked = FALSE; - if (!sInstance) - { - llwarns << "Attempted getServer with no login view shown" << llendl; - } - else - { - LLComboBox* combo = sInstance->getChild("server_combo"); - user_picked = combo->isDirty(); - } - return user_picked; -} - -// static -BOOL LLPanelLogin::areCredentialFieldsDirty() -{ - if (!sInstance) - { - llwarns << "Attempted getServer with no login view shown" << llendl; - } - else - { - std::string username = sInstance->getChild("username_combo")->getValue().asString(); - LLStringUtil::trim(username); - std::string password = sInstance->getChild("password_edit")->getValue().asString(); - LLComboBox* combo = sInstance->getChild("username_combo"); - if(combo && combo->isDirty()) - { - return true; - } - LLLineEditor* ctrl = sInstance->getChild("password_edit"); - if(ctrl && ctrl->isDirty()) - { - return true; - } - } - return false; -} - - -// static -void LLPanelLogin::updateLocationCombo( bool force_visible ) -{ - if (!sInstance) - { - return; - } - - LLComboBox* combo = sInstance->getChild("start_location_combo"); - - switch(LLStartUp::getStartSLURL().getType()) - { - case LLSLURL::LOCATION: - { - - combo->setCurrentByIndex( 2 ); - combo->setTextEntry(LLStartUp::getStartSLURL().getLocationString()); - break; - } - case LLSLURL::HOME_LOCATION: - combo->setCurrentByIndex(1); - break; - default: - combo->setCurrentByIndex(0); - break; - } - - BOOL show_start = TRUE; - - if ( ! force_visible ) - show_start = gSavedSettings.getBOOL("ShowStartLocation"); - - sInstance->getChildView("start_location_combo")->setVisible( show_start); - sInstance->getChildView("start_location_text")->setVisible( show_start); - - BOOL show_server = gSavedSettings.getBOOL("ForceShowGrid"); - sInstance->getChildView("server_combo_text")->setVisible( show_server); - sInstance->getChildView("server_combo")->setVisible( show_server); -} - -// static -void LLPanelLogin::updateStartSLURL() -{ - if (!sInstance) return; - - LLComboBox* combo = sInstance->getChild("start_location_combo"); - S32 index = combo->getCurrentIndex(); - - switch (index) - { - case 0: - { - LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST)); - break; - } - case 1: - { - LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); - break; - } - default: - { - LLSLURL slurl = LLSLURL(combo->getValue().asString()); - if(slurl.getType() == LLSLURL::LOCATION) - { - // we've changed the grid, so update the grid selection - LLStartUp::setStartSLURL(slurl); - } - break; - } - } -} - - -void LLPanelLogin::setLocation(const LLSLURL& slurl) -{ - LLStartUp::setStartSLURL(slurl); - updateServer(); -} - -// static -void LLPanelLogin::closePanel() -{ - if (sInstance) - { - LLPanelLogin::sInstance->getParent()->removeChild( LLPanelLogin::sInstance ); - - delete sInstance; - sInstance = NULL; - } -} - -// static -void LLPanelLogin::setAlwaysRefresh(bool refresh) -{ - if (LLStartUp::getStartupState() >= STATE_LOGIN_CLEANUP) return; - - LLMediaCtrl* web_browser = sInstance->getChild("login_html"); - - if (web_browser) - { - web_browser->setAlwaysRefresh(refresh); - } -} - - - -void LLPanelLogin::loadLoginPage() -{ - if (!sInstance) return; - - std::ostringstream oStr; - - std::string login_page = LLGridManager::getInstance()->getLoginPage(); - - oStr << login_page; - - // Use the right delimeter depending on how LLURI parses the URL - LLURI login_page_uri = LLURI(login_page); - - std::string first_query_delimiter = "&"; - if (login_page_uri.queryMap().size() == 0) - { - first_query_delimiter = "?"; - } - - // Language - std::string language = LLUI::getLanguage(); - oStr << first_query_delimiter<<"lang=" << language; - - // First Login? - if (gSavedSettings.getBOOL("FirstLoginThisInstall")) - { - oStr << "&firstlogin=TRUE"; - } - - // Channel and Version - std::string version = llformat("%s (%d)", - LLVersionInfo::getShortVersion().c_str(), - LLVersionInfo::getBuild()); - - char* curl_channel = curl_escape(LLVersionInfo::getChannel().c_str(), 0); - char* curl_version = curl_escape(version.c_str(), 0); - - oStr << "&channel=" << curl_channel; - oStr << "&version=" << curl_version; - - curl_free(curl_channel); - curl_free(curl_version); - - // Grid - char* curl_grid = curl_escape(LLGridManager::getInstance()->getGridLabel().c_str(), 0); - oStr << "&grid=" << curl_grid; - curl_free(curl_grid); - - // add OS info - char * os_info = curl_escape(LLAppViewer::instance()->getOSInfo().getOSStringSimple().c_str(), 0); - oStr << "&os=" << os_info; - curl_free(os_info); - - - gViewerWindow->setMenuBackgroundColor(false, !LLGridManager::getInstance()->isInProductionGrid()); - gLoginMenuBarView->setBackgroundColor(gMenuBarView->getBackgroundColor()); - - LLMediaCtrl* web_browser = sInstance->getChild("login_html"); - - // navigate to the "real" page - if (gSavedSettings.getBOOL("RegInClient")) - { - web_browser->setFocus(TRUE); - login_page = sInstance->getString("reg_in_client_url"); - web_browser->navigateTo(login_page, "text/html"); - } - else - { - web_browser->navigateTo( oStr.str(), "text/html" ); - } -} - -void LLPanelLogin::handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent event) -{ - if(event == MEDIA_EVENT_NAVIGATE_COMPLETE) - { - LLMediaCtrl* web_browser = sInstance->getChild("login_html"); - if (web_browser) - { - // *HACK HACK HACK HACK! - /* Stuff a Tab key into the browser now so that the first field will - ** get the focus! The embedded javascript on the page that properly - ** sets the initial focus in a real web browser is not working inside - ** the viewer, so this is an UGLY HACK WORKAROUND for now. - */ - // Commented out as it's not reliable - //web_browser->handleKey(KEY_TAB, MASK_NONE, false); - } - } -} - -//--------------------------------------------------------------------------- -// Protected methods -//--------------------------------------------------------------------------- - -// static -void LLPanelLogin::onClickConnect(void *) -{ - if (sInstance && sInstance->mCallback) - { - // tell the responder we're not here anymore - if ( gResponsePtr ) - gResponsePtr->setParent( 0 ); - - // JC - Make sure the fields all get committed. - sInstance->setFocus(FALSE); - - LLComboBox* combo = sInstance->getChild("server_combo"); - LLSD combo_val = combo->getSelectedValue(); - if (combo_val.isUndefined()) - { - combo_val = combo->getValue(); - } - if(combo_val.isUndefined()) - { - LLNotificationsUtil::add("StartRegionEmpty"); - return; - } - try - { - LLGridManager::getInstance()->setGridChoice(combo_val.asString()); - } - catch (LLInvalidGridName ex) - { - LLSD args; - args["GRID"] = combo_val.asString(); - LLNotificationsUtil::add("InvalidGrid", args); - return; - } - updateStartSLURL(); - std::string username = sInstance->getChild("username_combo")->getValue().asString(); - - - if(username.empty()) - { - // user must type in something into the username field - LLNotificationsUtil::add("MustHaveAccountToLogIn"); - } - else - { - LLPointer cred; - BOOL remember; - getFields(cred, remember); - std::string identifier_type; - cred->identifierType(identifier_type); - LLSD allowed_credential_types; - LLGridManager::getInstance()->getLoginIdentifierTypes(allowed_credential_types); - - // check the typed in credential type against the credential types expected by the server. - for(LLSD::array_iterator i = allowed_credential_types.beginArray(); - i != allowed_credential_types.endArray(); - i++) - { - - if(i->asString() == identifier_type) - { - // yay correct credential type - sInstance->mCallback(0, sInstance->mCallbackData); - return; - } - } - - // Right now, maingrid is the only thing that is picky about - // credential format, as it doesn't yet allow account (single username) - // format creds. - Rox. James, we wanna fix the message when we change - // this. - LLNotificationsUtil::add("InvalidCredentialFormat"); - } - } -} - -/* -// static -bool LLPanelLogin::newAccountAlertCallback(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (0 == option) - { - llinfos << "Going to account creation URL" << llendl; - LLWeb::loadURLExternal( LLNotifications::instance().getGlobalString("CREATE_ACCOUNT_URL")); - } - else - { - sInstance->setFocus(TRUE); - } - return false; -} -*/ - -// static -void LLPanelLogin::onClickNewAccount(void*) -{ - LLWeb::loadURLExternal(sInstance->getString("create_account_url")); -} - - -// static -void LLPanelLogin::onClickVersion(void*) -{ - LLFloaterReg::showInstance("sl_about"); -} - -//static -void LLPanelLogin::onClickForgotPassword(void*) -{ - if (sInstance ) - { - LLWeb::loadURLExternal(sInstance->getString( "forgot_password_url" )); - } -} - -//static -void LLPanelLogin::onClickHelp(void*) -{ - if (sInstance) - { - LLViewerHelp* vhelp = LLViewerHelp::getInstance(); - vhelp->showTopic(vhelp->preLoginTopic()); - } -} - -// static -void LLPanelLogin::onPassKey(LLLineEditor* caller, void* user_data) -{ - LLPanelLogin *This = (LLPanelLogin *) user_data; - This->mPasswordModified = TRUE; - if (gKeyboard->getKeyDown(KEY_CAPSLOCK) && sCapslockDidNotification == FALSE) - { -// *TODO: use another way to notify user about enabled caps lock, see EXT-6858 - sCapslockDidNotification = TRUE; - } -} - - -void LLPanelLogin::updateServer() -{ - try - { - - updateServerCombo(); - // if they've selected another grid, we should load the credentials - // for that grid and set them to the UI. - if(sInstance && !sInstance->areCredentialFieldsDirty()) - { - LLPointer credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); - bool remember = sInstance->getChild("remember_check")->getValue(); - sInstance->setFields(credential, remember); - } - // grid changed so show new splash screen (possibly) - loadLoginPage(); - updateLocationCombo(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION); - } - catch (LLInvalidGridName ex) - { - // do nothing - } -} - -void LLPanelLogin::updateServerCombo() -{ - if (!sInstance) - { - return; - } - // We add all of the possible values, sorted, and then add a bar and the current value at the top - LLComboBox* server_choice_combo = sInstance->getChild("server_combo"); - server_choice_combo->removeall(); - - std::map known_grids = LLGridManager::getInstance()->getKnownGrids(!gSavedSettings.getBOOL("ShowBetaGrids")); - - for (std::map::iterator grid_choice = known_grids.begin(); - grid_choice != known_grids.end(); - grid_choice++) - { - if (!grid_choice->first.empty()) - { - server_choice_combo->add(grid_choice->second, grid_choice->first); - } - } - server_choice_combo->sortByName(); - - server_choice_combo->addSeparator(ADD_TOP); - - server_choice_combo->add(LLGridManager::getInstance()->getGridLabel(), - LLGridManager::getInstance()->getGrid(), ADD_TOP); - - server_choice_combo->selectFirstItem(); -} - -// static -void LLPanelLogin::onSelectServer(LLUICtrl*, void*) -{ - // *NOTE: The paramters for this method are ignored. - // LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe, void*) - // calls this method. - LL_INFOS("AppInit") << "onSelectServer" << LL_ENDL; - // The user twiddled with the grid choice ui. - // apply the selection to the grid setting. - LLPointer credential; - - LLComboBox* combo = sInstance->getChild("server_combo"); - LLSD combo_val = combo->getSelectedValue(); - if (combo_val.isUndefined()) - { - combo_val = combo->getValue(); - } - - combo = sInstance->getChild("start_location_combo"); - combo->setCurrentByIndex(1); - LLStartUp::setStartSLURL(LLSLURL(gSavedSettings.getString("LoginLocation"))); - LLGridManager::getInstance()->setGridChoice(combo_val.asString()); - // This new selection will override preset uris - // from the command line. - updateServer(); - updateLocationCombo(false); - updateLoginPanelLinks(); -} - -void LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe) -{ - if (!sInstance) - { - return; - } - - LLComboBox* combo = sInstance->getChild("server_combo"); - if(fe == combo) - { - onSelectServer(combo, NULL); - } -} - -void LLPanelLogin::updateLoginPanelLinks() -{ - LLSD grid_data; - LLGridManager::getInstance()->getGridInfo(grid_data); - bool system_grid = grid_data.has(GRID_IS_SYSTEM_GRID_VALUE); - - // need to call through sInstance, as it's called from onSelectServer, which - // is static. - sInstance->getChildView("create_new_account_text")->setVisible( system_grid); - sInstance->getChildView("forgot_password_text")->setVisible( system_grid); -} - -//static -void LLPanelLogin::onModeChange() -{ - LLNotificationsUtil::add("ModeChange", LLSD(), LLSD(), boost::bind(&onModeChangeConfirm, _1, _2)); -} - -//static -void LLPanelLogin::onModeChangeConfirm(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - switch (option) - { - case 0: - LLAppViewer::instance()->requestQuit(); - break; - case 1: - // do nothing - break; - default: - break; - } -} +/** + * @file llpanellogin.cpp + * @brief Login dialog and logo display + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llpanellogin.h" + +#include "indra_constants.h" // for key and mask constants +#include "llfloaterreg.h" +#include "llfontgl.h" +#include "llmd5.h" +#include "llsecondlifeurls.h" +#include "v4color.h" + +#include "llappviewer.h" +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "llcommandhandler.h" // for secondlife:///app/login/ +#include "llcombobox.h" +#include "llcurl.h" +#include "llviewercontrol.h" +#include "llfloaterpreference.h" +#include "llfocusmgr.h" +#include "lllineeditor.h" +#include "llnotificationsutil.h" +#include "llsecapi.h" +#include "llstartup.h" +#include "lltextbox.h" +#include "llui.h" +#include "lluiconstants.h" +#include "llslurl.h" +#include "llversioninfo.h" +#include "llviewerhelp.h" +#include "llviewertexturelist.h" +#include "llviewermenu.h" // for handle_preferences() +#include "llviewernetwork.h" +#include "llviewerwindow.h" // to link into child list +#include "lluictrlfactory.h" +#include "llhttpclient.h" +#include "llweb.h" +#include "llmediactrl.h" +#include "llrootview.h" + +#include "llfloatertos.h" +#include "lltrans.h" +#include "llglheaders.h" +#include "llpanelloginlistener.h" + +#if LL_WINDOWS +#pragma warning(disable: 4355) // 'this' used in initializer list +#endif // LL_WINDOWS + +#include "llsdserialize.h" + +const S32 BLACK_BORDER_HEIGHT = 160; +const S32 MAX_PASSWORD = 16; + +LLPanelLogin *LLPanelLogin::sInstance = NULL; +BOOL LLPanelLogin::sCapslockDidNotification = FALSE; + + +class LLLoginRefreshHandler : public LLCommandHandler +{ +public: + // don't allow from external browsers + LLLoginRefreshHandler() : LLCommandHandler("login_refresh", UNTRUSTED_BLOCK) { } + bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) + { + if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) + { + LLPanelLogin::loadLoginPage(); + } + return true; + } +}; + +LLLoginRefreshHandler gLoginRefreshHandler; + + +// helper class that trys to download a URL from a web site and calls a method +// on parent class indicating if the web server is working or not +class LLIamHereLogin : public LLHTTPClient::Responder +{ + private: + LLIamHereLogin( LLPanelLogin* parent ) : + mParent( parent ) + {} + + LLPanelLogin* mParent; + + public: + static boost::intrusive_ptr< LLIamHereLogin > build( LLPanelLogin* parent ) + { + return boost::intrusive_ptr< LLIamHereLogin >( new LLIamHereLogin( parent ) ); + }; + + virtual void setParent( LLPanelLogin* parentIn ) + { + mParent = parentIn; + }; + + // We don't actually expect LLSD back, so need to override completedRaw + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) + { + completed(status, reason, LLSD()); // will call result() or error() + } + + virtual void result( const LLSD& content ) + { + if ( mParent ) + mParent->setSiteIsAlive( true ); + }; + + virtual void error( U32 status, const std::string& reason ) + { + if ( mParent ) + mParent->setSiteIsAlive( false ); + }; +}; + +// this is global and not a class member to keep crud out of the header file +namespace { + boost::intrusive_ptr< LLIamHereLogin > gResponsePtr = 0; +}; + + +//--------------------------------------------------------------------------- +// Public methods +//--------------------------------------------------------------------------- +LLPanelLogin::LLPanelLogin(const LLRect &rect, + BOOL show_server, + void (*callback)(S32 option, void* user_data), + void *cb_data) +: LLPanel(), + mLogoImage(), + mCallback(callback), + mCallbackData(cb_data), + mHtmlAvailable( TRUE ), + mListener(new LLPanelLoginListener(this)) +{ + setBackgroundVisible(FALSE); + setBackgroundOpaque(TRUE); + + // instance management + if (LLPanelLogin::sInstance) + { + llwarns << "Duplicate instance of login view deleted" << llendl; + // Don't leave bad pointer in gFocusMgr + gFocusMgr.setDefaultKeyboardFocus(NULL); + + delete LLPanelLogin::sInstance; + } + + mPasswordModified = FALSE; + LLPanelLogin::sInstance = this; + + LLView* login_holder = gViewerWindow->getLoginPanelHolder(); + if (login_holder) + { + login_holder->addChild(this); + } + + // Logo + mLogoImage = LLUI::getUIImage("startup_logo"); + + buildFromFile( "panel_login.xml"); + + // Legacy login web page is hidden under the menu bar. + // Adjust reg-in-client web browser widget to not be hidden. + if (gSavedSettings.getBOOL("RegInClient")) + { + reshape(rect.getWidth(), rect.getHeight() - MENU_BAR_HEIGHT); + } + else + { + reshape(rect.getWidth(), rect.getHeight()); + } + + getChild("password_edit")->setKeystrokeCallback(onPassKey, this); + + // change z sort of clickable text to be behind buttons + //sendChildToBack(getChildView("channel_text")); + sendChildToBack(getChildView("forgot_password_text")); + + if(LLStartUp::getStartSLURL().getType() != LLSLURL::LOCATION) + { + LLSLURL slurl(gSavedSettings.getString("LoginLocation")); + LLStartUp::setStartSLURL(slurl); + } + updateLocationCombo(false); + + gSavedSettings.getControl("SessionSettingsFile")->getSignal()->connect(boost::bind(&onModeChange)); + + LLComboBox* server_choice_combo = sInstance->getChild("server_combo"); + server_choice_combo->setCommitCallback(onSelectServer, NULL); + server_choice_combo->setFocusLostCallback(boost::bind(onServerComboLostFocus, _1)); + updateServerCombo(); + + childSetAction("connect_btn", onClickConnect, this); + + getChild("login")->setDefaultBtn("connect_btn"); + + std::string channel = LLVersionInfo::getChannel(); + std::string version = llformat("%s (%d)", + LLVersionInfo::getShortVersion().c_str(), + LLVersionInfo::getBuild()); + //LLTextBox* channel_text = getChild("channel_text"); + //channel_text->setTextArg("[CHANNEL]", channel); // though not displayed + //channel_text->setTextArg("[VERSION]", version); + //channel_text->setClickedCallback(onClickVersion, this); + + LLTextBox* forgot_password_text = getChild("forgot_password_text"); + forgot_password_text->setClickedCallback(onClickForgotPassword, NULL); + + LLTextBox* create_new_account_text = getChild("create_new_account_text"); + create_new_account_text->setClickedCallback(onClickNewAccount, NULL); + + LLTextBox* need_help_text = getChild("login_help"); + need_help_text->setClickedCallback(onClickHelp, NULL); + + // get the web browser control + LLMediaCtrl* web_browser = getChild("login_html"); + web_browser->addObserver(this); + + // Clear the browser's cache to avoid any potential for the cache messing up the login screen. + web_browser->clearCache(); + + reshapeBrowser(); + + // kick off a request to grab the url manually + gResponsePtr = LLIamHereLogin::build( this ); + + LLHTTPClient::head( LLGridManager::getInstance()->getLoginPage(), gResponsePtr ); + + // Show last logged in user favorites in "Start at" combo. + addUsersWithFavoritesToUsername(); + getChild("username_combo")->setTextChangedCallback(boost::bind(&LLPanelLogin::addFavoritesToStartLocation, this)); + + updateLocationCombo(false); + +} + +void LLPanelLogin::addUsersWithFavoritesToUsername() +{ + LLComboBox* combo = getChild("username_combo"); + if (!combo) return; + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + LLSD fav_llsd; + llifstream file; + file.open(filename); + if (!file.is_open()) return; + LLSDSerialize::fromXML(fav_llsd, file); + for (LLSD::map_const_iterator iter = fav_llsd.beginMap(); + iter != fav_llsd.endMap(); ++iter) + { + combo->add(iter->first); + } +} + +void LLPanelLogin::addFavoritesToStartLocation() +{ + LLComboBox* combo = getChild("start_location_combo"); + if (!combo) return; + int num_items = combo->getItemCount(); + for (int i = num_items - 1; i > 2; i--) + { + combo->remove(i); + } + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "stored_favorites.xml"); + LLSD fav_llsd; + llifstream file; + file.open(filename); + if (!file.is_open()) return; + LLSDSerialize::fromXML(fav_llsd, file); + for (LLSD::map_const_iterator iter = fav_llsd.beginMap(); + iter != fav_llsd.endMap(); ++iter) + { + if(iter->first != getChild("username_combo")->getSimple()) continue; + combo->addSeparator(); + LLSD user_llsd = iter->second; + for (LLSD::array_const_iterator iter1 = user_llsd.beginArray(); + iter1 != user_llsd.endArray(); ++iter1) + { + std::string label = (*iter1)["name"].asString(); + std::string value = (*iter1)["slurl"].asString(); + if(label != "" && value != "") + { + combo->add(label, value); + } + } + break; + } +} + +// force the size to be correct (XML doesn't seem to be sufficient to do this) +// (with some padding so the other login screen doesn't show through) +void LLPanelLogin::reshapeBrowser() +{ + LLMediaCtrl* web_browser = getChild("login_html"); + LLRect rect = gViewerWindow->getWindowRectScaled(); + LLRect html_rect; + html_rect.setCenterAndSize( + rect.getCenterX() - 2, rect.getCenterY() + 40, + rect.getWidth() + 6, rect.getHeight() - 78 ); + web_browser->setRect( html_rect ); + web_browser->reshape( html_rect.getWidth(), html_rect.getHeight(), TRUE ); + reshape( rect.getWidth(), rect.getHeight(), 1 ); +} + +void LLPanelLogin::setSiteIsAlive( bool alive ) +{ + LLMediaCtrl* web_browser = getChild("login_html"); + // if the contents of the site was retrieved + if ( alive ) + { + if ( web_browser ) + { + loadLoginPage(); + + // mark as available + mHtmlAvailable = TRUE; + } + } + else + // the site is not available (missing page, server down, other badness) + { + if ( web_browser ) + { + // hide browser control (revealing default one) + web_browser->setVisible( FALSE ); + + // mark as unavailable + mHtmlAvailable = FALSE; + } + } +} + + +LLPanelLogin::~LLPanelLogin() +{ + LLPanelLogin::sInstance = NULL; + + // tell the responder we're not here anymore + if ( gResponsePtr ) + gResponsePtr->setParent( 0 ); + + //// We know we're done with the image, so be rid of it. + //gTextureList.deleteImage( mLogoImage ); + + // Controls having keyboard focus by default + // must reset it on destroy. (EXT-2748) + gFocusMgr.setDefaultKeyboardFocus(NULL); +} + +// virtual +void LLPanelLogin::draw() +{ + glPushMatrix(); + { + F32 image_aspect = 1.333333f; + F32 view_aspect = (F32)getRect().getWidth() / (F32)getRect().getHeight(); + // stretch image to maintain aspect ratio + if (image_aspect > view_aspect) + { + glTranslatef(-0.5f * (image_aspect / view_aspect - 1.f) * getRect().getWidth(), 0.f, 0.f); + glScalef(image_aspect / view_aspect, 1.f, 1.f); + } + + S32 width = getRect().getWidth(); + S32 height = getRect().getHeight(); + + if ( mHtmlAvailable ) + { + if (getChild("login_widgets")->getVisible()) + { + // draw a background box in black + gl_rect_2d( 0, height - 264, width, 264, LLColor4::black ); + // draw the bottom part of the background image + // just the blue background to the native client UI + mLogoImage->draw(0, -264, width + 8, mLogoImage->getHeight()); + } + } + else + { + // the HTML login page is not available so default to the original screen + S32 offscreen_part = height / 3; + mLogoImage->draw(0, -offscreen_part, width, height+offscreen_part); + }; + } + glPopMatrix(); + + LLPanel::draw(); +} + +// virtual +BOOL LLPanelLogin::handleKeyHere(KEY key, MASK mask) +{ + if ( KEY_F1 == key ) + { + LLViewerHelp* vhelp = LLViewerHelp::getInstance(); + vhelp->showTopic(vhelp->f1HelpTopic()); + return TRUE; + } + + return LLPanel::handleKeyHere(key, mask); +} + +// virtual +void LLPanelLogin::setFocus(BOOL b) +{ + if(b != hasFocus()) + { + if(b) + { + LLPanelLogin::giveFocus(); + } + else + { + LLPanel::setFocus(b); + } + } +} + +// static +void LLPanelLogin::giveFocus() +{ + if( sInstance ) + { + // Grab focus and move cursor to first blank input field + std::string username = sInstance->getChild("username_combo")->getValue().asString(); + std::string pass = sInstance->getChild("password_edit")->getValue().asString(); + + BOOL have_username = !username.empty(); + BOOL have_pass = !pass.empty(); + + LLLineEditor* edit = NULL; + LLComboBox* combo = NULL; + if (have_username && !have_pass) + { + // User saved his name but not his password. Move + // focus to password field. + edit = sInstance->getChild("password_edit"); + } + else + { + // User doesn't have a name, so start there. + combo = sInstance->getChild("username_combo"); + } + + if (edit) + { + edit->setFocus(TRUE); + edit->selectAll(); + } + else if (combo) + { + combo->setFocus(TRUE); + } + } +} + +// static +void LLPanelLogin::showLoginWidgets() +{ + // *NOTE: Mani - This may or may not be obselete code. + // It seems to be part of the defunct? reg-in-client project. + sInstance->getChildView("login_widgets")->setVisible( true); + LLMediaCtrl* web_browser = sInstance->getChild("login_html"); + sInstance->reshapeBrowser(); + // *TODO: Append all the usual login parameters, like first_login=Y etc. + std::string splash_screen_url = LLGridManager::getInstance()->getLoginPage(); + web_browser->navigateTo( splash_screen_url, "text/html" ); + LLUICtrl* username_combo = sInstance->getChild("username_combo"); + username_combo->setFocus(TRUE); +} + +// static +void LLPanelLogin::show(const LLRect &rect, + BOOL show_server, + void (*callback)(S32 option, void* user_data), + void* callback_data) +{ + new LLPanelLogin(rect, show_server, callback, callback_data); + + if( !gFocusMgr.getKeyboardFocus() ) + { + // Grab focus and move cursor to first enabled control + sInstance->setFocus(TRUE); + } + + // Make sure that focus always goes here (and use the latest sInstance that was just created) + gFocusMgr.setDefaultKeyboardFocus(sInstance); +} + +// static +void LLPanelLogin::setFields(LLPointer credential, + BOOL remember) +{ + if (!sInstance) + { + llwarns << "Attempted fillFields with no login view shown" << llendl; + return; + } + LL_INFOS("Credentials") << "Setting login fields to " << *credential << LL_ENDL; + + LLSD identifier = credential->getIdentifier(); + if((std::string)identifier["type"] == "agent") + { + std::string firstname = identifier["first_name"].asString(); + std::string lastname = identifier["last_name"].asString(); + std::string login_id = firstname; + if (!lastname.empty() && lastname != "Resident") + { + // support traditional First Last name SLURLs + login_id += " "; + login_id += lastname; + } + sInstance->getChild("username_combo")->setLabel(login_id); + } + else if((std::string)identifier["type"] == "account") + { + sInstance->getChild("username_combo")->setLabel((std::string)identifier["account_name"]); + } + else + { + sInstance->getChild("username_combo")->setLabel(std::string()); + } + sInstance->addFavoritesToStartLocation(); + // if the password exists in the credential, set the password field with + // a filler to get some stars + LLSD authenticator = credential->getAuthenticator(); + LL_INFOS("Credentials") << "Setting authenticator field " << authenticator["type"].asString() << LL_ENDL; + if(authenticator.isMap() && + authenticator.has("secret") && + (authenticator["secret"].asString().size() > 0)) + { + + // This is a MD5 hex digest of a password. + // We don't actually use the password input field, + // fill it with MAX_PASSWORD characters so we get a + // nice row of asterixes. + const std::string filler("123456789!123456"); + sInstance->getChild("password_edit")->setValue(std::string("123456789!123456")); + } + else + { + sInstance->getChild("password_edit")->setValue(std::string()); + } + sInstance->getChild("remember_check")->setValue(remember); +} + + +// static +void LLPanelLogin::getFields(LLPointer& credential, + BOOL& remember) +{ + if (!sInstance) + { + llwarns << "Attempted getFields with no login view shown" << llendl; + return; + } + + // load the credential so we can pass back the stored password or hash if the user did + // not modify the password field. + + credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); + + LLSD identifier = LLSD::emptyMap(); + LLSD authenticator = LLSD::emptyMap(); + + if(credential.notNull()) + { + authenticator = credential->getAuthenticator(); + } + + std::string username = sInstance->getChild("username_combo")->getValue().asString(); + LLStringUtil::trim(username); + std::string password = sInstance->getChild("password_edit")->getValue().asString(); + + LL_INFOS2("Credentials", "Authentication") << "retrieving username:" << username << LL_ENDL; + // determine if the username is a first/last form or not. + size_t separator_index = username.find_first_of(' '); + if (separator_index == username.npos + && !LLGridManager::getInstance()->isSystemGrid()) + { + LL_INFOS2("Credentials", "Authentication") << "account: " << username << LL_ENDL; + // single username, so this is a 'clear' identifier + identifier["type"] = CRED_IDENTIFIER_TYPE_ACCOUNT; + identifier["account_name"] = username; + + if (LLPanelLogin::sInstance->mPasswordModified) + { + authenticator = LLSD::emptyMap(); + // password is plaintext + authenticator["type"] = CRED_AUTHENTICATOR_TYPE_CLEAR; + authenticator["secret"] = password; + } + } + else + { + // Be lenient in terms of what separators we allow for two-word names + // and allow legacy users to login with firstname.lastname + separator_index = username.find_first_of(" ._"); + std::string first = username.substr(0, separator_index); + std::string last; + if (separator_index != username.npos) + { + last = username.substr(separator_index+1, username.npos); + LLStringUtil::trim(last); + } + else + { + // ...on Linden grids, single username users as considered to have + // last name "Resident" + // *TODO: Make login.cgi support "account_name" like above + last = "Resident"; + } + + if (last.find_first_of(' ') == last.npos) + { + LL_INFOS2("Credentials", "Authentication") << "agent: " << username << LL_ENDL; + // traditional firstname / lastname + identifier["type"] = CRED_IDENTIFIER_TYPE_AGENT; + identifier["first_name"] = first; + identifier["last_name"] = last; + + if (LLPanelLogin::sInstance->mPasswordModified) + { + authenticator = LLSD::emptyMap(); + authenticator["type"] = CRED_AUTHENTICATOR_TYPE_HASH; + authenticator["algorithm"] = "md5"; + LLMD5 pass((const U8 *)password.c_str()); + char md5pass[33]; /* Flawfinder: ignore */ + pass.hex_digest(md5pass); + authenticator["secret"] = md5pass; + } + } + } + credential = gSecAPIHandler->createCredential(LLGridManager::getInstance()->getGrid(), identifier, authenticator); + remember = sInstance->getChild("remember_check")->getValue(); +} + +// static +BOOL LLPanelLogin::isGridComboDirty() +{ + BOOL user_picked = FALSE; + if (!sInstance) + { + llwarns << "Attempted getServer with no login view shown" << llendl; + } + else + { + LLComboBox* combo = sInstance->getChild("server_combo"); + user_picked = combo->isDirty(); + } + return user_picked; +} + +// static +BOOL LLPanelLogin::areCredentialFieldsDirty() +{ + if (!sInstance) + { + llwarns << "Attempted getServer with no login view shown" << llendl; + } + else + { + std::string username = sInstance->getChild("username_combo")->getValue().asString(); + LLStringUtil::trim(username); + std::string password = sInstance->getChild("password_edit")->getValue().asString(); + LLComboBox* combo = sInstance->getChild("username_combo"); + if(combo && combo->isDirty()) + { + return true; + } + LLLineEditor* ctrl = sInstance->getChild("password_edit"); + if(ctrl && ctrl->isDirty()) + { + return true; + } + } + return false; +} + + +// static +void LLPanelLogin::updateLocationCombo( bool force_visible ) +{ + if (!sInstance) + { + return; + } + + LLComboBox* combo = sInstance->getChild("start_location_combo"); + + switch(LLStartUp::getStartSLURL().getType()) + { + case LLSLURL::LOCATION: + { + + combo->setCurrentByIndex( 2 ); + combo->setTextEntry(LLStartUp::getStartSLURL().getLocationString()); + break; + } + case LLSLURL::HOME_LOCATION: + combo->setCurrentByIndex(1); + break; + default: + combo->setCurrentByIndex(0); + break; + } + + BOOL show_start = TRUE; + + if ( ! force_visible ) + show_start = gSavedSettings.getBOOL("ShowStartLocation"); + + sInstance->getChildView("start_location_combo")->setVisible( show_start); + sInstance->getChildView("start_location_text")->setVisible( show_start); + + BOOL show_server = gSavedSettings.getBOOL("ForceShowGrid"); + sInstance->getChildView("server_combo_text")->setVisible( show_server); + sInstance->getChildView("server_combo")->setVisible( show_server); +} + +// static +void LLPanelLogin::updateStartSLURL() +{ + if (!sInstance) return; + + LLComboBox* combo = sInstance->getChild("start_location_combo"); + S32 index = combo->getCurrentIndex(); + + switch (index) + { + case 0: + { + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST)); + break; + } + case 1: + { + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); + break; + } + default: + { + LLSLURL slurl = LLSLURL(combo->getValue().asString()); + if(slurl.getType() == LLSLURL::LOCATION) + { + // we've changed the grid, so update the grid selection + LLStartUp::setStartSLURL(slurl); + } + break; + } + } +} + + +void LLPanelLogin::setLocation(const LLSLURL& slurl) +{ + LLStartUp::setStartSLURL(slurl); + updateServer(); +} + +// static +void LLPanelLogin::closePanel() +{ + if (sInstance) + { + LLPanelLogin::sInstance->getParent()->removeChild( LLPanelLogin::sInstance ); + + delete sInstance; + sInstance = NULL; + } +} + +// static +void LLPanelLogin::setAlwaysRefresh(bool refresh) +{ + if (LLStartUp::getStartupState() >= STATE_LOGIN_CLEANUP) return; + + LLMediaCtrl* web_browser = sInstance->getChild("login_html"); + + if (web_browser) + { + web_browser->setAlwaysRefresh(refresh); + } +} + + + +void LLPanelLogin::loadLoginPage() +{ + if (!sInstance) return; + + std::ostringstream oStr; + + std::string login_page = LLGridManager::getInstance()->getLoginPage(); + + oStr << login_page; + + // Use the right delimeter depending on how LLURI parses the URL + LLURI login_page_uri = LLURI(login_page); + + std::string first_query_delimiter = "&"; + if (login_page_uri.queryMap().size() == 0) + { + first_query_delimiter = "?"; + } + + // Language + std::string language = LLUI::getLanguage(); + oStr << first_query_delimiter<<"lang=" << language; + + // First Login? + if (gSavedSettings.getBOOL("FirstLoginThisInstall")) + { + oStr << "&firstlogin=TRUE"; + } + + // Channel and Version + std::string version = llformat("%s (%d)", + LLVersionInfo::getShortVersion().c_str(), + LLVersionInfo::getBuild()); + + char* curl_channel = curl_escape(LLVersionInfo::getChannel().c_str(), 0); + char* curl_version = curl_escape(version.c_str(), 0); + + oStr << "&channel=" << curl_channel; + oStr << "&version=" << curl_version; + + curl_free(curl_channel); + curl_free(curl_version); + + // Grid + char* curl_grid = curl_escape(LLGridManager::getInstance()->getGridLabel().c_str(), 0); + oStr << "&grid=" << curl_grid; + curl_free(curl_grid); + + // add OS info + char * os_info = curl_escape(LLAppViewer::instance()->getOSInfo().getOSStringSimple().c_str(), 0); + oStr << "&os=" << os_info; + curl_free(os_info); + + + gViewerWindow->setMenuBackgroundColor(false, !LLGridManager::getInstance()->isInProductionGrid()); + gLoginMenuBarView->setBackgroundColor(gMenuBarView->getBackgroundColor()); + + LLMediaCtrl* web_browser = sInstance->getChild("login_html"); + + // navigate to the "real" page + if (gSavedSettings.getBOOL("RegInClient")) + { + web_browser->setFocus(TRUE); + login_page = sInstance->getString("reg_in_client_url"); + web_browser->navigateTo(login_page, "text/html"); + } + else + { + web_browser->navigateTo( oStr.str(), "text/html" ); + } +} + +void LLPanelLogin::handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent event) +{ + if(event == MEDIA_EVENT_NAVIGATE_COMPLETE) + { + LLMediaCtrl* web_browser = sInstance->getChild("login_html"); + if (web_browser) + { + // *HACK HACK HACK HACK! + /* Stuff a Tab key into the browser now so that the first field will + ** get the focus! The embedded javascript on the page that properly + ** sets the initial focus in a real web browser is not working inside + ** the viewer, so this is an UGLY HACK WORKAROUND for now. + */ + // Commented out as it's not reliable + //web_browser->handleKey(KEY_TAB, MASK_NONE, false); + } + } +} + +//--------------------------------------------------------------------------- +// Protected methods +//--------------------------------------------------------------------------- + +// static +void LLPanelLogin::onClickConnect(void *) +{ + if (sInstance && sInstance->mCallback) + { + // tell the responder we're not here anymore + if ( gResponsePtr ) + gResponsePtr->setParent( 0 ); + + // JC - Make sure the fields all get committed. + sInstance->setFocus(FALSE); + + LLComboBox* combo = sInstance->getChild("server_combo"); + LLSD combo_val = combo->getSelectedValue(); + if (combo_val.isUndefined()) + { + combo_val = combo->getValue(); + } + if(combo_val.isUndefined()) + { + LLNotificationsUtil::add("StartRegionEmpty"); + return; + } + try + { + LLGridManager::getInstance()->setGridChoice(combo_val.asString()); + } + catch (LLInvalidGridName ex) + { + LLSD args; + args["GRID"] = combo_val.asString(); + LLNotificationsUtil::add("InvalidGrid", args); + return; + } + updateStartSLURL(); + std::string username = sInstance->getChild("username_combo")->getValue().asString(); + + + if(username.empty()) + { + // user must type in something into the username field + LLNotificationsUtil::add("MustHaveAccountToLogIn"); + } + else + { + LLPointer cred; + BOOL remember; + getFields(cred, remember); + std::string identifier_type; + cred->identifierType(identifier_type); + LLSD allowed_credential_types; + LLGridManager::getInstance()->getLoginIdentifierTypes(allowed_credential_types); + + // check the typed in credential type against the credential types expected by the server. + for(LLSD::array_iterator i = allowed_credential_types.beginArray(); + i != allowed_credential_types.endArray(); + i++) + { + + if(i->asString() == identifier_type) + { + // yay correct credential type + sInstance->mCallback(0, sInstance->mCallbackData); + return; + } + } + + // Right now, maingrid is the only thing that is picky about + // credential format, as it doesn't yet allow account (single username) + // format creds. - Rox. James, we wanna fix the message when we change + // this. + LLNotificationsUtil::add("InvalidCredentialFormat"); + } + } +} + +/* +// static +bool LLPanelLogin::newAccountAlertCallback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 == option) + { + llinfos << "Going to account creation URL" << llendl; + LLWeb::loadURLExternal( LLNotifications::instance().getGlobalString("CREATE_ACCOUNT_URL")); + } + else + { + sInstance->setFocus(TRUE); + } + return false; +} +*/ + +// static +void LLPanelLogin::onClickNewAccount(void*) +{ + LLWeb::loadURLExternal(sInstance->getString("create_account_url")); +} + + +// static +void LLPanelLogin::onClickVersion(void*) +{ + LLFloaterReg::showInstance("sl_about"); +} + +//static +void LLPanelLogin::onClickForgotPassword(void*) +{ + if (sInstance ) + { + LLWeb::loadURLExternal(sInstance->getString( "forgot_password_url" )); + } +} + +//static +void LLPanelLogin::onClickHelp(void*) +{ + if (sInstance) + { + LLViewerHelp* vhelp = LLViewerHelp::getInstance(); + vhelp->showTopic(vhelp->preLoginTopic()); + } +} + +// static +void LLPanelLogin::onPassKey(LLLineEditor* caller, void* user_data) +{ + LLPanelLogin *This = (LLPanelLogin *) user_data; + This->mPasswordModified = TRUE; + if (gKeyboard->getKeyDown(KEY_CAPSLOCK) && sCapslockDidNotification == FALSE) + { +// *TODO: use another way to notify user about enabled caps lock, see EXT-6858 + sCapslockDidNotification = TRUE; + } +} + + +void LLPanelLogin::updateServer() +{ + try + { + + updateServerCombo(); + // if they've selected another grid, we should load the credentials + // for that grid and set them to the UI. + if(sInstance && !sInstance->areCredentialFieldsDirty()) + { + LLPointer credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); + bool remember = sInstance->getChild("remember_check")->getValue(); + sInstance->setFields(credential, remember); + } + // grid changed so show new splash screen (possibly) + loadLoginPage(); + updateLocationCombo(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION); + } + catch (LLInvalidGridName ex) + { + // do nothing + } +} + +void LLPanelLogin::updateServerCombo() +{ + if (!sInstance) + { + return; + } + // We add all of the possible values, sorted, and then add a bar and the current value at the top + LLComboBox* server_choice_combo = sInstance->getChild("server_combo"); + server_choice_combo->removeall(); + + std::map known_grids = LLGridManager::getInstance()->getKnownGrids(!gSavedSettings.getBOOL("ShowBetaGrids")); + + for (std::map::iterator grid_choice = known_grids.begin(); + grid_choice != known_grids.end(); + grid_choice++) + { + if (!grid_choice->first.empty()) + { + server_choice_combo->add(grid_choice->second, grid_choice->first); + } + } + server_choice_combo->sortByName(); + + server_choice_combo->addSeparator(ADD_TOP); + + server_choice_combo->add(LLGridManager::getInstance()->getGridLabel(), + LLGridManager::getInstance()->getGrid(), ADD_TOP); + + server_choice_combo->selectFirstItem(); +} + +// static +void LLPanelLogin::onSelectServer(LLUICtrl*, void*) +{ + // *NOTE: The paramters for this method are ignored. + // LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe, void*) + // calls this method. + LL_INFOS("AppInit") << "onSelectServer" << LL_ENDL; + // The user twiddled with the grid choice ui. + // apply the selection to the grid setting. + LLPointer credential; + + LLComboBox* combo = sInstance->getChild("server_combo"); + LLSD combo_val = combo->getSelectedValue(); + if (combo_val.isUndefined()) + { + combo_val = combo->getValue(); + } + + combo = sInstance->getChild("start_location_combo"); + combo->setCurrentByIndex(1); + LLStartUp::setStartSLURL(LLSLURL(gSavedSettings.getString("LoginLocation"))); + LLGridManager::getInstance()->setGridChoice(combo_val.asString()); + // This new selection will override preset uris + // from the command line. + updateServer(); + updateLocationCombo(false); + updateLoginPanelLinks(); +} + +void LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe) +{ + if (!sInstance) + { + return; + } + + LLComboBox* combo = sInstance->getChild("server_combo"); + if(fe == combo) + { + onSelectServer(combo, NULL); + } +} + +void LLPanelLogin::updateLoginPanelLinks() +{ + LLSD grid_data; + LLGridManager::getInstance()->getGridInfo(grid_data); + bool system_grid = grid_data.has(GRID_IS_SYSTEM_GRID_VALUE); + + // need to call through sInstance, as it's called from onSelectServer, which + // is static. + sInstance->getChildView("create_new_account_text")->setVisible( system_grid); + sInstance->getChildView("forgot_password_text")->setVisible( system_grid); +} + +//static +void LLPanelLogin::onModeChange() +{ + LLNotificationsUtil::add("ModeChange", LLSD(), LLSD(), boost::bind(&onModeChangeConfirm, _1, _2)); +} + +//static +void LLPanelLogin::onModeChangeConfirm(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + switch (option) + { + case 0: + LLAppViewer::instance()->requestQuit(); + break; + case 1: + // do nothing + break; + default: + break; + } +} diff --git a/indra/newview/llpanelnearbymedia.cpp b/indra/newview/llpanelnearbymedia.cpp index 14e39f2c48..a7f1ab28fd 100644 --- a/indra/newview/llpanelnearbymedia.cpp +++ b/indra/newview/llpanelnearbymedia.cpp @@ -564,16 +564,14 @@ void LLPanelNearByMedia::refreshParcelItems() if (NULL != mParcelMediaItem) { std::string name, url, tooltip; - if (!LLViewerParcelMgr::getInstance()->getAgentParcel()->getObscureMedia()) + getNameAndUrlHelper(LLViewerParcelMedia::getParcelMedia(), name, url, ""); + if (name.empty() || name == url) { - getNameAndUrlHelper(LLViewerParcelMedia::getParcelMedia(), name, url, ""); - if (name.empty() || name == url) - { - tooltip = url; - } - else { - tooltip = name + " : " + url; - } + tooltip = url; + } + else + { + tooltip = name + " : " + url; } LLViewerMediaImpl *impl = LLViewerParcelMedia::getParcelMedia(); updateListItem(mParcelMediaItem, @@ -611,10 +609,8 @@ void LLPanelNearByMedia::refreshParcelItems() bool is_playing = LLViewerMedia::isParcelAudioPlaying(); std::string url; - if (!LLViewerParcelMgr::getInstance()->getAgentParcel()->getObscureMusic()) - { - url = LLViewerMedia::getParcelAudioURL(); - } + url = LLViewerMedia::getParcelAudioURL(); + updateListItem(mParcelAudioItem, mParcelAudioName, url, diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index b07a46a222..b52f33ec3b 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -384,6 +384,16 @@ private: { lldebugs << "Inventory changed: " << mask << llendl; + static bool synchronize_friends_folders = true; + if (synchronize_friends_folders) + { + // Checks whether "Friends" and "Friends/All" folders exist in "Calling Cards" folder, + // fetches their contents if needed and synchronizes it with buddies list. + // If the folders are not found they are created. + LLFriendCardsManager::instance().syncFriendCardsFolders(); + synchronize_friends_folders = false; + } + // *NOTE: deleting of InventoryItem is performed via moving to Trash. // That means LLInventoryObserver::STRUCTURE is present in MASK instead of LLInventoryObserver::REMOVE if ((CALLINGCARD_ADDED & mask) == CALLINGCARD_ADDED) @@ -750,18 +760,23 @@ void LLPanelPeople::updateFriendList() all_friendsp.clear(); online_friendsp.clear(); - LLFriendCardsManager::folderid_buddies_map_t listMap; + uuid_vec_t buddies_uuids; + LLAvatarTracker::buddy_map_t::const_iterator buddies_iter; - // *NOTE: For now collectFriendsLists returns data only for Friends/All folder. EXT-694. - LLFriendCardsManager::instance().collectFriendsLists(listMap); - if (listMap.size() > 0) + // Fill the avatar list with friends UUIDs + for (buddies_iter = all_buddies.begin(); buddies_iter != all_buddies.end(); ++buddies_iter) { - lldebugs << "Friends Cards were found, count: " << listMap.begin()->second.size() << llendl; - all_friendsp = listMap.begin()->second; + buddies_uuids.push_back(buddies_iter->first); + } + + if (buddies_uuids.size() > 0) + { + lldebugs << "Friends added to the list: " << buddies_uuids.size() << llendl; + all_friendsp = buddies_uuids; } else { - lldebugs << "Friends Cards were not found" << llendl; + lldebugs << "No friends found" << llendl; } LLAvatarTracker::buddy_map_t::const_iterator buddy_it = all_buddies.begin(); diff --git a/indra/newview/llrecentpeople.cpp b/indra/newview/llrecentpeople.cpp index 959fd51bbf..7689cd1a52 100644 --- a/indra/newview/llrecentpeople.cpp +++ b/indra/newview/llrecentpeople.cpp @@ -33,7 +33,7 @@ using namespace LLOldEvents; -bool LLRecentPeople::add(const LLUUID& id) +bool LLRecentPeople::add(const LLUUID& id, const LLSD& userdata) { if (id == gAgent.getID()) return false; @@ -42,10 +42,16 @@ bool LLRecentPeople::add(const LLUUID& id) if (is_not_group_id) { - LLDate date_added = LLDate::now(); + // For each avaline call the id of caller is different even if + // the phone number is the same. + // To avoid duplication of avaline list items in the recent list + // of panel People, deleting id's with similar phone number. + const LLUUID& caller_id = getIDByPhoneNumber(userdata); + if (caller_id.notNull()) + mPeople.erase(caller_id); - //[] instead of insert to replace existing id->date with new date value - mPeople[id] = date_added; + //[] instead of insert to replace existing id->llsd["date"] with new date value + mPeople[id] = userdata; mChangedSignal(); } @@ -64,15 +70,55 @@ void LLRecentPeople::get(uuid_vec_t& result) const result.push_back((*pos).first); } -const LLDate& LLRecentPeople::getDate(const LLUUID& id) const +const LLDate LLRecentPeople::getDate(const LLUUID& id) const { recent_people_t::const_iterator it = mPeople.find(id); - if (it!= mPeople.end()) return (*it).second; + if (it!= mPeople.end()) return it->second["date"].asDate(); static LLDate no_date = LLDate(); return no_date; } +const LLSD& LLRecentPeople::getData(const LLUUID& id) const +{ + recent_people_t::const_iterator it = mPeople.find(id); + + if (it != mPeople.end()) + return it->second; + + static LLSD no_data = LLSD(); + return no_data; +} + +bool LLRecentPeople::isAvalineCaller(const LLUUID& id) const +{ + recent_people_t::const_iterator it = mPeople.find(id); + + if (it != mPeople.end()) + { + const LLSD& user = it->second; + return user["avaline_call"].asBoolean(); + } + + return false; +} + +const LLUUID& LLRecentPeople::getIDByPhoneNumber(const LLSD& userdata) +{ + if (!userdata["avaline_call"].asBoolean()) + return LLUUID::null; + + for (recent_people_t::const_iterator it = mPeople.begin(); it != mPeople.end(); ++it) + { + const LLSD& user_info = it->second; + + if (user_info["call_number"].asString() == userdata["call_number"].asString()) + return it->first; + } + + return LLUUID::null; +} + // virtual bool LLRecentPeople::handleEvent(LLPointer event, const LLSD& userdata) { diff --git a/indra/newview/llrecentpeople.h b/indra/newview/llrecentpeople.h index 852a92ff80..d0d6376867 100644 --- a/indra/newview/llrecentpeople.h +++ b/indra/newview/llrecentpeople.h @@ -58,9 +58,15 @@ public: * Add specified avatar to the list if it's not there already. * * @param id avatar to add. + * + * @param userdata additional information about last interaction party. + * For example when last interaction party is not an avatar + * but an avaline caller, additional info (such as phone + * number, session id and etc.) should be added. + * * @return false if the avatar is in the list already, true otherwise */ - bool add(const LLUUID& id); + bool add(const LLUUID& id, const LLSD& userdata = LLSD().with("date", LLDate::now())); /** * @param id avatar to search. @@ -75,7 +81,25 @@ public: */ void get(uuid_vec_t& result) const; - const LLDate& getDate(const LLUUID& id) const; + /** + * Returns last interaction time with specified participant + * + */ + const LLDate getDate(const LLUUID& id) const; + + /** + * Returns data about specified participant + * + * @param id identifier of specific participant + */ + const LLSD& getData(const LLUUID& id) const; + + /** + * Checks whether specific participant is an avaline caller + * + * @param id identifier of specific participant + */ + bool isAvalineCaller(const LLUUID& id) const; /** * Set callback to be called when the list changed. @@ -92,7 +116,10 @@ public: /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); private: - typedef std::map recent_people_t; + + const LLUUID& getIDByPhoneNumber(const LLSD& userdata); + + typedef std::map recent_people_t; recent_people_t mPeople; signal_t mChangedSignal; }; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index da891d1c51..50bc0b4a98 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -65,6 +65,7 @@ #include "llinventorymodel.h" #include "llmenugl.h" #include "llmutelist.h" +#include "llnotificationsutil.h" #include "llsidepaneltaskinfo.h" #include "llslurl.h" #include "llstatusbar.h" @@ -562,6 +563,103 @@ BOOL LLSelectMgr::removeObjectFromSelections(const LLUUID &id) return object_found; } +bool LLSelectMgr::linkObjects() +{ + if (!LLSelectMgr::getInstance()->selectGetAllRootsValid()) + { + LLNotificationsUtil::add("UnableToLinkWhileDownloading"); + return true; + } + + S32 object_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); + if (object_count > MAX_CHILDREN_PER_TASK + 1) + { + LLSD args; + args["COUNT"] = llformat("%d", object_count); + int max = MAX_CHILDREN_PER_TASK+1; + args["MAX"] = llformat("%d", max); + LLNotificationsUtil::add("UnableToLinkObjects", args); + return true; + } + + if (LLSelectMgr::getInstance()->getSelection()->getRootObjectCount() < 2) + { + LLNotificationsUtil::add("CannotLinkIncompleteSet"); + return true; + } + + if (!LLSelectMgr::getInstance()->selectGetRootsModify()) + { + LLNotificationsUtil::add("CannotLinkModify"); + return true; + } + + LLUUID owner_id; + std::string owner_name; + if (!LLSelectMgr::getInstance()->selectGetOwner(owner_id, owner_name)) + { + // we don't actually care if you're the owner, but novices are + // the most likely to be stumped by this one, so offer the + // easiest and most likely solution. + LLNotificationsUtil::add("CannotLinkDifferentOwners"); + return true; + } + + LLSelectMgr::getInstance()->sendLink(); + + return true; +} + +bool LLSelectMgr::unlinkObjects() +{ + LLSelectMgr::getInstance()->sendDelink(); + return true; +} + +// in order to link, all objects must have the same owner, and the +// agent must have the ability to modify all of the objects. However, +// we're not answering that question with this method. The question +// we're answering is: does the user have a reasonable expectation +// that a link operation should work? If so, return true, false +// otherwise. this allows the handle_link method to more finely check +// the selection and give an error message when the uer has a +// reasonable expectation for the link to work, but it will fail. +bool LLSelectMgr::enableLinkObjects() +{ + bool new_value = false; + // check if there are at least 2 objects selected, and that the + // user can modify at least one of the selected objects. + + // in component mode, can't link + if (!gSavedSettings.getBOOL("EditLinkedParts")) + { + if(LLSelectMgr::getInstance()->selectGetAllRootsValid() && LLSelectMgr::getInstance()->getSelection()->getRootObjectCount() >= 2) + { + struct f : public LLSelectedObjectFunctor + { + virtual bool apply(LLViewerObject* object) + { + return object->permModify(); + } + } func; + const bool firstonly = true; + new_value = LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, firstonly); + } + } + return new_value; +} + +bool LLSelectMgr::enableUnlinkObjects() +{ + LLViewerObject* first_editable_object = LLSelectMgr::getInstance()->getSelection()->getFirstEditableObject(); + + bool new_value = LLSelectMgr::getInstance()->selectGetAllRootsValid() && + first_editable_object && + !first_editable_object->isAttachment(); + + return new_value; +} + void LLSelectMgr::deselectObjectAndFamily(LLViewerObject* object, BOOL send_to_sim, BOOL include_entire_object) { // bail if nothing selected or if object wasn't selected in the first place diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 65a9a493f6..cb387f5c3c 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -439,6 +439,17 @@ public: BOOL removeObjectFromSelections(const LLUUID &id); + //////////////////////////////////////////////////////////////// + // Selection editing + //////////////////////////////////////////////////////////////// + bool linkObjects(); + + bool unlinkObjects(); + + bool enableLinkObjects(); + + bool enableUnlinkObjects(); + //////////////////////////////////////////////////////////////// // Selection accessors //////////////////////////////////////////////////////////////// diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 8c5a52c187..3c53e54203 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -309,6 +309,15 @@ static bool handleRenderUseVBOChanged(const LLSD& newvalue) return true; } +static bool handleRenderUseVBOMappingChanged(const LLSD& newvalue) +{ + if (gPipeline.isInit()) + { + gPipeline.setDisableVBOMapping(newvalue.asBoolean()); + } + return true; +} + static bool handleWLSkyDetailChanged(const LLSD&) { if (gSky.mVOWLSkyp.notNull()) @@ -589,6 +598,7 @@ void settings_setup_listeners() gSavedSettings.getControl("MuteAmbient")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2)); gSavedSettings.getControl("MuteUI")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2)); gSavedSettings.getControl("RenderVBOEnable")->getSignal()->connect(boost::bind(&handleRenderUseVBOChanged, _2)); + gSavedSettings.getControl("RenderVBOMappingDisable")->getSignal()->connect(boost::bind(&handleRenderUseVBOMappingChanged, _2)); gSavedSettings.getControl("RenderUseStreamVBO")->getSignal()->connect(boost::bind(&handleResetVertexBuffersChanged, _2)); gSavedSettings.getControl("WLSkyDetail")->getSignal()->connect(boost::bind(&handleWLSkyDetailChanged, _2)); gSavedSettings.getControl("NumpadControl")->getSignal()->connect(boost::bind(&handleNumpadControlChanged, _2)); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 38877b7836..030808db92 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -1,8271 +1,8167 @@ -/** - * @file llviewermenu.cpp - * @brief Builds menus out of items. - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" -#include "llviewermenu.h" - -// linden library includes -#include "llavatarnamecache.h" // IDEVO -#include "llfloaterreg.h" -#include "llcombobox.h" -#include "llinventorypanel.h" -#include "llnotifications.h" -#include "llnotificationsutil.h" - -// newview includes -#include "llagent.h" -#include "llagentcamera.h" -#include "llagentwearables.h" -#include "llagentpilot.h" -#include "llbottomtray.h" -#include "llcompilequeue.h" -#include "llconsole.h" -#include "lldebugview.h" -#include "llfilepicker.h" -#include "llfirstuse.h" -#include "llfloaterbuy.h" -#include "llfloaterbuycontents.h" -#include "llbuycurrencyhtml.h" -#include "llfloatergodtools.h" -#include "llfloaterinventory.h" -#include "llfloaterland.h" -#include "llfloaterpay.h" -#include "llfloaterreporter.h" -#include "llfloatersearch.h" -#include "llfloaterscriptdebug.h" -#include "llfloatersnapshot.h" -#include "llfloatertools.h" -#include "llfloaterworldmap.h" -#include "llavataractions.h" -#include "lllandmarkactions.h" -#include "llgroupmgr.h" -#include "lltooltip.h" -#include "llhints.h" -#include "llhudeffecttrail.h" -#include "llhudmanager.h" -#include "llimview.h" -#include "llinventorybridge.h" -#include "llinventorydefines.h" -#include "llinventoryfunctions.h" -#include "llpanellogin.h" -#include "llpanelblockedlist.h" -#include "llmenucommands.h" -#include "llmoveview.h" -#include "llparcel.h" -#include "llrootview.h" -#include "llselectmgr.h" -#include "llsidetray.h" -#include "llstatusbar.h" -#include "lltextureview.h" -#include "lltoolcomp.h" -#include "lltoolmgr.h" -#include "lltoolpie.h" -#include "lltoolselectland.h" -#include "lltrans.h" -#include "llviewergenericmessage.h" -#include "llviewerhelp.h" -#include "llviewermenufile.h" // init_menu_file() -#include "llviewermessage.h" -#include "llviewernetwork.h" -#include "llviewerobjectlist.h" -#include "llviewerparcelmgr.h" -#include "llviewerstats.h" -#include "llvoavatarself.h" -#include "llworldmap.h" -#include "pipeline.h" -#include "llviewerjoystick.h" -#include "llwlanimator.h" -#include "llwlparammanager.h" -#include "llfloatercamera.h" -#include "lluilistener.h" -#include "llappearancemgr.h" -#include "lltrans.h" -#include "lleconomy.h" -#include "boost/unordered_map.hpp" - -using namespace LLVOAvatarDefines; - -static boost::unordered_map sDefaultItemLabels; - -BOOL enable_land_build(void*); -BOOL enable_object_build(void*); - -LLVOAvatar* find_avatar_from_object( LLViewerObject* object ); -LLVOAvatar* find_avatar_from_object( const LLUUID& object_id ); - -void handle_test_load_url(void*); - -// -// Evil hackish imported globals - -//extern BOOL gHideSelectedObjects; -//extern BOOL gAllowSelectAvatar; -//extern BOOL gDebugAvatarRotation; -extern BOOL gDebugClicks; -extern BOOL gDebugWindowProc; -//extern BOOL gDebugTextEditorTips; -//extern BOOL gDebugSelectMgr; - -// -// Globals -// - -LLMenuBarGL *gMenuBarView = NULL; -LLViewerMenuHolderGL *gMenuHolder = NULL; -LLMenuGL *gPopupMenuView = NULL; -LLMenuGL *gEditMenu = NULL; -LLMenuBarGL *gLoginMenuBarView = NULL; - -// Pie menus -LLContextMenu *gMenuAvatarSelf = NULL; -LLContextMenu *gMenuAvatarOther = NULL; -LLContextMenu *gMenuObject = NULL; -LLContextMenu *gMenuAttachmentSelf = NULL; -LLContextMenu *gMenuAttachmentOther = NULL; -LLContextMenu *gMenuLand = NULL; - -const std::string SAVE_INTO_INVENTORY("Save Object Back to My Inventory"); -const std::string SAVE_INTO_TASK_INVENTORY("Save Object Back to Object Contents"); - -LLMenuGL* gAttachSubMenu = NULL; -LLMenuGL* gDetachSubMenu = NULL; -LLMenuGL* gTakeOffClothes = NULL; -LLContextMenu* gAttachScreenPieMenu = NULL; -LLContextMenu* gAttachPieMenu = NULL; -LLContextMenu* gAttachBodyPartPieMenus[8]; -LLContextMenu* gDetachPieMenu = NULL; -LLContextMenu* gDetachScreenPieMenu = NULL; -LLContextMenu* gDetachBodyPartPieMenus[8]; - -LLMenuItemCallGL* gAFKMenu = NULL; -LLMenuItemCallGL* gBusyMenu = NULL; - -// -// Local prototypes - -// File Menu -const char* upload_pick(void* data); -void handle_compress_image(void*); - - -// Edit menu -void handle_dump_group_info(void *); -void handle_dump_capabilities_info(void *); - -// Advanced->Consoles menu -void handle_region_dump_settings(void*); -void handle_region_dump_temp_asset_data(void*); -void handle_region_clear_temp_asset_data(void*); - -// Object pie menu -BOOL sitting_on_selection(); - -void near_sit_object(); -//void label_sit_or_stand(std::string& label, void*); -// buy and take alias into the same UI positions, so these -// declarations handle this mess. -BOOL is_selection_buy_not_take(); -S32 selection_price(); -BOOL enable_take(); -void handle_take(); -void handle_object_show_inspector(); -void handle_avatar_show_inspector(); -bool confirm_take(const LLSD& notification, const LLSD& response); - -void handle_buy_object(LLSaleInfo sale_info); -void handle_buy_contents(LLSaleInfo sale_info); - -// Land pie menu -void near_sit_down_point(BOOL success, void *); - -// Avatar pie menu - -// Debug menu - - -void velocity_interpolate( void* ); - -void handle_rebake_textures(void*); -BOOL check_admin_override(void*); -void handle_admin_override_toggle(void*); -#ifdef TOGGLE_HACKED_GODLIKE_VIEWER -void handle_toggle_hacked_godmode(void*); -BOOL check_toggle_hacked_godmode(void*); -bool enable_toggle_hacked_godmode(void*); -#endif - -void toggle_show_xui_names(void *); -BOOL check_show_xui_names(void *); - -// Debug UI - -void handle_buy_currency_test(void*); - -void handle_god_mode(void*); - -// God menu -void handle_leave_god_mode(void*); - - -void handle_reset_view(); - -void handle_duplicate_in_place(void*); - - -void handle_object_owner_self(void*); -void handle_object_owner_permissive(void*); -void handle_object_lock(void*); -void handle_object_asset_ids(void*); -void force_take_copy(void*); -#ifdef _CORY_TESTING -void force_export_copy(void*); -void force_import_geometry(void*); -#endif - -void handle_force_parcel_owner_to_me(void*); -void handle_force_parcel_to_content(void*); -void handle_claim_public_land(void*); - -void handle_god_request_avatar_geometry(void *); // Hack for easy testing of new avatar geometry -void reload_vertex_shader(void *); -void handle_disconnect_viewer(void *); - -void force_error_breakpoint(void *); -void force_error_llerror(void *); -void force_error_bad_memory_access(void *); -void force_error_infinite_loop(void *); -void force_error_software_exception(void *); -void force_error_driver_crash(void *); - -void handle_force_delete(void*); -void print_object_info(void*); -void print_agent_nvpairs(void*); -void toggle_debug_menus(void*); -void upload_done_callback(const LLUUID& uuid, void* user_data, S32 result, LLExtStat ext_status); -void dump_select_mgr(void*); - -void dump_inventory(void*); -void toggle_visibility(void*); -BOOL get_visibility(void*); - -// Avatar Pie menu -void request_friendship(const LLUUID& agent_id); - -// Tools menu -void handle_selected_texture_info(void*); - -void handle_dump_followcam(void*); -void handle_viewer_enable_message_log(void*); -void handle_viewer_disable_message_log(void*); - -BOOL enable_buy_land(void*); - -// Help menu - -void handle_test_male(void *); -void handle_test_female(void *); -void handle_toggle_pg(void*); -void handle_dump_attachments(void *); -void handle_dump_avatar_local_textures(void*); -void handle_debug_avatar_textures(void*); -void handle_grab_baked_texture(void*); -BOOL enable_grab_baked_texture(void*); -void handle_dump_region_object_cache(void*); - -BOOL enable_save_into_inventory(void*); -BOOL enable_save_into_task_inventory(void*); - -BOOL enable_detach(const LLSD& = LLSD()); -void menu_toggle_attached_lights(void* user_data); -void menu_toggle_attached_particles(void* user_data); - -class LLMenuParcelObserver : public LLParcelObserver -{ -public: - LLMenuParcelObserver(); - ~LLMenuParcelObserver(); - virtual void changed(); -}; - -static LLMenuParcelObserver* gMenuParcelObserver = NULL; - -static LLUIListener sUIListener; - -LLMenuParcelObserver::LLMenuParcelObserver() -{ - LLViewerParcelMgr::getInstance()->addObserver(this); -} - -LLMenuParcelObserver::~LLMenuParcelObserver() -{ - LLViewerParcelMgr::getInstance()->removeObserver(this); -} - -void LLMenuParcelObserver::changed() -{ - gMenuHolder->childSetEnabled("Land Buy Pass", LLPanelLandGeneral::enableBuyPass(NULL)); - - BOOL buyable = enable_buy_land(NULL); - gMenuHolder->childSetEnabled("Land Buy", buyable); - gMenuHolder->childSetEnabled("Buy Land...", buyable); -} - - -void initialize_menus(); - -//----------------------------------------------------------------------------- -// Initialize main menus -// -// HOW TO NAME MENUS: -// -// First Letter Of Each Word Is Capitalized, Even At Or And -// -// Items that lead to dialog boxes end in "..." -// -// Break up groups of more than 6 items with separators -//----------------------------------------------------------------------------- - -void set_underclothes_menu_options() -{ - if (gMenuHolder && gAgent.isTeen()) - { - gMenuHolder->getChild("Self Underpants")->setVisible(FALSE); - gMenuHolder->getChild("Self Undershirt")->setVisible(FALSE); - } - if (gMenuBarView && gAgent.isTeen()) - { - gMenuBarView->getChild("Menu Underpants")->setVisible(FALSE); - gMenuBarView->getChild("Menu Undershirt")->setVisible(FALSE); - } -} - -void init_menus() -{ - S32 top = gViewerWindow->getRootView()->getRect().getHeight(); - - // Initialize actions - initialize_menus(); - - /// - /// Popup menu - /// - /// The popup menu is now populated by the show_context_menu() - /// method. - - LLMenuGL::Params menu_params; - menu_params.name = "Popup"; - menu_params.visible = false; - gPopupMenuView = LLUICtrlFactory::create(menu_params); - gMenuHolder->addChild( gPopupMenuView ); - - /// - /// Context menus - /// - - const widget_registry_t& registry = - LLViewerMenuHolderGL::child_registry_t::instance(); - gEditMenu = LLUICtrlFactory::createFromFile("menu_edit.xml", gMenuHolder, registry); - gMenuAvatarSelf = LLUICtrlFactory::createFromFile( - "menu_avatar_self.xml", gMenuHolder, registry); - gMenuAvatarOther = LLUICtrlFactory::createFromFile( - "menu_avatar_other.xml", gMenuHolder, registry); - - gDetachScreenPieMenu = gMenuHolder->getChild("Object Detach HUD", true); - gDetachPieMenu = gMenuHolder->getChild("Object Detach", true); - - gMenuObject = LLUICtrlFactory::createFromFile( - "menu_object.xml", gMenuHolder, registry); - - gAttachScreenPieMenu = gMenuHolder->getChild("Object Attach HUD"); - gAttachPieMenu = gMenuHolder->getChild("Object Attach"); - - gMenuAttachmentSelf = LLUICtrlFactory::createFromFile( - "menu_attachment_self.xml", gMenuHolder, registry); - gMenuAttachmentOther = LLUICtrlFactory::createFromFile( - "menu_attachment_other.xml", gMenuHolder, registry); - - gMenuLand = LLUICtrlFactory::createFromFile( - "menu_land.xml", gMenuHolder, registry); - - /// - /// set up the colors - /// - LLColor4 color; - - LLColor4 context_menu_color = LLUIColorTable::instance().getColor("MenuPopupBgColor"); - - gMenuAvatarSelf->setBackgroundColor( context_menu_color ); - gMenuAvatarOther->setBackgroundColor( context_menu_color ); - gMenuObject->setBackgroundColor( context_menu_color ); - gMenuAttachmentSelf->setBackgroundColor( context_menu_color ); - gMenuAttachmentOther->setBackgroundColor( context_menu_color ); - - gMenuLand->setBackgroundColor( context_menu_color ); - - color = LLUIColorTable::instance().getColor( "MenuPopupBgColor" ); - gPopupMenuView->setBackgroundColor( color ); - - // If we are not in production, use a different color to make it apparent. - if (LLGridManager::getInstance()->isInProductionGrid()) - { - color = LLUIColorTable::instance().getColor( "MenuBarBgColor" ); - } - else - { - color = LLUIColorTable::instance().getColor( "MenuNonProductionBgColor" ); - } - gMenuBarView = LLUICtrlFactory::getInstance()->createFromFile("menu_viewer.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - gMenuBarView->setRect(LLRect(0, top, 0, top - MENU_BAR_HEIGHT)); - gMenuBarView->setBackgroundColor( color ); - - LLView* menu_bar_holder = gViewerWindow->getRootView()->getChildView("menu_bar_holder"); - menu_bar_holder->addChild(gMenuBarView); - - gViewerWindow->setMenuBackgroundColor(false, - LLGridManager::getInstance()->isInProductionGrid()); - - // Assume L$10 for now, the server will tell us the real cost at login - // *TODO:Also fix cost in llfolderview.cpp for Inventory menus - const std::string upload_cost("10"); - gMenuHolder->childSetLabelArg("Upload Image", "[COST]", upload_cost); - gMenuHolder->childSetLabelArg("Upload Sound", "[COST]", upload_cost); - gMenuHolder->childSetLabelArg("Upload Animation", "[COST]", upload_cost); - gMenuHolder->childSetLabelArg("Bulk Upload", "[COST]", upload_cost); - - gAFKMenu = gMenuBarView->getChild("Set Away", TRUE); - gBusyMenu = gMenuBarView->getChild("Set Busy", TRUE); - gAttachSubMenu = gMenuBarView->findChildMenuByName("Attach Object", TRUE); - gDetachSubMenu = gMenuBarView->findChildMenuByName("Detach Object", TRUE); - -#if !MEM_TRACK_MEM - // Don't display the Memory console menu if the feature is turned off - LLMenuItemCheckGL *memoryMenu = gMenuBarView->getChild("Memory", TRUE); - if (memoryMenu) - { - memoryMenu->setVisible(FALSE); - } -#endif - - gMenuBarView->createJumpKeys(); - - // Let land based option enable when parcel changes - gMenuParcelObserver = new LLMenuParcelObserver(); - - gLoginMenuBarView = LLUICtrlFactory::getInstance()->createFromFile("menu_login.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - gLoginMenuBarView->arrangeAndClear(); - LLRect menuBarRect = gLoginMenuBarView->getRect(); - menuBarRect.setLeftTopAndSize(0, menu_bar_holder->getRect().getHeight(), menuBarRect.getWidth(), menuBarRect.getHeight()); - gLoginMenuBarView->setRect(menuBarRect); - gLoginMenuBarView->setBackgroundColor( color ); - menu_bar_holder->addChild(gLoginMenuBarView); - - // tooltips are on top of EVERYTHING, including menus - gViewerWindow->getRootView()->sendChildToFront(gToolTipView); -} - -/////////////////// -// SHOW CONSOLES // -/////////////////// - - -class LLAdvancedToggleConsole : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string console_type = userdata.asString(); - if ("texture" == console_type) - { - toggle_visibility( (void*)gTextureView ); - } - else if ("debug" == console_type) - { - toggle_visibility( (void*)static_cast(gDebugView->mDebugConsolep)); - } - else if (gTextureSizeView && "texture size" == console_type) - { - toggle_visibility( (void*)gTextureSizeView ); - } - else if (gTextureCategoryView && "texture category" == console_type) - { - toggle_visibility( (void*)gTextureCategoryView ); - } - else if ("fast timers" == console_type) - { - toggle_visibility( (void*)gDebugView->mFastTimerView ); - } -#if MEM_TRACK_MEM - else if ("memory view" == console_type) - { - toggle_visibility( (void*)gDebugView->mMemoryView ); - } -#endif - return true; - } -}; -class LLAdvancedCheckConsole : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string console_type = userdata.asString(); - bool new_value = false; - if ("texture" == console_type) - { - new_value = get_visibility( (void*)gTextureView ); - } - else if ("debug" == console_type) - { - new_value = get_visibility( (void*)((LLView*)gDebugView->mDebugConsolep) ); - } - else if (gTextureSizeView && "texture size" == console_type) - { - new_value = get_visibility( (void*)gTextureSizeView ); - } - else if (gTextureCategoryView && "texture category" == console_type) - { - new_value = get_visibility( (void*)gTextureCategoryView ); - } - else if ("fast timers" == console_type) - { - new_value = get_visibility( (void*)gDebugView->mFastTimerView ); - } -#if MEM_TRACK_MEM - else if ("memory view" == console_type) - { - new_value = get_visibility( (void*)gDebugView->mMemoryView ); - } -#endif - - return new_value; - } -}; - - -////////////////////////// -// DUMP INFO TO CONSOLE // -////////////////////////// - - -class LLAdvancedDumpInfoToConsole : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string info_type = userdata.asString(); - if ("region" == info_type) - { - handle_region_dump_settings(NULL); - } - else if ("group" == info_type) - { - handle_dump_group_info(NULL); - } - else if ("capabilities" == info_type) - { - handle_dump_capabilities_info(NULL); - } - return true; - } -}; - - -////////////// -// HUD INFO // -////////////// - - -class LLAdvancedToggleHUDInfo : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string info_type = userdata.asString(); - - if ("camera" == info_type) - { - gDisplayCameraPos = !(gDisplayCameraPos); - } - else if ("wind" == info_type) - { - gDisplayWindInfo = !(gDisplayWindInfo); - } - else if ("fov" == info_type) - { - gDisplayFOV = !(gDisplayFOV); - } - else if ("badge" == info_type) - { - gDisplayBadge = !(gDisplayBadge); - } - return true; - } -}; - -class LLAdvancedCheckHUDInfo : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string info_type = userdata.asString(); - bool new_value = false; - if ("camera" == info_type) - { - new_value = gDisplayCameraPos; - } - else if ("wind" == info_type) - { - new_value = gDisplayWindInfo; - } - else if ("fov" == info_type) - { - new_value = gDisplayFOV; - } - else if ("badge" == info_type) - { - new_value = gDisplayBadge; - } - return new_value; - } -}; - - -////////////// -// FLYING // -////////////// - -class LLAdvancedAgentFlyingInfo : public view_listener_t -{ - bool handleEvent(const LLSD&) - { - return gAgent.getFlying(); - } -}; - - -/////////////////////// -// CLEAR GROUP CACHE // -/////////////////////// - -class LLAdvancedClearGroupCache : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLGroupMgr::debugClearAllGroups(NULL); - return true; - } -}; - - - - -///////////////// -// RENDER TYPE // -///////////////// -U32 render_type_from_string(std::string render_type) -{ - if ("simple" == render_type) - { - return LLPipeline::RENDER_TYPE_SIMPLE; - } - else if ("alpha" == render_type) - { - return LLPipeline::RENDER_TYPE_ALPHA; - } - else if ("tree" == render_type) - { - return LLPipeline::RENDER_TYPE_TREE; - } - else if ("character" == render_type) - { - return LLPipeline::RENDER_TYPE_AVATAR; - } - else if ("surfacePath" == render_type) - { - return LLPipeline::RENDER_TYPE_TERRAIN; - } - else if ("sky" == render_type) - { - return LLPipeline::RENDER_TYPE_SKY; - } - else if ("water" == render_type) - { - return LLPipeline::RENDER_TYPE_WATER; - } - else if ("ground" == render_type) - { - return LLPipeline::RENDER_TYPE_GROUND; - } - else if ("volume" == render_type) - { - return LLPipeline::RENDER_TYPE_VOLUME; - } - else if ("grass" == render_type) - { - return LLPipeline::RENDER_TYPE_GRASS; - } - else if ("clouds" == render_type) - { - return LLPipeline::RENDER_TYPE_CLOUDS; - } - else if ("particles" == render_type) - { - return LLPipeline::RENDER_TYPE_PARTICLES; - } - else if ("bump" == render_type) - { - return LLPipeline::RENDER_TYPE_BUMP; - } - else - { - return 0; - } -} - - -class LLAdvancedToggleRenderType : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - U32 render_type = render_type_from_string( userdata.asString() ); - if ( render_type != 0 ) - { - LLPipeline::toggleRenderTypeControl( (void*)render_type ); - } - return true; - } -}; - - -class LLAdvancedCheckRenderType : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - U32 render_type = render_type_from_string( userdata.asString() ); - bool new_value = false; - - if ( render_type != 0 ) - { - new_value = LLPipeline::hasRenderTypeControl( (void*)render_type ); - } - - return new_value; - } -}; - - -///////////// -// FEATURE // -///////////// -U32 feature_from_string(std::string feature) -{ - if ("ui" == feature) - { - return LLPipeline::RENDER_DEBUG_FEATURE_UI; - } - else if ("selected" == feature) - { - return LLPipeline::RENDER_DEBUG_FEATURE_SELECTED; - } - else if ("highlighted" == feature) - { - return LLPipeline::RENDER_DEBUG_FEATURE_HIGHLIGHTED; - } - else if ("dynamic textures" == feature) - { - return LLPipeline::RENDER_DEBUG_FEATURE_DYNAMIC_TEXTURES; - } - else if ("foot shadows" == feature) - { - return LLPipeline::RENDER_DEBUG_FEATURE_FOOT_SHADOWS; - } - else if ("fog" == feature) - { - return LLPipeline::RENDER_DEBUG_FEATURE_FOG; - } - else if ("fr info" == feature) - { - return LLPipeline::RENDER_DEBUG_FEATURE_FR_INFO; - } - else if ("flexible" == feature) - { - return LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE; - } - else - { - return 0; - } -}; - - -class LLAdvancedToggleFeature : public view_listener_t{ - bool handleEvent(const LLSD& userdata) - { - U32 feature = feature_from_string( userdata.asString() ); - if ( feature != 0 ) - { - LLPipeline::toggleRenderDebugFeature( (void*)feature ); - } - return true; - } -}; - -class LLAdvancedCheckFeature : public view_listener_t -{bool handleEvent(const LLSD& userdata) -{ - U32 feature = feature_from_string( userdata.asString() ); - bool new_value = false; - - if ( feature != 0 ) - { - new_value = LLPipeline::toggleRenderDebugFeatureControl( (void*)feature ); - } - - return new_value; -} -}; - -void toggle_destination_and_avatar_picker(const LLSD& show) -{ - S32 panel_idx = show.isDefined() ? show.asInteger() : -1; - LLView* container = gViewerWindow->getRootView()->getChildView("avatar_picker_and_destination_guide_container"); - LLMediaCtrl* destinations = container->findChild("destination_guide_contents"); - LLMediaCtrl* avatar_picker = container->findChild("avatar_picker_contents"); - - switch(panel_idx) - { - case 0: - container->setVisible(true); - destinations->setVisible(true); - avatar_picker->setVisible(false); - LLFirstUse::notUsingDestinationGuide(false); - break; - case 1: - container->setVisible(true); - destinations->setVisible(false); - avatar_picker->setVisible(true); - break; - default: - container->setVisible(false); - destinations->setVisible(false); - avatar_picker->setVisible(false); - break; - } - - gViewerWindow->getRootView()->getChildView("bottom_tray")->getChild("avatar_and_destination_btns")->setValue(show); -}; - - -////////////////// -// INFO DISPLAY // -////////////////// -U32 info_display_from_string(std::string info_display) -{ - if ("verify" == info_display) - { - return LLPipeline::RENDER_DEBUG_VERIFY; - } - else if ("bboxes" == info_display) - { - return LLPipeline::RENDER_DEBUG_BBOXES; - } - else if ("points" == info_display) - { - return LLPipeline::RENDER_DEBUG_POINTS; - } - else if ("octree" == info_display) - { - return LLPipeline::RENDER_DEBUG_OCTREE; - } - else if ("shadow frusta" == info_display) - { - return LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA; - } - else if ("occlusion" == info_display) - { - return LLPipeline::RENDER_DEBUG_OCCLUSION; - } - else if ("render batches" == info_display) - { - return LLPipeline::RENDER_DEBUG_BATCH_SIZE; - } - else if ("update type" == info_display) - { - return LLPipeline::RENDER_DEBUG_UPDATE_TYPE; - } - else if ("texture anim" == info_display) - { - return LLPipeline::RENDER_DEBUG_TEXTURE_ANIM; - } - else if ("texture priority" == info_display) - { - return LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY; - } - else if ("shame" == info_display) - { - return LLPipeline::RENDER_DEBUG_SHAME; - } - else if ("texture area" == info_display) - { - return LLPipeline::RENDER_DEBUG_TEXTURE_AREA; - } - else if ("face area" == info_display) - { - return LLPipeline::RENDER_DEBUG_FACE_AREA; - } - else if ("lights" == info_display) - { - return LLPipeline::RENDER_DEBUG_LIGHTS; - } - else if ("particles" == info_display) - { - return LLPipeline::RENDER_DEBUG_PARTICLES; - } - else if ("composition" == info_display) - { - return LLPipeline::RENDER_DEBUG_COMPOSITION; - } - else if ("glow" == info_display) - { - return LLPipeline::RENDER_DEBUG_GLOW; - } - else if ("collision skeleton" == info_display) - { - return LLPipeline::RENDER_DEBUG_AVATAR_VOLUME; - } - else if ("raycast" == info_display) - { - return LLPipeline::RENDER_DEBUG_RAYCAST; - } - else if ("agent target" == info_display) - { - return LLPipeline::RENDER_DEBUG_AGENT_TARGET; - } - else - { - return 0; - } -}; - -class LLAdvancedToggleInfoDisplay : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - U32 info_display = info_display_from_string( userdata.asString() ); - - if ( info_display != 0 ) - { - LLPipeline::toggleRenderDebug( (void*)info_display ); - } - - return true; - } -}; - - -class LLAdvancedCheckInfoDisplay : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - U32 info_display = info_display_from_string( userdata.asString() ); - bool new_value = false; - - if ( info_display != 0 ) - { - new_value = LLPipeline::toggleRenderDebugControl( (void*)info_display ); - } - - return new_value; - } -}; - - -/////////////////////////// -//// RANDOMIZE FRAMERATE // -/////////////////////////// - - -class LLAdvancedToggleRandomizeFramerate : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gRandomizeFramerate = !(gRandomizeFramerate); - return true; - } -}; - -class LLAdvancedCheckRandomizeFramerate : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = gRandomizeFramerate; - return new_value; - } -}; - -void run_vectorize_perf_test(void *) -{ - gSavedSettings.setBOOL("VectorizePerfTest", TRUE); -} - - -//////////////////////////////// -// RUN Vectorized Perform Test// -//////////////////////////////// - - -class LLAdvancedVectorizePerfTest : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - run_vectorize_perf_test(NULL); - return true; - } -}; - -/////////////////////////// -//// PERIODIC SLOW FRAME // -/////////////////////////// - - -class LLAdvancedTogglePeriodicSlowFrame : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gPeriodicSlowFrame = !(gPeriodicSlowFrame); - return true; - } -}; - -class LLAdvancedCheckPeriodicSlowFrame : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = gPeriodicSlowFrame; - return new_value; - } -}; - - - -//////////////// -// FRAME TEST // -//////////////// - - -class LLAdvancedToggleFrameTest : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLPipeline::sRenderFrameTest = !(LLPipeline::sRenderFrameTest); - return true; - } -}; - -class LLAdvancedCheckFrameTest : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLPipeline::sRenderFrameTest; - return new_value; - } -}; - - -/////////////////////////// -// SELECTED TEXTURE INFO // -/////////////////////////// - - -class LLAdvancedSelectedTextureInfo : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_selected_texture_info(NULL); - return true; - } -}; - -////////////////////// -// TOGGLE WIREFRAME // -////////////////////// - -class LLAdvancedToggleWireframe : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gUseWireframe = !(gUseWireframe); - LLPipeline::updateRenderDeferred(); - gPipeline.resetVertexBuffers(); - return true; - } -}; - -class LLAdvancedCheckWireframe : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = gUseWireframe; - return new_value; - } -}; - -////////////////////// -// TEXTURE ATLAS // -////////////////////// - -class LLAdvancedToggleTextureAtlas : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLViewerTexture::sUseTextureAtlas = !LLViewerTexture::sUseTextureAtlas; - gSavedSettings.setBOOL("EnableTextureAtlas", LLViewerTexture::sUseTextureAtlas) ; - return true; - } -}; - -class LLAdvancedCheckTextureAtlas : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLViewerTexture::sUseTextureAtlas; // <-- make this using LLCacheControl - return new_value; - } -}; - -////////////////////////// -// DUMP SCRIPTED CAMERA // -////////////////////////// - -class LLAdvancedDumpScriptedCamera : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_dump_followcam(NULL); - return true; -} -}; - - - -////////////////////////////// -// DUMP REGION OBJECT CACHE // -////////////////////////////// - - -class LLAdvancedDumpRegionObjectCache : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) -{ - handle_dump_region_object_cache(NULL); - return true; - } -}; - -class LLAdvancedBuyCurrencyTest : public view_listener_t - { - bool handleEvent(const LLSD& userdata) - { - handle_buy_currency_test(NULL); - return true; - } -}; - - -///////////////////// -// DUMP SELECT MGR // -///////////////////// - - -class LLAdvancedDumpSelectMgr : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - dump_select_mgr(NULL); - return true; - } -}; - - - -//////////////////// -// DUMP INVENTORY // -//////////////////// - - -class LLAdvancedDumpInventory : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - dump_inventory(NULL); - return true; - } -}; - - - -//////////////////////////////// -// PRINT SELECTED OBJECT INFO // -//////////////////////////////// - - -class LLAdvancedPrintSelectedObjectInfo : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - print_object_info(NULL); - return true; - } -}; - - - -////////////////////// -// PRINT AGENT INFO // -////////////////////// - - -class LLAdvancedPrintAgentInfo : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - print_agent_nvpairs(NULL); - return true; - } -}; - - - -//////////////////////////////// -// PRINT TEXTURE MEMORY STATS // -//////////////////////////////// - - -class LLAdvancedPrintTextureMemoryStats : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - output_statistics(NULL); - return true; - } -}; - -////////////////// -// DEBUG CLICKS // -////////////////// - - -class LLAdvancedToggleDebugClicks : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gDebugClicks = !(gDebugClicks); - return true; - } -}; - -class LLAdvancedCheckDebugClicks : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = gDebugClicks; - return new_value; - } -}; - - - -///////////////// -// DEBUG VIEWS // -///////////////// - - -class LLAdvancedToggleDebugViews : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLView::sDebugRects = !(LLView::sDebugRects); - return true; - } -}; - -class LLAdvancedCheckDebugViews : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLView::sDebugRects; - return new_value; - } -}; - - - -/////////////////////// -// XUI NAME TOOLTIPS // -/////////////////////// - - -class LLAdvancedToggleXUINameTooltips : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - toggle_show_xui_names(NULL); - return true; - } -}; - -class LLAdvancedCheckXUINameTooltips : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = check_show_xui_names(NULL); - return new_value; - } -}; - - - -//////////////////////// -// DEBUG MOUSE EVENTS // -//////////////////////// - - -class LLAdvancedToggleDebugMouseEvents : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLView::sDebugMouseHandling = !(LLView::sDebugMouseHandling); - return true; - } -}; - -class LLAdvancedCheckDebugMouseEvents : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLView::sDebugMouseHandling; - return new_value; - } -}; - - - -//////////////// -// DEBUG KEYS // -//////////////// - - -class LLAdvancedToggleDebugKeys : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLView::sDebugKeys = !(LLView::sDebugKeys); - return true; - } -}; - -class LLAdvancedCheckDebugKeys : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLView::sDebugKeys; - return new_value; - } -}; - - - -/////////////////////// -// DEBUG WINDOW PROC // -/////////////////////// - - -class LLAdvancedToggleDebugWindowProc : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gDebugWindowProc = !(gDebugWindowProc); - return true; - } -}; - -class LLAdvancedCheckDebugWindowProc : public view_listener_t - { - bool handleEvent(const LLSD& userdata) - { - bool new_value = gDebugWindowProc; - return new_value; - } -}; - -// ------------------------------XUI MENU --------------------------- - -class LLAdvancedSendTestIms : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLIMModel::instance().testMessages(); - return true; -} -}; - - -/////////////// -// XUI NAMES // -/////////////// - - -class LLAdvancedToggleXUINames : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - toggle_show_xui_names(NULL); - return true; - } -}; - -class LLAdvancedCheckXUINames : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = check_show_xui_names(NULL); - return new_value; - } -}; - - -//////////////////////// -// GRAB BAKED TEXTURE // -//////////////////////// - - -class LLAdvancedGrabBakedTexture : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string texture_type = userdata.asString(); - if ("iris" == texture_type) - { - handle_grab_baked_texture( (void*)BAKED_EYES ); - } - else if ("head" == texture_type) - { - handle_grab_baked_texture( (void*)BAKED_HEAD ); - } - else if ("upper" == texture_type) - { - handle_grab_baked_texture( (void*)BAKED_UPPER ); - } - else if ("lower" == texture_type) - { - handle_grab_baked_texture( (void*)BAKED_LOWER ); - } - else if ("skirt" == texture_type) - { - handle_grab_baked_texture( (void*)BAKED_SKIRT ); - } - else if ("hair" == texture_type) - { - handle_grab_baked_texture( (void*)BAKED_HAIR ); - } - - return true; - } -}; - -class LLAdvancedEnableGrabBakedTexture : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) -{ - std::string texture_type = userdata.asString(); - bool new_value = false; - - if ("iris" == texture_type) - { - new_value = enable_grab_baked_texture( (void*)BAKED_EYES ); - } - else if ("head" == texture_type) - { - new_value = enable_grab_baked_texture( (void*)BAKED_HEAD ); - } - else if ("upper" == texture_type) - { - new_value = enable_grab_baked_texture( (void*)BAKED_UPPER ); - } - else if ("lower" == texture_type) - { - new_value = enable_grab_baked_texture( (void*)BAKED_LOWER ); - } - else if ("skirt" == texture_type) - { - new_value = enable_grab_baked_texture( (void*)BAKED_SKIRT ); - } - else if ("hair" == texture_type) - { - new_value = enable_grab_baked_texture( (void*)BAKED_HAIR ); - } - - return new_value; -} -}; - -/////////////////////// -// APPEARANCE TO XML // -/////////////////////// - - -class LLAdvancedAppearanceToXML : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLVOAvatar::dumpArchetypeXML(NULL); - return true; - } -}; - - - -/////////////////////////////// -// TOGGLE CHARACTER GEOMETRY // -/////////////////////////////// - - -class LLAdvancedToggleCharacterGeometry : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_god_request_avatar_geometry(NULL); - return true; -} -}; - - - ///////////////////////////// -// TEST MALE / TEST FEMALE // -///////////////////////////// - -class LLAdvancedTestMale : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_test_male(NULL); - return true; - } -}; - - -class LLAdvancedTestFemale : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_test_female(NULL); - return true; - } -}; - - - -/////////////// -// TOGGLE PG // -/////////////// - - -class LLAdvancedTogglePG : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_toggle_pg(NULL); - return true; - } -}; - - -class LLAdvancedForceParamsToDefault : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLAgent::clearVisualParams(NULL); - return true; - } -}; - - - -////////////////////////// -// RELOAD VERTEX SHADER // -////////////////////////// - - -class LLAdvancedReloadVertexShader : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - reload_vertex_shader(NULL); - return true; - } -}; - - - -//////////////////// -// ANIMATION INFO // -//////////////////// - - -class LLAdvancedToggleAnimationInfo : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLVOAvatar::sShowAnimationDebug = !(LLVOAvatar::sShowAnimationDebug); - return true; - } -}; - -class LLAdvancedCheckAnimationInfo : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLVOAvatar::sShowAnimationDebug; - return new_value; - } -}; - - -////////////////// -// SHOW LOOK AT // -////////////////// - - -class LLAdvancedToggleShowLookAt : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLHUDEffectLookAt::sDebugLookAt = !(LLHUDEffectLookAt::sDebugLookAt); - return true; - } -}; - -class LLAdvancedCheckShowLookAt : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLHUDEffectLookAt::sDebugLookAt; - return new_value; - } -}; - - - -/////////////////// -// SHOW POINT AT // -/////////////////// - - -class LLAdvancedToggleShowPointAt : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLHUDEffectPointAt::sDebugPointAt = !(LLHUDEffectPointAt::sDebugPointAt); - return true; - } -}; - -class LLAdvancedCheckShowPointAt : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLHUDEffectPointAt::sDebugPointAt; - return new_value; - } -}; - - - -///////////////////////// -// DEBUG JOINT UPDATES // -///////////////////////// - - -class LLAdvancedToggleDebugJointUpdates : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLVOAvatar::sJointDebug = !(LLVOAvatar::sJointDebug); - return true; - } -}; - -class LLAdvancedCheckDebugJointUpdates : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLVOAvatar::sJointDebug; - return new_value; - } -}; - - - -///////////////// -// DISABLE LOD // -///////////////// - - -class LLAdvancedToggleDisableLOD : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLViewerJoint::sDisableLOD = !(LLViewerJoint::sDisableLOD); - return true; - } -}; - -class LLAdvancedCheckDisableLOD : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLViewerJoint::sDisableLOD; - return new_value; - } -}; - - - -///////////////////////// -// DEBUG CHARACTER VIS // -///////////////////////// - - -class LLAdvancedToggleDebugCharacterVis : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLVOAvatar::sDebugInvisible = !(LLVOAvatar::sDebugInvisible); - return true; - } -}; - -class LLAdvancedCheckDebugCharacterVis : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLVOAvatar::sDebugInvisible; - return new_value; - } -}; - - -////////////////////// -// DUMP ATTACHMENTS // -////////////////////// - - -class LLAdvancedDumpAttachments : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_dump_attachments(NULL); - return true; - } -}; - - - -///////////////////// -// REBAKE TEXTURES // -///////////////////// - - -class LLAdvancedRebakeTextures : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_rebake_textures(NULL); - return true; - } -}; - - -#if 1 //ndef LL_RELEASE_FOR_DOWNLOAD -/////////////////////////// -// DEBUG AVATAR TEXTURES // -/////////////////////////// - - -class LLAdvancedDebugAvatarTextures : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if (gAgent.isGodlike()) - { - handle_debug_avatar_textures(NULL); - } - return true; - } -}; - -//////////////////////////////// -// DUMP AVATAR LOCAL TEXTURES // -//////////////////////////////// - - -class LLAdvancedDumpAvatarLocalTextures : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { -#ifndef LL_RELEASE_FOR_DOWNLOAD - handle_dump_avatar_local_textures(NULL); -#endif - return true; - } -}; - -#endif - -///////////////// -// MESSAGE LOG // -///////////////// - - -class LLAdvancedEnableMessageLog : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_viewer_enable_message_log(NULL); - return true; - } -}; - -class LLAdvancedDisableMessageLog : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_viewer_disable_message_log(NULL); - return true; - } -}; - -///////////////// -// DROP PACKET // -///////////////// - - -class LLAdvancedDropPacket : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gMessageSystem->mPacketRing.dropPackets(1); - return true; - } -}; - - - -///////////////// -// AGENT PILOT // -///////////////// - - -class LLAdvancedAgentPilot : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string command = userdata.asString(); - if ("start playback" == command) - { - LLAgentPilot::startPlayback(NULL); - } - else if ("stop playback" == command) - { - LLAgentPilot::stopPlayback(NULL); - } - else if ("start record" == command) - { - LLAgentPilot::startRecord(NULL); - } - else if ("stop record" == command) - { - LLAgentPilot::saveRecord(NULL); - } - - return true; - } -}; - - - -////////////////////// -// AGENT PILOT LOOP // -////////////////////// - - -class LLAdvancedToggleAgentPilotLoop : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLAgentPilot::sLoop = !(LLAgentPilot::sLoop); - return true; - } -}; - -class LLAdvancedCheckAgentPilotLoop : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLAgentPilot::sLoop; - return new_value; - } -}; - - -///////////////////////// -// SHOW OBJECT UPDATES // -///////////////////////// - - -class LLAdvancedToggleShowObjectUpdates : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gShowObjectUpdates = !(gShowObjectUpdates); - return true; - } -}; - -class LLAdvancedCheckShowObjectUpdates : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = gShowObjectUpdates; - return new_value; - } -}; - - - -//////////////////// -// COMPRESS IMAGE // -//////////////////// - - -class LLAdvancedCompressImage : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_compress_image(NULL); - return true; - } -}; - - - -///////////////////////// -// SHOW DEBUG SETTINGS // -///////////////////////// - - -class LLAdvancedShowDebugSettings : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLFloaterReg::showInstance("settings_debug",userdata); - return true; - } -}; - - - -//////////////////////// -// VIEW ADMIN OPTIONS // -//////////////////////// - -class LLAdvancedEnableViewAdminOptions : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - // Don't enable in god mode since the admin menu is shown anyway. - // Only enable if the user has set the appropriate debug setting. - bool new_value = !gAgent.getAgentAccess().isGodlikeWithoutAdminMenuFakery() && gSavedSettings.getBOOL("AdminMenu"); - return new_value; - } -}; - -class LLAdvancedToggleViewAdminOptions : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_admin_override_toggle(NULL); - return true; - } -}; - -class LLAdvancedCheckViewAdminOptions : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = check_admin_override(NULL) || gAgent.isGodlike(); - return new_value; - } -}; - -///////////////////////////////////// -// Enable Object Object Occlusion /// -///////////////////////////////////// -class LLAdvancedEnableObjectObjectOcclusion: public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - - bool new_value = gGLManager.mHasOcclusionQuery; // && LLFeatureManager::getInstance()->isFeatureAvailable(userdata.asString()); - return new_value; -} -}; - -///////////////////////////////////// -// Enable Framebuffer Objects /// -///////////////////////////////////// -class LLAdvancedEnableRenderFBO: public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = gGLManager.mHasFramebufferObject; - return new_value; - } -}; - -///////////////////////////////////// -// Enable Deferred Rendering /// -///////////////////////////////////// -class LLAdvancedEnableRenderDeferred: public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = gSavedSettings.getBOOL("RenderUseFBO") && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_WINDLIGHT > 0) && - LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) > 0; - return new_value; - } -}; - -///////////////////////////////////// -// Enable Deferred Rendering sub-options -///////////////////////////////////// -class LLAdvancedEnableRenderDeferredOptions: public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = gSavedSettings.getBOOL("RenderUseFBO") && gSavedSettings.getBOOL("RenderDeferred"); - return new_value; - } -}; - - - -////////////////// -// ADMIN STATUS // -////////////////// - - -class LLAdvancedRequestAdminStatus : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_god_mode(NULL); - return true; - } -}; - -class LLAdvancedLeaveAdminStatus : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_leave_god_mode(NULL); - return true; - } -}; - -////////////////////////// -// Advanced > Debugging // -////////////////////////// - - -class LLAdvancedForceErrorBreakpoint : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - force_error_breakpoint(NULL); - return true; - } -}; - -class LLAdvancedForceErrorLlerror : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - force_error_llerror(NULL); - return true; - } -}; -class LLAdvancedForceErrorBadMemoryAccess : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - force_error_bad_memory_access(NULL); - return true; - } -}; - -class LLAdvancedForceErrorInfiniteLoop : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - force_error_infinite_loop(NULL); - return true; - } -}; - -class LLAdvancedForceErrorSoftwareException : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - force_error_software_exception(NULL); - return true; - } -}; - -class LLAdvancedForceErrorDriverCrash : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - force_error_driver_crash(NULL); - return true; - } -}; - -class LLAdvancedForceErrorDisconnectViewer : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_disconnect_viewer(NULL); - return true; -} -}; - - -#ifdef TOGGLE_HACKED_GODLIKE_VIEWER - -class LLAdvancedHandleToggleHackedGodmode : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_toggle_hacked_godmode(NULL); - return true; - } -}; - -class LLAdvancedCheckToggleHackedGodmode : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - check_toggle_hacked_godmode(NULL); - return true; - } -}; - -class LLAdvancedEnableToggleHackedGodmode : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = enable_toggle_hacked_godmode(NULL); - return new_value; - } -}; -#endif - - -// -////------------------------------------------------------------------- -//// Advanced menu -////------------------------------------------------------------------- - -////////////////// -// ADMIN MENU // -////////////////// - -// Admin > Object -class LLAdminForceTakeCopy : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - force_take_copy(NULL); - return true; - } -}; - -class LLAdminHandleObjectOwnerSelf : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_object_owner_self(NULL); - return true; - } -}; -class LLAdminHandleObjectOwnerPermissive : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_object_owner_permissive(NULL); - return true; - } -}; - -class LLAdminHandleForceDelete : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_force_delete(NULL); - return true; - } -}; - -class LLAdminHandleObjectLock : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_object_lock(NULL); - return true; - } -}; - -class LLAdminHandleObjectAssetIDs: public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_object_asset_ids(NULL); - return true; - } -}; - -//Admin >Parcel -class LLAdminHandleForceParcelOwnerToMe: public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_force_parcel_owner_to_me(NULL); - return true; - } -}; -class LLAdminHandleForceParcelToContent: public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_force_parcel_to_content(NULL); - return true; - } -}; -class LLAdminHandleClaimPublicLand: public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_claim_public_land(NULL); - return true; - } -}; - -// Admin > Region -class LLAdminHandleRegionDumpTempAssetData: public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_region_dump_temp_asset_data(NULL); - return true; - } -}; -//Admin (Top Level) - -class LLAdminOnSaveState: public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLPanelRegionTools::onSaveState(NULL); - return true; -} -}; - - -//----------------------------------------------------------------------------- -// cleanup_menus() -//----------------------------------------------------------------------------- -void cleanup_menus() -{ - delete gMenuParcelObserver; - gMenuParcelObserver = NULL; - - delete gMenuAvatarSelf; - gMenuAvatarSelf = NULL; - - delete gMenuAvatarOther; - gMenuAvatarOther = NULL; - - delete gMenuObject; - gMenuObject = NULL; - - delete gMenuAttachmentSelf; - gMenuAttachmentSelf = NULL; - - delete gMenuAttachmentOther; - gMenuAttachmentSelf = NULL; - - delete gMenuLand; - gMenuLand = NULL; - - delete gMenuBarView; - gMenuBarView = NULL; - - delete gPopupMenuView; - gPopupMenuView = NULL; - - delete gMenuHolder; - gMenuHolder = NULL; -} - -//----------------------------------------------------------------------------- -// Object pie menu -//----------------------------------------------------------------------------- - -class LLObjectReportAbuse : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if (objectp) - { - LLFloaterReporter::showFromObject(objectp->getID()); - } - return true; - } -}; - -// Enabled it you clicked an object -class LLObjectEnableReportAbuse : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLSelectMgr::getInstance()->getSelection()->getObjectCount() != 0; - return new_value; - } -}; - -void handle_object_touch() -{ - LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if (!object) return; - - LLPickInfo pick = LLToolPie::getInstance()->getPick(); - - LLMessageSystem *msg = gMessageSystem; - - msg->newMessageFast(_PREHASH_ObjectGrab); - msg->nextBlockFast( _PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast( _PREHASH_ObjectData); - msg->addU32Fast( _PREHASH_LocalID, object->mLocalID); - msg->addVector3Fast(_PREHASH_GrabOffset, LLVector3::zero ); - msg->nextBlock("SurfaceInfo"); - msg->addVector3("UVCoord", LLVector3(pick.mUVCoords)); - msg->addVector3("STCoord", LLVector3(pick.mSTCoords)); - msg->addS32Fast(_PREHASH_FaceIndex, pick.mObjectFace); - msg->addVector3("Position", pick.mIntersection); - msg->addVector3("Normal", pick.mNormal); - msg->addVector3("Binormal", pick.mBinormal); - msg->sendMessage( object->getRegion()->getHost()); - - // *NOTE: Hope the packets arrive safely and in order or else - // there will be some problems. - // *TODO: Just fix this bad assumption. - msg->newMessageFast(_PREHASH_ObjectDeGrab); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_ObjectData); - msg->addU32Fast(_PREHASH_LocalID, object->mLocalID); - msg->nextBlock("SurfaceInfo"); - msg->addVector3("UVCoord", LLVector3(pick.mUVCoords)); - msg->addVector3("STCoord", LLVector3(pick.mSTCoords)); - msg->addS32Fast(_PREHASH_FaceIndex, pick.mObjectFace); - msg->addVector3("Position", pick.mIntersection); - msg->addVector3("Normal", pick.mNormal); - msg->addVector3("Binormal", pick.mBinormal); - msg->sendMessage(object->getRegion()->getHost()); -} - -static void init_default_item_label(const std::string& item_name) -{ - boost::unordered_map::iterator it = sDefaultItemLabels.find(item_name); - if (it == sDefaultItemLabels.end()) - { - // *NOTE: This will not work for items of type LLMenuItemCheckGL because they return boolean value - // (doesn't seem to matter much ATM). - LLStringExplicit default_label = gMenuHolder->childGetValue(item_name).asString(); - if (!default_label.empty()) - { - sDefaultItemLabels.insert(std::pair(item_name, default_label)); - } - } -} - -static LLStringExplicit get_default_item_label(const std::string& item_name) -{ - LLStringExplicit res(""); - boost::unordered_map::iterator it = sDefaultItemLabels.find(item_name); - if (it != sDefaultItemLabels.end()) - { - res = it->second; - } - - return res; -} - - -bool enable_object_touch(LLUICtrl* ctrl) -{ - LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - - bool new_value = obj && obj->flagHandleTouch(); - - std::string item_name = ctrl->getName(); - init_default_item_label(item_name); - - // Update label based on the node touch name if available. - LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); - if (node && node->mValid && !node->mTouchName.empty()) - { - gMenuHolder->childSetText(item_name, node->mTouchName); - } - else - { - gMenuHolder->childSetText(item_name, get_default_item_label(item_name)); - } - - return new_value; -}; - -//void label_touch(std::string& label, void*) -//{ -// LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); -// if (node && node->mValid && !node->mTouchName.empty()) -// { -// label.assign(node->mTouchName); -// } -// else -// { -// label.assign("Touch"); -// } -//} - -void handle_object_open() -{ - LLFloaterReg::showInstance("openobject"); -} - -bool enable_object_open() -{ - // Look for contents in root object, which is all the LLFloaterOpenObject - // understands. - LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if (!obj) return false; - - LLViewerObject* root = obj->getRootEdit(); - if (!root) return false; - - return root->allowOpen(); -} - - -class LLViewJoystickFlycam : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_toggle_flycam(); - return true; - } -}; - -class LLViewCheckJoystickFlycam : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLViewerJoystick::getInstance()->getOverrideCamera(); - return new_value; - } -}; - -void handle_toggle_flycam() -{ - LLViewerJoystick::getInstance()->toggleFlycam(); -} - -class LLObjectBuild : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if (gAgentCamera.getFocusOnAvatar() && !LLToolMgr::getInstance()->inEdit() && gSavedSettings.getBOOL("EditCameraMovement") ) - { - // zoom in if we're looking at the avatar - gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); - gAgentCamera.setFocusGlobal(LLToolPie::getInstance()->getPick()); - gAgentCamera.cameraZoomIn(0.666f); - gAgentCamera.cameraOrbitOver( 30.f * DEG_TO_RAD ); - gViewerWindow->moveCursorToCenter(); - } - else if ( gSavedSettings.getBOOL("EditCameraMovement") ) - { - gAgentCamera.setFocusGlobal(LLToolPie::getInstance()->getPick()); - gViewerWindow->moveCursorToCenter(); - } - - LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); - LLToolMgr::getInstance()->getCurrentToolset()->selectTool( LLToolCompCreate::getInstance() ); - - // Could be first use - //LLFirstUse::useBuild(); - return true; - } -}; - - -void handle_object_edit() -{ - LLViewerParcelMgr::getInstance()->deselectLand(); - - if (gAgentCamera.getFocusOnAvatar() && !LLToolMgr::getInstance()->inEdit()) - { - LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); - - if (selection->getSelectType() == SELECT_TYPE_HUD || !gSavedSettings.getBOOL("EditCameraMovement")) - { - // always freeze camera in space, even if camera doesn't move - // so, for example, follow cam scripts can't affect you when in build mode - gAgentCamera.setFocusGlobal(gAgentCamera.calcFocusPositionTargetGlobal(), LLUUID::null); - gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); - } - else - { - gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); - LLViewerObject* selected_objectp = selection->getFirstRootObject(); - if (selected_objectp) - { - // zoom in on object center instead of where we clicked, as we need to see the manipulator handles - gAgentCamera.setFocusGlobal(selected_objectp->getPositionGlobal(), selected_objectp->getID()); - gAgentCamera.cameraZoomIn(0.666f); - gAgentCamera.cameraOrbitOver( 30.f * DEG_TO_RAD ); - gViewerWindow->moveCursorToCenter(); - } - } - } - - LLFloaterReg::showInstance("build"); - - LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); - gFloaterTools->setEditTool( LLToolCompTranslate::getInstance() ); - - LLViewerJoystick::getInstance()->moveObjects(true); - LLViewerJoystick::getInstance()->setNeedsReset(true); - - // Could be first use - //LLFirstUse::useBuild(); - return; -} - -void handle_object_inspect() -{ - LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); - LLViewerObject* selected_objectp = selection->getFirstRootObject(); - if (selected_objectp) - { - LLSD key; - key["task"] = "task"; - LLSideTray::getInstance()->showPanel("sidepanel_inventory", key); - } - - /* - // Old floater properties - LLFloaterReg::showInstance("inspect", LLSD()); - */ -} - -//--------------------------------------------------------------------------- -// Land pie menu -//--------------------------------------------------------------------------- -class LLLandBuild : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLViewerParcelMgr::getInstance()->deselectLand(); - - if (gAgentCamera.getFocusOnAvatar() && !LLToolMgr::getInstance()->inEdit() && gSavedSettings.getBOOL("EditCameraMovement") ) - { - // zoom in if we're looking at the avatar - gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); - gAgentCamera.setFocusGlobal(LLToolPie::getInstance()->getPick()); - gAgentCamera.cameraZoomIn(0.666f); - gAgentCamera.cameraOrbitOver( 30.f * DEG_TO_RAD ); - gViewerWindow->moveCursorToCenter(); - } - else if ( gSavedSettings.getBOOL("EditCameraMovement") ) - { - // otherwise just move focus - gAgentCamera.setFocusGlobal(LLToolPie::getInstance()->getPick()); - gViewerWindow->moveCursorToCenter(); - } - - - LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); - LLToolMgr::getInstance()->getCurrentToolset()->selectTool( LLToolCompCreate::getInstance() ); - - // Could be first use - //LLFirstUse::useBuild(); - return true; - } -}; - -class LLLandBuyPass : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLPanelLandGeneral::onClickBuyPass((void *)FALSE); - return true; - } -}; - -class LLLandEnableBuyPass : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLPanelLandGeneral::enableBuyPass(NULL); - return new_value; - } -}; - -// BUG: Should really check if CLICK POINT is in a parcel where you can build. -BOOL enable_land_build(void*) -{ - if (gAgent.isGodlike()) return TRUE; - if (gAgent.inPrelude()) return FALSE; - - BOOL can_build = FALSE; - LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (agent_parcel) - { - can_build = agent_parcel->getAllowModify(); - } - return can_build; -} - -// BUG: Should really check if OBJECT is in a parcel where you can build. -BOOL enable_object_build(void*) -{ - if (gAgent.isGodlike()) return TRUE; - if (gAgent.inPrelude()) return FALSE; - - BOOL can_build = FALSE; - LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (agent_parcel) - { - can_build = agent_parcel->getAllowModify(); - } - return can_build; -} - -bool enable_object_edit() -{ - // *HACK: The new "prelude" Help Islands have a build sandbox area, - // so users need the Edit and Create pie menu options when they are - // there. Eventually this needs to be replaced with code that only - // lets you edit objects if you have permission to do so (edit perms, - // group edit, god). See also lltoolbar.cpp. JC - bool enable = false; - if (gAgent.inPrelude()) - { - enable = LLViewerParcelMgr::getInstance()->allowAgentBuild() - || LLSelectMgr::getInstance()->getSelection()->isAttachment(); - } - else if (LLSelectMgr::getInstance()->selectGetAllValidAndObjectsFound()) - { - enable = true; - } - - return enable; -} - -// mutually exclusive - show either edit option or build in menu -bool enable_object_build() -{ - return !enable_object_edit(); -} - -class LLSelfRemoveAllAttachments : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLAgentWearables::userRemoveAllAttachments(); - return true; - } -}; - -class LLSelfEnableRemoveAllAttachments : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = false; - if (isAgentAvatarValid()) - { - for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); - iter != gAgentAvatarp->mAttachmentPoints.end(); ) - { - LLVOAvatar::attachment_map_t::iterator curiter = iter++; - LLViewerJointAttachment* attachment = curiter->second; - if (attachment->getNumObjects() > 0) - { - new_value = true; - break; - } - } - } - return new_value; - } -}; - -BOOL enable_has_attachments(void*) -{ - - return FALSE; -} - -//--------------------------------------------------------------------------- -// Avatar pie menu -//--------------------------------------------------------------------------- -//void handle_follow(void *userdata) -//{ -// // follow a given avatar by ID -// LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); -// if (objectp) -// { -// gAgent.startFollowPilot(objectp->getID()); -// } -//} - -bool enable_object_mute() -{ - LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if (!object) return false; - - LLVOAvatar* avatar = find_avatar_from_object(object); - if (avatar) - { - // It's an avatar - LLNameValue *lastname = avatar->getNVPair("LastName"); - bool is_linden = - lastname && !LLStringUtil::compareStrings(lastname->getString(), "Linden"); - bool is_self = avatar->isSelf(); - return !is_linden && !is_self; - } - else - { - // Just a regular object - return LLSelectMgr::getInstance()->getSelection()-> - contains( object, SELECT_ALL_TES ); - } -} - -class LLObjectMute : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if (!object) return true; - - LLUUID id; - std::string name; - LLMute::EType type; - LLVOAvatar* avatar = find_avatar_from_object(object); - if (avatar) - { - id = avatar->getID(); - - LLNameValue *firstname = avatar->getNVPair("FirstName"); - LLNameValue *lastname = avatar->getNVPair("LastName"); - if (firstname && lastname) - { - name = LLCacheName::buildFullName( - firstname->getString(), lastname->getString()); - } - - type = LLMute::AGENT; - } - else - { - // it's an object - id = object->getID(); - - LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); - if (node) - { - name = node->mName; - } - - type = LLMute::OBJECT; - } - - LLMute mute(id, name, type); - if (LLMuteList::getInstance()->isMuted(mute.mID, mute.mName)) - { - LLMuteList::getInstance()->remove(mute); - } - else - { - LLMuteList::getInstance()->add(mute); - LLPanelBlockedList::showPanelAndSelect(mute.mID); - } - - return true; - } -}; - -bool handle_go_to() -{ - // try simulator autopilot - std::vector strings; - std::string val; - LLVector3d pos = LLToolPie::getInstance()->getPick().mPosGlobal; - val = llformat("%g", pos.mdV[VX]); - strings.push_back(val); - val = llformat("%g", pos.mdV[VY]); - strings.push_back(val); - val = llformat("%g", pos.mdV[VZ]); - strings.push_back(val); - send_generic_message("autopilot", strings); - - LLViewerParcelMgr::getInstance()->deselectLand(); - - if (isAgentAvatarValid() && !gSavedSettings.getBOOL("AutoPilotLocksCamera")) - { - gAgentCamera.setFocusGlobal(gAgentCamera.getFocusTargetGlobal(), gAgentAvatarp->getID()); - } - else - { - // Snap camera back to behind avatar - gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); - } - - // Could be first use - //LLFirstUse::useGoTo(); - return true; -} - -class LLGoToObject : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - return handle_go_to(); - } -}; - -class LLAvatarReportAbuse : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); - if(avatar) - { - LLFloaterReporter::showFromObject(avatar->getID()); - } - return true; - } -}; - - -//--------------------------------------------------------------------------- -// Parcel freeze, eject, etc. -//--------------------------------------------------------------------------- -bool callback_freeze(const LLSD& notification, const LLSD& response) -{ - LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - - if (0 == option || 1 == option) - { - U32 flags = 0x0; - if (1 == option) - { - // unfreeze - flags |= 0x1; - } - - LLMessageSystem* msg = gMessageSystem; - LLViewerObject* avatar = gObjectList.findObject(avatar_id); - - if (avatar) - { - msg->newMessage("FreezeUser"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->nextBlock("Data"); - msg->addUUID("TargetID", avatar_id ); - msg->addU32("Flags", flags ); - msg->sendReliable( avatar->getRegion()->getHost() ); - } - } - return false; -} - - -void handle_avatar_freeze(const LLSD& avatar_id) -{ - // Use avatar_id if available, otherwise default to right-click avatar - LLVOAvatar* avatar = NULL; - if (avatar_id.asUUID().notNull()) - { - avatar = find_avatar_from_object(avatar_id.asUUID()); - } - else - { - avatar = find_avatar_from_object( - LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); - } - - if( avatar ) - { - std::string fullname = avatar->getFullname(); - LLSD payload; - payload["avatar_id"] = avatar->getID(); - - if (!fullname.empty()) - { - LLSD args; - args["AVATAR_NAME"] = fullname; - LLNotificationsUtil::add("FreezeAvatarFullname", - args, - payload, - callback_freeze); - } - else - { - LLNotificationsUtil::add("FreezeAvatar", - LLSD(), - payload, - callback_freeze); - } - } -} - -class LLAvatarVisibleDebug : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - return gAgent.isGodlike(); - } -}; - -class LLAvatarDebug : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); - if( avatar ) - { - if (avatar->isSelf()) - { - ((LLVOAvatarSelf *)avatar)->dumpLocalTextures(); - } - llinfos << "Dumping temporary asset data to simulator logs for avatar " << avatar->getID() << llendl; - std::vector strings; - strings.push_back(avatar->getID().asString()); - LLUUID invoice; - send_generic_message("dumptempassetdata", strings, invoice); - LLFloaterReg::showInstance( "avatar_textures", LLSD(avatar->getID()) ); - } - return true; - } -}; - -bool callback_eject(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (2 == option) - { - // Cancel button. - return false; - } - LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); - bool ban_enabled = notification["payload"]["ban_enabled"].asBoolean(); - - if (0 == option) - { - // Eject button - LLMessageSystem* msg = gMessageSystem; - LLViewerObject* avatar = gObjectList.findObject(avatar_id); - - if (avatar) - { - U32 flags = 0x0; - msg->newMessage("EjectUser"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID() ); - msg->addUUID("SessionID", gAgent.getSessionID() ); - msg->nextBlock("Data"); - msg->addUUID("TargetID", avatar_id ); - msg->addU32("Flags", flags ); - msg->sendReliable( avatar->getRegion()->getHost() ); - } - } - else if (ban_enabled) - { - // This is tricky. It is similar to say if it is not an 'Eject' button, - // and it is also not an 'Cancle' button, and ban_enabled==ture, - // it should be the 'Eject and Ban' button. - LLMessageSystem* msg = gMessageSystem; - LLViewerObject* avatar = gObjectList.findObject(avatar_id); - - if (avatar) - { - U32 flags = 0x1; - msg->newMessage("EjectUser"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID() ); - msg->addUUID("SessionID", gAgent.getSessionID() ); - msg->nextBlock("Data"); - msg->addUUID("TargetID", avatar_id ); - msg->addU32("Flags", flags ); - msg->sendReliable( avatar->getRegion()->getHost() ); - } - } - return false; -} - -void handle_avatar_eject(const LLSD& avatar_id) -{ - // Use avatar_id if available, otherwise default to right-click avatar - LLVOAvatar* avatar = NULL; - if (avatar_id.asUUID().notNull()) - { - avatar = find_avatar_from_object(avatar_id.asUUID()); - } - else - { - avatar = find_avatar_from_object( - LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); - } - - if( avatar ) - { - LLSD payload; - payload["avatar_id"] = avatar->getID(); - std::string fullname = avatar->getFullname(); - - const LLVector3d& pos = avatar->getPositionGlobal(); - LLParcel* parcel = LLViewerParcelMgr::getInstance()->selectParcelAt(pos)->getParcel(); - - if (LLViewerParcelMgr::getInstance()->isParcelOwnedByAgent(parcel,GP_LAND_MANAGE_BANNED)) - { - payload["ban_enabled"] = true; - if (!fullname.empty()) - { - LLSD args; - args["AVATAR_NAME"] = fullname; - LLNotificationsUtil::add("EjectAvatarFullname", - args, - payload, - callback_eject); - } - else - { - LLNotificationsUtil::add("EjectAvatarFullname", - LLSD(), - payload, - callback_eject); - } - } - else - { - payload["ban_enabled"] = false; - if (!fullname.empty()) - { - LLSD args; - args["AVATAR_NAME"] = fullname; - LLNotificationsUtil::add("EjectAvatarFullnameNoBan", - args, - payload, - callback_eject); - } - else - { - LLNotificationsUtil::add("EjectAvatarNoBan", - LLSD(), - payload, - callback_eject); - } - } - } -} - -bool enable_freeze_eject(const LLSD& avatar_id) -{ - // Use avatar_id if available, otherwise default to right-click avatar - LLVOAvatar* avatar = NULL; - if (avatar_id.asUUID().notNull()) - { - avatar = find_avatar_from_object(avatar_id.asUUID()); - } - else - { - avatar = find_avatar_from_object( - LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); - } - if (!avatar) return false; - - // Gods can always freeze - if (gAgent.isGodlike()) return true; - - // Estate owners / managers can freeze - // Parcel owners can also freeze - const LLVector3& pos = avatar->getPositionRegion(); - const LLVector3d& pos_global = avatar->getPositionGlobal(); - LLParcel* parcel = LLViewerParcelMgr::getInstance()->selectParcelAt(pos_global)->getParcel(); - LLViewerRegion* region = avatar->getRegion(); - if (!region) return false; - - bool new_value = region->isOwnedSelf(pos); - if (!new_value || region->isOwnedGroup(pos)) - { - new_value = LLViewerParcelMgr::getInstance()->isParcelOwnedByAgent(parcel,GP_LAND_ADMIN); - } - return new_value; -} - - -void login_done(S32 which, void *user) -{ - llinfos << "Login done " << which << llendl; - - LLPanelLogin::closePanel(); -} - - -bool callback_leave_group(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option == 0) - { - LLMessageSystem *msg = gMessageSystem; - - msg->newMessageFast(_PREHASH_LeaveGroupRequest); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_GroupData); - msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID() ); - gAgent.sendReliableMessage(); - } - return false; -} - -void append_aggregate(std::string& string, const LLAggregatePermissions& ag_perm, PermissionBit bit, const char* txt) -{ - LLAggregatePermissions::EValue val = ag_perm.getValue(bit); - std::string buffer; - switch(val) - { - case LLAggregatePermissions::AP_NONE: - buffer = llformat( "* %s None\n", txt); - break; - case LLAggregatePermissions::AP_SOME: - buffer = llformat( "* %s Some\n", txt); - break; - case LLAggregatePermissions::AP_ALL: - buffer = llformat( "* %s All\n", txt); - break; - case LLAggregatePermissions::AP_EMPTY: - default: - break; - } - string.append(buffer); -} - -bool enable_buy_object() -{ - // In order to buy, there must only be 1 purchaseable object in - // the selection manger. - if(LLSelectMgr::getInstance()->getSelection()->getRootObjectCount() != 1) return false; - LLViewerObject* obj = NULL; - LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); - if(node) - { - obj = node->getObject(); - if(!obj) return false; - - if( for_sale_selection(node) ) - { - // *NOTE: Is this needed? This checks to see if anyone owns the - // object, dating back to when we had "public" objects owned by - // no one. JC - if(obj->permAnyOwner()) return true; - } - } - return false; -} - -// Note: This will only work if the selected object's data has been -// received by the viewer and cached in the selection manager. -void handle_buy_object(LLSaleInfo sale_info) -{ - if(!LLSelectMgr::getInstance()->selectGetAllRootsValid()) - { - LLNotificationsUtil::add("UnableToBuyWhileDownloading"); - return; - } - - LLUUID owner_id; - std::string owner_name; - BOOL owners_identical = LLSelectMgr::getInstance()->selectGetOwner(owner_id, owner_name); - if (!owners_identical) - { - LLNotificationsUtil::add("CannotBuyObjectsFromDifferentOwners"); - return; - } - - LLPermissions perm; - BOOL valid = LLSelectMgr::getInstance()->selectGetPermissions(perm); - LLAggregatePermissions ag_perm; - valid &= LLSelectMgr::getInstance()->selectGetAggregatePermissions(ag_perm); - if(!valid || !sale_info.isForSale() || !perm.allowTransferTo(gAgent.getID())) - { - LLNotificationsUtil::add("ObjectNotForSale"); - return; - } - - LLFloaterBuy::show(sale_info); -} - - -void handle_buy_contents(LLSaleInfo sale_info) -{ - LLFloaterBuyContents::show(sale_info); -} - -void handle_region_dump_temp_asset_data(void*) -{ - llinfos << "Dumping temporary asset data to simulator logs" << llendl; - std::vector strings; - LLUUID invoice; - send_generic_message("dumptempassetdata", strings, invoice); -} - -void handle_region_clear_temp_asset_data(void*) -{ - llinfos << "Clearing temporary asset data" << llendl; - std::vector strings; - LLUUID invoice; - send_generic_message("cleartempassetdata", strings, invoice); -} - -void handle_region_dump_settings(void*) -{ - LLViewerRegion* regionp = gAgent.getRegion(); - if (regionp) - { - llinfos << "Damage: " << (regionp->getAllowDamage() ? "on" : "off") << llendl; - llinfos << "Landmark: " << (regionp->getAllowLandmark() ? "on" : "off") << llendl; - llinfos << "SetHome: " << (regionp->getAllowSetHome() ? "on" : "off") << llendl; - llinfos << "ResetHome: " << (regionp->getResetHomeOnTeleport() ? "on" : "off") << llendl; - llinfos << "SunFixed: " << (regionp->getSunFixed() ? "on" : "off") << llendl; - llinfos << "BlockFly: " << (regionp->getBlockFly() ? "on" : "off") << llendl; - llinfos << "AllowP2P: " << (regionp->getAllowDirectTeleport() ? "on" : "off") << llendl; - llinfos << "Water: " << (regionp->getWaterHeight()) << llendl; - } -} - -void handle_dump_group_info(void *) -{ - gAgent.dumpGroupInfo(); -} - -void handle_dump_capabilities_info(void *) -{ - LLViewerRegion* regionp = gAgent.getRegion(); - if (regionp) - { - regionp->logActiveCapabilities(); - } -} - -void handle_dump_region_object_cache(void*) -{ - LLViewerRegion* regionp = gAgent.getRegion(); - if (regionp) - { - regionp->dumpCache(); - } -} - -void handle_dump_focus() -{ - LLUICtrl *ctrl = dynamic_cast(gFocusMgr.getKeyboardFocus()); - - llinfos << "Keyboard focus " << (ctrl ? ctrl->getName() : "(none)") << llendl; -} - -class LLSelfStandUp : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gAgent.standUp(); - return true; - } -}; - -bool enable_standup_self() -{ - return isAgentAvatarValid() && gAgentAvatarp->isSitting(); -} - -class LLSelfSitDown : public view_listener_t - { - bool handleEvent(const LLSD& userdata) - { - gAgent.sitDown(); - return true; - } - }; - -bool enable_sitdown_self() -{ - return isAgentAvatarValid() && !gAgentAvatarp->isSitting() && !gAgent.getFlying(); -} - -// Used from the login screen to aid in UI work on side tray -void handle_show_side_tray() -{ - LLSideTray* side_tray = LLSideTray::getInstance(); - LLView* root = gViewerWindow->getRootView(); - // automatically removes and re-adds if there already - root->addChild(side_tray); -} - -// Toggle one of "People" panel tabs in side tray. -class LLTogglePanelPeopleTab : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string panel_name = userdata.asString(); - - LLSD param; - param["people_panel_tab_name"] = panel_name; - - static LLPanel* friends_panel = NULL; - static LLPanel* groups_panel = NULL; - static LLPanel* nearby_panel = NULL; - - if (panel_name == "friends_panel") - { - return togglePeoplePanel(friends_panel, panel_name, param); - } - else if (panel_name == "groups_panel") - { - return togglePeoplePanel(groups_panel, panel_name, param); - } - else if (panel_name == "nearby_panel") - { - return togglePeoplePanel(nearby_panel, panel_name, param); - } - else - { - return false; - } - } - - static bool togglePeoplePanel(LLPanel* &panel, const std::string& panel_name, const LLSD& param) - { - if(!panel) - { - panel = LLSideTray::getInstance()->getPanel(panel_name); - if(!panel) - return false; - } - - LLSideTray::getInstance()->togglePanel(panel, "panel_people", param); - - return true; - } -}; - -BOOL check_admin_override(void*) -{ - return gAgent.getAdminOverride(); -} - -void handle_admin_override_toggle(void*) -{ - gAgent.setAdminOverride(!gAgent.getAdminOverride()); - - // The above may have affected which debug menus are visible - show_debug_menus(); -} - -void handle_god_mode(void*) -{ - gAgent.requestEnterGodMode(); -} - -void handle_leave_god_mode(void*) -{ - gAgent.requestLeaveGodMode(); -} - -void set_god_level(U8 god_level) -{ - U8 old_god_level = gAgent.getGodLevel(); - gAgent.setGodLevel( god_level ); - LLViewerParcelMgr::getInstance()->notifyObservers(); - - // God mode changes region visibility - LLWorldMap::getInstance()->reloadItems(true); - - // inventory in items may change in god mode - gObjectList.dirtyAllObjectInventory(); - - if(gViewerWindow) - { - gViewerWindow->setMenuBackgroundColor(god_level > GOD_NOT, - LLGridManager::getInstance()->isInProductionGrid()); - } - - LLSD args; - if(god_level > GOD_NOT) - { - args["LEVEL"] = llformat("%d",(S32)god_level); - LLNotificationsUtil::add("EnteringGodMode", args); - } - else - { - args["LEVEL"] = llformat("%d",(S32)old_god_level); - LLNotificationsUtil::add("LeavingGodMode", args); - } - - // changing god-level can affect which menus we see - show_debug_menus(); - - // changing god-level can invalidate search results - LLFloaterSearch *search = dynamic_cast(LLFloaterReg::getInstance("search")); - if (search) - { - search->godLevelChanged(god_level); - } -} - -#ifdef TOGGLE_HACKED_GODLIKE_VIEWER -void handle_toggle_hacked_godmode(void*) -{ - gHackGodmode = !gHackGodmode; - set_god_level(gHackGodmode ? GOD_MAINTENANCE : GOD_NOT); -} - -BOOL check_toggle_hacked_godmode(void*) -{ - return gHackGodmode; -} - -bool enable_toggle_hacked_godmode(void*) -{ - return !LLGridManager::getInstance()->isInProductionGrid(); -} -#endif - -void process_grant_godlike_powers(LLMessageSystem* msg, void**) -{ - LLUUID agent_id; - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); - LLUUID session_id; - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); - if((agent_id == gAgent.getID()) && (session_id == gAgent.getSessionID())) - { - U8 god_level; - msg->getU8Fast(_PREHASH_GrantData, _PREHASH_GodLevel, god_level); - set_god_level(god_level); - } - else - { - llwarns << "Grant godlike for wrong agent " << agent_id << llendl; - } -} - -/* -class LLHaveCallingcard : public LLInventoryCollectFunctor -{ -public: - LLHaveCallingcard(const LLUUID& agent_id); - virtual ~LLHaveCallingcard() {} - virtual bool operator()(LLInventoryCategory* cat, - LLInventoryItem* item); - BOOL isThere() const { return mIsThere;} -protected: - LLUUID mID; - BOOL mIsThere; -}; - -LLHaveCallingcard::LLHaveCallingcard(const LLUUID& agent_id) : - mID(agent_id), - mIsThere(FALSE) -{ -} - -bool LLHaveCallingcard::operator()(LLInventoryCategory* cat, - LLInventoryItem* item) -{ - if(item) - { - if((item->getType() == LLAssetType::AT_CALLINGCARD) - && (item->getCreatorUUID() == mID)) - { - mIsThere = TRUE; - } - } - return FALSE; -} -*/ - -BOOL is_agent_mappable(const LLUUID& agent_id) -{ - const LLRelationship* buddy_info = NULL; - bool is_friend = LLAvatarActions::isFriend(agent_id); - - if (is_friend) - buddy_info = LLAvatarTracker::instance().getBuddyInfo(agent_id); - - return (buddy_info && - buddy_info->isOnline() && - buddy_info->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION) - ); -} - - -// Enable a menu item when you don't have someone's card. -class LLAvatarEnableAddFriend : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); - bool new_value = avatar && !LLAvatarActions::isFriend(avatar->getID()); - return new_value; - } -}; - -void request_friendship(const LLUUID& dest_id) -{ - LLViewerObject* dest = gObjectList.findObject(dest_id); - if(dest && dest->isAvatar()) - { - std::string full_name; - LLNameValue* nvfirst = dest->getNVPair("FirstName"); - LLNameValue* nvlast = dest->getNVPair("LastName"); - if(nvfirst && nvlast) - { - full_name = LLCacheName::buildFullName( - nvfirst->getString(), nvlast->getString()); - } - if (!full_name.empty()) - { - LLAvatarActions::requestFriendshipDialog(dest_id, full_name); - } - else - { - LLNotificationsUtil::add("CantOfferFriendship"); - } - } -} - - -class LLEditEnableCustomizeAvatar : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = gAgentWearables.areWearablesLoaded(); - return new_value; - } -}; - -class LLEnableEditShape : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - return gAgentWearables.isWearableModifiable(LLWearableType::WT_SHAPE, 0); - } -}; - -bool is_object_sittable() -{ - LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - - if (object && object->getPCode() == LL_PCODE_VOLUME) - { - return true; - } - else - { - return false; - } -} - - -// only works on pie menu -void handle_object_sit_or_stand() -{ - LLPickInfo pick = LLToolPie::getInstance()->getPick(); - LLViewerObject *object = pick.getObject();; - if (!object || pick.mPickType == LLPickInfo::PICK_FLORA) - { - return; - } - - if (sitting_on_selection()) - { - gAgent.standUp(); - return; - } - - // get object selection offset - - if (object && object->getPCode() == LL_PCODE_VOLUME) - { - - gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gMessageSystem->nextBlockFast(_PREHASH_TargetObject); - gMessageSystem->addUUIDFast(_PREHASH_TargetID, object->mID); - gMessageSystem->addVector3Fast(_PREHASH_Offset, pick.mObjectOffset); - - object->getRegion()->sendReliableMessage(); - } -} - -void near_sit_down_point(BOOL success, void *) -{ - if (success) - { - gAgent.setFlying(FALSE); - gAgent.setControlFlags(AGENT_CONTROL_SIT_ON_GROUND); - - // Might be first sit - //LLFirstUse::useSit(); - } -} - -class LLLandSit : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gAgent.standUp(); - LLViewerParcelMgr::getInstance()->deselectLand(); - - LLVector3d posGlobal = LLToolPie::getInstance()->getPick().mPosGlobal; - - LLQuaternion target_rot; - if (isAgentAvatarValid()) - { - target_rot = gAgentAvatarp->getRotation(); - } - else - { - target_rot = gAgent.getFrameAgent().getQuaternion(); - } - gAgent.startAutoPilotGlobal(posGlobal, "Sit", &target_rot, near_sit_down_point, NULL, 0.7f); - return true; - } -}; - -//------------------------------------------------------------------- -// Help menu functions -//------------------------------------------------------------------- - -// -// Major mode switching -// -void reset_view_final( BOOL proceed ); - -void handle_reset_view() -{ - if (gAgentCamera.cameraCustomizeAvatar()) - { - // switching to outfit selector should automagically save any currently edited wearable - LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "my_outfits")); - } - - gAgentCamera.switchCameraPreset(CAMERA_PRESET_REAR_VIEW); - reset_view_final( TRUE ); - LLFloaterCamera::resetCameraMode(); -} - -class LLViewResetView : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - handle_reset_view(); - return true; - } -}; - -// Note: extra parameters allow this function to be called from dialog. -void reset_view_final( BOOL proceed ) -{ - if( !proceed ) - { - return; - } - - gAgentCamera.resetView(TRUE, TRUE); - gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR); -} - -class LLViewLookAtLastChatter : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gAgentCamera.lookAtLastChat(); - return true; - } -}; - -class LLViewMouselook : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if (!gAgentCamera.cameraMouselook()) - { - gAgentCamera.changeCameraToMouselook(); - } - else - { - gAgentCamera.changeCameraToDefault(); - } - return true; - } -}; - -class LLViewDefaultUISize : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gSavedSettings.setF32("UIScaleFactor", 1.0f); - gSavedSettings.setBOOL("UIAutoScale", FALSE); - gViewerWindow->reshape(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw()); - return true; - } -}; - -class LLEditDuplicate : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if(LLEditMenuHandler::gEditMenuHandler) - { - LLEditMenuHandler::gEditMenuHandler->duplicate(); - } - return true; - } -}; - -class LLEditEnableDuplicate : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canDuplicate(); - return new_value; - } -}; - -void handle_duplicate_in_place(void*) -{ - llinfos << "handle_duplicate_in_place" << llendl; - - LLVector3 offset(0.f, 0.f, 0.f); - LLSelectMgr::getInstance()->selectDuplicate(offset, TRUE); -} - -/* dead code 30-apr-2008 -void handle_deed_object_to_group(void*) -{ - LLUUID group_id; - - LLSelectMgr::getInstance()->selectGetGroup(group_id); - LLSelectMgr::getInstance()->sendOwner(LLUUID::null, group_id, FALSE); - LLViewerStats::getInstance()->incStat(LLViewerStats::ST_RELEASE_COUNT); -} - -BOOL enable_deed_object_to_group(void*) -{ - if(LLSelectMgr::getInstance()->getSelection()->isEmpty()) return FALSE; - LLPermissions perm; - LLUUID group_id; - - if (LLSelectMgr::getInstance()->selectGetGroup(group_id) && - gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) && - LLSelectMgr::getInstance()->selectGetPermissions(perm) && - perm.deedToGroup(gAgent.getID(), group_id)) - { - return TRUE; - } - return FALSE; -} - -*/ - - -/* - * No longer able to support viewer side manipulations in this way - * -void god_force_inv_owner_permissive(LLViewerObject* object, - LLInventoryObject::object_list_t* inventory, - S32 serial_num, - void*) -{ - typedef std::vector > item_array_t; - item_array_t items; - - LLInventoryObject::object_list_t::const_iterator inv_it = inventory->begin(); - LLInventoryObject::object_list_t::const_iterator inv_end = inventory->end(); - for ( ; inv_it != inv_end; ++inv_it) - { - if(((*inv_it)->getType() != LLAssetType::AT_CATEGORY)) - { - LLInventoryObject* obj = *inv_it; - LLPointer new_item = new LLViewerInventoryItem((LLViewerInventoryItem*)obj); - LLPermissions perm(new_item->getPermissions()); - perm.setMaskBase(PERM_ALL); - perm.setMaskOwner(PERM_ALL); - new_item->setPermissions(perm); - items.push_back(new_item); - } - } - item_array_t::iterator end = items.end(); - item_array_t::iterator it; - for(it = items.begin(); it != end; ++it) - { - // since we have the inventory item in the callback, it should not - // invalidate iteration through the selection manager. - object->updateInventory((*it), TASK_INVENTORY_ITEM_KEY, false); - } -} -*/ - -void handle_object_owner_permissive(void*) -{ - // only send this if they're a god. - if(gAgent.isGodlike()) - { - // do the objects. - LLSelectMgr::getInstance()->selectionSetObjectPermissions(PERM_BASE, TRUE, PERM_ALL, TRUE); - LLSelectMgr::getInstance()->selectionSetObjectPermissions(PERM_OWNER, TRUE, PERM_ALL, TRUE); - } -} - -void handle_object_owner_self(void*) -{ - // only send this if they're a god. - if(gAgent.isGodlike()) - { - LLSelectMgr::getInstance()->sendOwner(gAgent.getID(), gAgent.getGroupID(), TRUE); - } -} - -// Shortcut to set owner permissions to not editable. -void handle_object_lock(void*) -{ - LLSelectMgr::getInstance()->selectionSetObjectPermissions(PERM_OWNER, FALSE, PERM_MODIFY); -} - -void handle_object_asset_ids(void*) -{ - // only send this if they're a god. - if (gAgent.isGodlike()) - { - LLSelectMgr::getInstance()->sendGodlikeRequest("objectinfo", "assetids"); - } -} - -void handle_force_parcel_owner_to_me(void*) -{ - LLViewerParcelMgr::getInstance()->sendParcelGodForceOwner( gAgent.getID() ); -} - -void handle_force_parcel_to_content(void*) -{ - LLViewerParcelMgr::getInstance()->sendParcelGodForceToContent(); -} - -void handle_claim_public_land(void*) -{ - if (LLViewerParcelMgr::getInstance()->getSelectionRegion() != gAgent.getRegion()) - { - LLNotificationsUtil::add("ClaimPublicLand"); - return; - } - - LLVector3d west_south_global; - LLVector3d east_north_global; - LLViewerParcelMgr::getInstance()->getSelection(west_south_global, east_north_global); - LLVector3 west_south = gAgent.getPosAgentFromGlobal(west_south_global); - LLVector3 east_north = gAgent.getPosAgentFromGlobal(east_north_global); - - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("GodlikeMessage"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null); //not used - msg->nextBlock("MethodData"); - msg->addString("Method", "claimpublicland"); - msg->addUUID("Invoice", LLUUID::null); - std::string buffer; - buffer = llformat( "%f", west_south.mV[VX]); - msg->nextBlock("ParamList"); - msg->addString("Parameter", buffer); - buffer = llformat( "%f", west_south.mV[VY]); - msg->nextBlock("ParamList"); - msg->addString("Parameter", buffer); - buffer = llformat( "%f", east_north.mV[VX]); - msg->nextBlock("ParamList"); - msg->addString("Parameter", buffer); - buffer = llformat( "%f", east_north.mV[VY]); - msg->nextBlock("ParamList"); - msg->addString("Parameter", buffer); - gAgent.sendReliableMessage(); -} - - - -// HACK for easily testing new avatar geometry -void handle_god_request_avatar_geometry(void *) -{ - if (gAgent.isGodlike()) - { - LLSelectMgr::getInstance()->sendGodlikeRequest("avatar toggle", ""); - } -} - - -void derez_objects(EDeRezDestination dest, const LLUUID& dest_id) -{ - if(gAgentCamera.cameraMouselook()) - { - gAgentCamera.changeCameraToDefault(); - } - //gInventoryView->setPanelOpen(TRUE); - - std::string error; - LLDynamicArray derez_objects; - - // Check conditions that we can't deal with, building a list of - // everything that we'll actually be derezzing. - LLViewerRegion* first_region = NULL; - for (LLObjectSelection::valid_root_iterator iter = LLSelectMgr::getInstance()->getSelection()->valid_root_begin(); - iter != LLSelectMgr::getInstance()->getSelection()->valid_root_end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* object = node->getObject(); - LLViewerRegion* region = object->getRegion(); - if (!first_region) - { - first_region = region; - } - else - { - if(region != first_region) - { - // Derez doesn't work at all if the some of the objects - // are in regions besides the first object selected. - - // ...crosses region boundaries - error = "AcquireErrorObjectSpan"; - break; - } - } - if (object->isAvatar()) - { - // ...don't acquire avatars - continue; - } - - // If AssetContainers are being sent back, they will appear as - // boxes in the owner's inventory. - if (object->getNVPair("AssetContainer") - && dest != DRD_RETURN_TO_OWNER) - { - // this object is an asset container, derez its contents, not it - llwarns << "Attempt to derez deprecated AssetContainer object type not supported." << llendl; - /* - object->requestInventory(container_inventory_arrived, - (void *)(BOOL)(DRD_TAKE_INTO_AGENT_INVENTORY == dest)); - */ - continue; - } - BOOL can_derez_current = FALSE; - switch(dest) - { - case DRD_TAKE_INTO_AGENT_INVENTORY: - case DRD_TRASH: - if( (node->mPermissions->allowTransferTo(gAgent.getID()) && object->permModify()) - || (node->allowOperationOnNode(PERM_OWNER, GP_OBJECT_MANIPULATE)) ) - { - can_derez_current = TRUE; - } - break; - - case DRD_RETURN_TO_OWNER: - can_derez_current = TRUE; - break; - - default: - if((node->mPermissions->allowTransferTo(gAgent.getID()) - && object->permCopy()) - || gAgent.isGodlike()) - { - can_derez_current = TRUE; - } - break; - } - if(can_derez_current) - { - derez_objects.put(object); - } - } - - // This constant is based on (1200 - HEADER_SIZE) / 4 bytes per - // root. I lopped off a few (33) to provide a bit - // pad. HEADER_SIZE is currently 67 bytes, most of which is UUIDs. - // This gives us a maximum of 63500 root objects - which should - // satisfy anybody. - const S32 MAX_ROOTS_PER_PACKET = 250; - const S32 MAX_PACKET_COUNT = 254; - F32 packets = ceil((F32)derez_objects.count() / (F32)MAX_ROOTS_PER_PACKET); - if(packets > (F32)MAX_PACKET_COUNT) - { - error = "AcquireErrorTooManyObjects"; - } - - if(error.empty() && derez_objects.count() > 0) - { - U8 d = (U8)dest; - LLUUID tid; - tid.generate(); - U8 packet_count = (U8)packets; - S32 object_index = 0; - S32 objects_in_packet = 0; - LLMessageSystem* msg = gMessageSystem; - for(U8 packet_number = 0; - packet_number < packet_count; - ++packet_number) - { - msg->newMessageFast(_PREHASH_DeRezObject); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_AgentBlock); - msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); - msg->addU8Fast(_PREHASH_Destination, d); - msg->addUUIDFast(_PREHASH_DestinationID, dest_id); - msg->addUUIDFast(_PREHASH_TransactionID, tid); - msg->addU8Fast(_PREHASH_PacketCount, packet_count); - msg->addU8Fast(_PREHASH_PacketNumber, packet_number); - objects_in_packet = 0; - while((object_index < derez_objects.count()) - && (objects_in_packet++ < MAX_ROOTS_PER_PACKET)) - - { - LLViewerObject* object = derez_objects.get(object_index++); - msg->nextBlockFast(_PREHASH_ObjectData); - msg->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID()); - // VEFFECT: DerezObject - LLHUDEffectSpiral* effectp = (LLHUDEffectSpiral*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); - effectp->setPositionGlobal(object->getPositionGlobal()); - effectp->setColor(LLColor4U(gAgent.getEffectColor())); - } - msg->sendReliable(first_region->getHost()); - } - make_ui_sound("UISndObjectRezOut"); - - // Busy count decremented by inventory update, so only increment - // if will be causing an update. - if (dest != DRD_RETURN_TO_OWNER) - { - gViewerWindow->getWindow()->incBusyCount(); - } - } - else if(!error.empty()) - { - LLNotificationsUtil::add(error); - } -} - -void handle_take_copy() -{ - if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) return; - - const LLUUID category_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OBJECT); - derez_objects(DRD_ACQUIRE_TO_AGENT_INVENTORY, category_id); -} - -// You can return an object to its owner if it is on your land. -class LLObjectReturn : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) return true; - - mObjectSelection = LLSelectMgr::getInstance()->getEditSelection(); - - LLNotificationsUtil::add("ReturnToOwner", LLSD(), LLSD(), boost::bind(&LLObjectReturn::onReturnToOwner, this, _1, _2)); - return true; - } - - bool onReturnToOwner(const LLSD& notification, const LLSD& response) - { - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (0 == option) - { - // Ignore category ID for this derez destination. - derez_objects(DRD_RETURN_TO_OWNER, LLUUID::null); - } - - // drop reference to current selection - mObjectSelection = NULL; - return false; - } - -protected: - LLObjectSelectionHandle mObjectSelection; -}; - - -// Allow return to owner if one or more of the selected items is -// over land you own. -class LLObjectEnableReturn : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) - { - // Do not enable if nothing selected - return false; - } -#ifdef HACKED_GODLIKE_VIEWER - bool new_value = true; -#else - bool new_value = false; - if (gAgent.isGodlike()) - { - new_value = true; - } - else - { - LLViewerRegion* region = gAgent.getRegion(); - if (region) - { - // Estate owners and managers can always return objects. - if (region->canManageEstate()) - { - new_value = true; - } - else - { - struct f : public LLSelectedObjectFunctor - { - virtual bool apply(LLViewerObject* obj) - { - return - obj->permModify() || - obj->isReturnable(); - } - } func; - const bool firstonly = true; - new_value = LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, firstonly); - } - } - } -#endif - return new_value; - } -}; - -void force_take_copy(void*) -{ - if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) return; - const LLUUID category_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OBJECT); - derez_objects(DRD_FORCE_TO_GOD_INVENTORY, category_id); -} - -void handle_take() -{ - // we want to use the folder this was derezzed from if it's - // available. Otherwise, derez to the normal place. - if(LLSelectMgr::getInstance()->getSelection()->isEmpty()) - { - return; - } - - BOOL you_own_everything = TRUE; - BOOL locked_but_takeable_object = FALSE; - LLUUID category_id; - - for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); - iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* object = node->getObject(); - if(object) - { - if(!object->permYouOwner()) - { - you_own_everything = FALSE; - } - - if(!object->permMove()) - { - locked_but_takeable_object = TRUE; - } - } - if(node->mFolderID.notNull()) - { - if(category_id.isNull()) - { - category_id = node->mFolderID; - } - else if(category_id != node->mFolderID) - { - // we have found two potential destinations. break out - // now and send to the default location. - category_id.setNull(); - break; - } - } - } - if(category_id.notNull()) - { - // there is an unambiguous destination. See if this agent has - // such a location and it is not in the trash or library - if(!gInventory.getCategory(category_id)) - { - // nope, set to NULL. - category_id.setNull(); - } - if(category_id.notNull()) - { - // check trash - const LLUUID trash = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); - if(category_id == trash || gInventory.isObjectDescendentOf(category_id, trash)) - { - category_id.setNull(); - } - - // check library - if(gInventory.isObjectDescendentOf(category_id, gInventory.getLibraryRootFolderID())) - { - category_id.setNull(); - } - - } - } - if(category_id.isNull()) - { - category_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OBJECT); - } - LLSD payload; - payload["folder_id"] = category_id; - - LLNotification::Params params("ConfirmObjectTakeLock"); - params.payload(payload); - params.functor.function(confirm_take); - - if(locked_but_takeable_object || - !you_own_everything) - { - if(locked_but_takeable_object && you_own_everything) - { - params.name("ConfirmObjectTakeLock"); - } - else if(!locked_but_takeable_object && !you_own_everything) - { - params.name("ConfirmObjectTakeNoOwn"); - } - else - { - params.name("ConfirmObjectTakeLockNoOwn"); - } - - LLNotifications::instance().add(params); - } - else - { - LLNotifications::instance().forceResponse(params, 0); - } -} - -void handle_object_show_inspector() -{ - LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); - LLViewerObject* objectp = selection->getFirstRootObject(TRUE); - if (!objectp) - { - return; - } - - LLSD params; - params["object_id"] = objectp->getID(); - LLFloaterReg::showInstance("inspect_object", params); -} - -void handle_avatar_show_inspector() -{ - LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); - if(avatar) - { - LLSD params; - params["avatar_id"] = avatar->getID(); - LLFloaterReg::showInstance("inspect_avatar", params); - } -} - - - -bool confirm_take(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if(enable_take() && (option == 0)) - { - derez_objects(DRD_TAKE_INTO_AGENT_INVENTORY, notification["payload"]["folder_id"].asUUID()); - } - return false; -} - -// You can take an item when it is public and transferrable, or when -// you own it. We err on the side of enabling the item when at least -// one item selected can be copied to inventory. -BOOL enable_take() -{ - if (sitting_on_selection()) - { - return FALSE; - } - - for (LLObjectSelection::valid_root_iterator iter = LLSelectMgr::getInstance()->getSelection()->valid_root_begin(); - iter != LLSelectMgr::getInstance()->getSelection()->valid_root_end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* object = node->getObject(); - if (object->isAvatar()) - { - // ...don't acquire avatars - continue; - } - -#ifdef HACKED_GODLIKE_VIEWER - return TRUE; -#else -# ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLGridManager::getInstance()->isInProductionGrid() - && gAgent.isGodlike()) - { - return TRUE; - } -# endif - if((node->mPermissions->allowTransferTo(gAgent.getID()) - && object->permModify()) - || (node->mPermissions->getOwner() == gAgent.getID())) - { - return TRUE; - } -#endif - } - return FALSE; -} - - -void handle_buy_or_take() -{ - if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) - { - return; - } - - if (is_selection_buy_not_take()) - { - S32 total_price = selection_price(); - - if (total_price <= gStatusBar->getBalance() || total_price == 0) - { - handle_buy(); - } - else - { - LLStringUtil::format_map_t args; - args["AMOUNT"] = llformat("%d", total_price); - LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString( "BuyingCosts", args ), total_price ); - } - } - else - { - handle_take(); - } -} - -bool visible_buy_object() -{ - return is_selection_buy_not_take() && enable_buy_object(); -} - -bool visible_take_object() -{ - return !is_selection_buy_not_take() && enable_take(); -} - -bool tools_visible_buy_object() -{ - return is_selection_buy_not_take(); -} - -bool tools_visible_take_object() -{ - return !is_selection_buy_not_take(); -} - -class LLToolsEnableBuyOrTake : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool is_buy = is_selection_buy_not_take(); - bool new_value = is_buy ? enable_buy_object() : enable_take(); - return new_value; - } -}; - -// This is a small helper function to determine if we have a buy or a -// take in the selection. This method is to help with the aliasing -// problems of putting buy and take in the same pie menu space. After -// a fair amont of discussion, it was determined to prefer buy over -// take. The reasoning follows from the fact that when users walk up -// to buy something, they will click on one or more items. Thus, if -// anything is for sale, it becomes a buy operation, and the server -// will group all of the buy items, and copyable/modifiable items into -// one package and give the end user as much as the permissions will -// allow. If the user wanted to take something, they will select fewer -// and fewer items until only 'takeable' items are left. The one -// exception is if you own everything in the selection that is for -// sale, in this case, you can't buy stuff from yourself, so you can -// take it. -// return value = TRUE if selection is a 'buy'. -// FALSE if selection is a 'take' -BOOL is_selection_buy_not_take() -{ - for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); - iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* obj = node->getObject(); - if(obj && !(obj->permYouOwner()) && (node->mSaleInfo.isForSale())) - { - // you do not own the object and it is for sale, thus, - // it's a buy - return TRUE; - } - } - return FALSE; -} - -S32 selection_price() -{ - S32 total_price = 0; - for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); - iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* obj = node->getObject(); - if(obj && !(obj->permYouOwner()) && (node->mSaleInfo.isForSale())) - { - // you do not own the object and it is for sale. - // Add its price. - total_price += node->mSaleInfo.getSalePrice(); - } - } - - return total_price; -} -/* -bool callback_show_buy_currency(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (0 == option) - { - llinfos << "Loading page " << LLNotifications::instance().getGlobalString("BUY_CURRENCY_URL") << llendl; - LLWeb::loadURL(LLNotifications::instance().getGlobalString("BUY_CURRENCY_URL")); - } - return false; -} -*/ - -void show_buy_currency(const char* extra) -{ - // Don't show currency web page for branded clients. -/* - std::ostringstream mesg; - if (extra != NULL) - { - mesg << extra << "\n \n"; - } - mesg << "Go to " << LLNotifications::instance().getGlobalString("BUY_CURRENCY_URL")<< "\nfor information on purchasing currency?"; -*/ - LLSD args; - if (extra != NULL) - { - args["EXTRA"] = extra; - } - LLNotificationsUtil::add("PromptGoToCurrencyPage", args);//, LLSD(), callback_show_buy_currency); -} - -void handle_buy() -{ - if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) return; - - LLSaleInfo sale_info; - BOOL valid = LLSelectMgr::getInstance()->selectGetSaleInfo(sale_info); - if (!valid) return; - - S32 price = sale_info.getSalePrice(); - - if (price > 0 && price > gStatusBar->getBalance()) - { - LLStringUtil::format_map_t args; - args["AMOUNT"] = llformat("%d", price); - LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("this_object_costs", args), price ); - return; - } - - if (sale_info.getSaleType() == LLSaleInfo::FS_CONTENTS) - { - handle_buy_contents(sale_info); - } - else - { - handle_buy_object(sale_info); - } -} - -bool anyone_copy_selection(LLSelectNode* nodep) -{ - bool perm_copy = (bool)(nodep->getObject()->permCopy()); - bool all_copy = (bool)(nodep->mPermissions->getMaskEveryone() & PERM_COPY); - return perm_copy && all_copy; -} - -bool for_sale_selection(LLSelectNode* nodep) -{ - return nodep->mSaleInfo.isForSale() - && nodep->mPermissions->getMaskOwner() & PERM_TRANSFER - && (nodep->mPermissions->getMaskOwner() & PERM_COPY - || nodep->mSaleInfo.getSaleType() != LLSaleInfo::FS_COPY); -} - -BOOL sitting_on_selection() -{ - LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); - if (!node) - { - return FALSE; - } - - if (!node->mValid) - { - return FALSE; - } - - LLViewerObject* root_object = node->getObject(); - if (!root_object) - { - return FALSE; - } - - // Need to determine if avatar is sitting on this object - if (!isAgentAvatarValid()) return FALSE; - - return (gAgentAvatarp->isSitting() && gAgentAvatarp->getRoot() == root_object); -} - -class LLToolsSaveToInventory : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if(enable_save_into_inventory(NULL)) - { - derez_objects(DRD_SAVE_INTO_AGENT_INVENTORY, LLUUID::null); - } - return true; - } -}; - -class LLToolsSaveToObjectInventory : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); - if(node && (node->mValid) && (!node->mFromTaskID.isNull())) - { - // *TODO: check to see if the fromtaskid object exists. - derez_objects(DRD_SAVE_INTO_TASK_INVENTORY, node->mFromTaskID); - } - return true; - } -}; - -// Round the position of all root objects to the grid -class LLToolsSnapObjectXY : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - F64 snap_size = (F64)gSavedSettings.getF32("GridResolution"); - - for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); - iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* obj = node->getObject(); - if (obj->permModify()) - { - LLVector3d pos_global = obj->getPositionGlobal(); - F64 round_x = fmod(pos_global.mdV[VX], snap_size); - if (round_x < snap_size * 0.5) - { - // closer to round down - pos_global.mdV[VX] -= round_x; - } - else - { - // closer to round up - pos_global.mdV[VX] -= round_x; - pos_global.mdV[VX] += snap_size; - } - - F64 round_y = fmod(pos_global.mdV[VY], snap_size); - if (round_y < snap_size * 0.5) - { - pos_global.mdV[VY] -= round_y; - } - else - { - pos_global.mdV[VY] -= round_y; - pos_global.mdV[VY] += snap_size; - } - - obj->setPositionGlobal(pos_global, FALSE); - } - } - LLSelectMgr::getInstance()->sendMultipleUpdate(UPD_POSITION); - return true; - } -}; - -// Determine if the option to cycle between linked prims is shown -class LLToolsEnableSelectNextPart : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = (gSavedSettings.getBOOL("EditLinkedParts") && - !LLSelectMgr::getInstance()->getSelection()->isEmpty()); - return new_value; - } -}; - -// Cycle selection through linked children in selected object. -// FIXME: Order of children list is not always the same as sim's idea of link order. This may confuse -// resis. Need link position added to sim messages to address this. -class LLToolsSelectNextPart : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - S32 object_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); - if (gSavedSettings.getBOOL("EditLinkedParts") && object_count) - { - LLViewerObject* selected = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); - if (selected && selected->getRootEdit()) - { - bool fwd = (userdata.asString() == "next"); - bool prev = (userdata.asString() == "previous"); - bool ifwd = (userdata.asString() == "includenext"); - bool iprev = (userdata.asString() == "includeprevious"); - LLViewerObject* to_select = NULL; - LLViewerObject::child_list_t children = selected->getRootEdit()->getChildren(); - children.push_front(selected->getRootEdit()); // need root in the list too - - for (LLViewerObject::child_list_t::iterator iter = children.begin(); iter != children.end(); ++iter) - { - if ((*iter)->isSelected()) - { - if (object_count > 1 && (fwd || prev)) // multiple selection, find first or last selected if not include - { - to_select = *iter; - if (fwd) - { - // stop searching if going forward; repeat to get last hit if backward - break; - } - } - else if ((object_count == 1) || (ifwd || iprev)) // single selection or include - { - if (fwd || ifwd) - { - ++iter; - while (iter != children.end() && ((*iter)->isAvatar() || (ifwd && (*iter)->isSelected()))) - { - ++iter; // skip sitting avatars and selected if include - } - } - else // backward - { - iter = (iter == children.begin() ? children.end() : iter); - --iter; - while (iter != children.begin() && ((*iter)->isAvatar() || (iprev && (*iter)->isSelected()))) - { - --iter; // skip sitting avatars and selected if include - } - } - iter = (iter == children.end() ? children.begin() : iter); - to_select = *iter; - break; - } - } - } - - if (to_select) - { - if (gFocusMgr.childHasKeyboardFocus(gFloaterTools)) - { - gFocusMgr.setKeyboardFocus(NULL); // force edit toolbox to commit any changes - } - if (fwd || prev) - { - LLSelectMgr::getInstance()->deselectAll(); - } - LLSelectMgr::getInstance()->selectObjectOnly(to_select); - return true; - } - } - } - return true; - } -}; - -// in order to link, all objects must have the same owner, and the -// agent must have the ability to modify all of the objects. However, -// we're not answering that question with this method. The question -// we're answering is: does the user have a reasonable expectation -// that a link operation should work? If so, return true, false -// otherwise. this allows the handle_link method to more finely check -// the selection and give an error message when the uer has a -// reasonable expectation for the link to work, but it will fail. -class LLToolsEnableLink : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = false; - // check if there are at least 2 objects selected, and that the - // user can modify at least one of the selected objects. - - // in component mode, can't link - if (!gSavedSettings.getBOOL("EditLinkedParts")) - { - if(LLSelectMgr::getInstance()->selectGetAllRootsValid() && LLSelectMgr::getInstance()->getSelection()->getRootObjectCount() >= 2) - { - struct f : public LLSelectedObjectFunctor - { - virtual bool apply(LLViewerObject* object) - { - return object->permModify(); - } - } func; - const bool firstonly = true; - new_value = LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, firstonly); - } - } - return new_value; - } -}; - -class LLToolsLink : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if(!LLSelectMgr::getInstance()->selectGetAllRootsValid()) - { - LLNotificationsUtil::add("UnableToLinkWhileDownloading"); - return true; - } - - S32 object_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); - if (object_count > MAX_CHILDREN_PER_TASK + 1) - { - LLSD args; - args["COUNT"] = llformat("%d", object_count); - int max = MAX_CHILDREN_PER_TASK+1; - args["MAX"] = llformat("%d", max); - LLNotificationsUtil::add("UnableToLinkObjects", args); - return true; - } - - if(LLSelectMgr::getInstance()->getSelection()->getRootObjectCount() < 2) - { - LLNotificationsUtil::add("CannotLinkIncompleteSet"); - return true; - } - if(!LLSelectMgr::getInstance()->selectGetRootsModify()) - { - LLNotificationsUtil::add("CannotLinkModify"); - return true; - } - LLUUID owner_id; - std::string owner_name; - if(!LLSelectMgr::getInstance()->selectGetOwner(owner_id, owner_name)) - { - // we don't actually care if you're the owner, but novices are - // the most likely to be stumped by this one, so offer the - // easiest and most likely solution. - LLNotificationsUtil::add("CannotLinkDifferentOwners"); - return true; - } - LLSelectMgr::getInstance()->sendLink(); - return true; - } -}; - -class LLToolsEnableUnlink : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLViewerObject* first_editable_object = LLSelectMgr::getInstance()->getSelection()->getFirstEditableObject(); - bool new_value = LLSelectMgr::getInstance()->selectGetAllRootsValid() && - first_editable_object && - !first_editable_object->isAttachment(); - return new_value; - } -}; - -class LLToolsUnlink : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLSelectMgr::getInstance()->sendDelink(); - return true; - } -}; - - -class LLToolsStopAllAnimations : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gAgent.stopCurrentAnimations(); - return true; - } -}; - -class LLToolsReleaseKeys : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gAgent.forceReleaseControls(); - - return true; - } -}; - -class LLToolsEnableReleaseKeys : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - return gAgent.anyControlGrabbed(); - } -}; - - -class LLEditEnableCut : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canCut(); - return new_value; - } -}; - -class LLEditCut : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if( LLEditMenuHandler::gEditMenuHandler ) - { - LLEditMenuHandler::gEditMenuHandler->cut(); - } - return true; - } -}; - -class LLEditEnableCopy : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canCopy(); - return new_value; - } -}; - -class LLEditCopy : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if( LLEditMenuHandler::gEditMenuHandler ) - { - LLEditMenuHandler::gEditMenuHandler->copy(); - } - return true; - } -}; - -class LLEditEnablePaste : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canPaste(); - return new_value; - } -}; - -class LLEditPaste : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if( LLEditMenuHandler::gEditMenuHandler ) - { - LLEditMenuHandler::gEditMenuHandler->paste(); - } - return true; - } -}; - -class LLEditEnableDelete : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canDoDelete(); - return new_value; - } -}; - -class LLEditDelete : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - // If a text field can do a deletion, it gets precedence over deleting - // an object in the world. - if( LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canDoDelete()) - { - LLEditMenuHandler::gEditMenuHandler->doDelete(); - } - - // and close any pie/context menus when done - gMenuHolder->hideMenus(); - - // When deleting an object we may not actually be done - // Keep selection so we know what to delete when confirmation is needed about the delete - gMenuObject->hide(); - return true; - } -}; - -bool enable_object_delete() -{ - bool new_value = -#ifdef HACKED_GODLIKE_VIEWER - TRUE; -#else -# ifdef TOGGLE_HACKED_GODLIKE_VIEWER - (!LLGridManager::getInstance()->isInProductionGrid() - && gAgent.isGodlike()) || -# endif - LLSelectMgr::getInstance()->canDoDelete(); -#endif - return new_value; -} - -void handle_object_delete() -{ - - if (LLSelectMgr::getInstance()) - { - LLSelectMgr::getInstance()->doDelete(); - } - - // and close any pie/context menus when done - gMenuHolder->hideMenus(); - - // When deleting an object we may not actually be done - // Keep selection so we know what to delete when confirmation is needed about the delete - gMenuObject->hide(); - return; -} - -void handle_force_delete(void*) -{ - LLSelectMgr::getInstance()->selectForceDelete(); -} - -class LLViewEnableJoystickFlycam : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = (gSavedSettings.getBOOL("JoystickEnabled") && gSavedSettings.getBOOL("JoystickFlycamEnabled")); - return new_value; - } -}; - -class LLViewEnableLastChatter : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - // *TODO: add check that last chatter is in range - bool new_value = (gAgentCamera.cameraThirdPerson() && gAgent.getLastChatter().notNull()); - return new_value; - } -}; - -class LLEditEnableDeselect : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canDeselect(); - return new_value; - } -}; - -class LLEditDeselect : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if( LLEditMenuHandler::gEditMenuHandler ) - { - LLEditMenuHandler::gEditMenuHandler->deselect(); - } - return true; - } -}; - -class LLEditEnableSelectAll : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canSelectAll(); - return new_value; - } -}; - - -class LLEditSelectAll : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if( LLEditMenuHandler::gEditMenuHandler ) - { - LLEditMenuHandler::gEditMenuHandler->selectAll(); - } - return true; - } -}; - - -class LLEditEnableUndo : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canUndo(); - return new_value; - } -}; - -class LLEditUndo : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if( LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canUndo() ) - { - LLEditMenuHandler::gEditMenuHandler->undo(); - } - return true; - } -}; - -class LLEditEnableRedo : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canRedo(); - return new_value; - } -}; - -class LLEditRedo : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if( LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canRedo() ) - { - LLEditMenuHandler::gEditMenuHandler->redo(); - } - return true; - } -}; - - - -void print_object_info(void*) -{ - LLSelectMgr::getInstance()->selectionDump(); -} - -void print_agent_nvpairs(void*) -{ - LLViewerObject *objectp; - - llinfos << "Agent Name Value Pairs" << llendl; - - objectp = gObjectList.findObject(gAgentID); - if (objectp) - { - objectp->printNameValuePairs(); - } - else - { - llinfos << "Can't find agent object" << llendl; - } - - llinfos << "Camera at " << gAgentCamera.getCameraPositionGlobal() << llendl; -} - -void show_debug_menus() -{ - // this might get called at login screen where there is no menu so only toggle it if one exists - if ( gMenuBarView ) - { - BOOL debug = gSavedSettings.getBOOL("UseDebugMenus"); - BOOL qamode = gSavedSettings.getBOOL("QAMode"); - - gMenuBarView->setItemVisible("Advanced", debug); -// gMenuBarView->setItemEnabled("Advanced", debug); // Don't disable Advanced keyboard shortcuts when hidden - - gMenuBarView->setItemVisible("Debug", qamode); - gMenuBarView->setItemEnabled("Debug", qamode); - - gMenuBarView->setItemVisible("Develop", qamode); - gMenuBarView->setItemEnabled("Develop", qamode); - - // Server ('Admin') menu hidden when not in godmode. - const bool show_server_menu = (gAgent.getGodLevel() > GOD_NOT || (debug && gAgent.getAdminOverride())); - gMenuBarView->setItemVisible("Admin", show_server_menu); - gMenuBarView->setItemEnabled("Admin", show_server_menu); - } - if (gLoginMenuBarView) - { - BOOL debug = gSavedSettings.getBOOL("UseDebugMenus"); - gLoginMenuBarView->setItemVisible("Debug", debug); - gLoginMenuBarView->setItemEnabled("Debug", debug); - } -} - -void toggle_debug_menus(void*) -{ - BOOL visible = ! gSavedSettings.getBOOL("UseDebugMenus"); - gSavedSettings.setBOOL("UseDebugMenus", visible); - show_debug_menus(); -} - - -// LLUUID gExporterRequestID; -// std::string gExportDirectory; - -// LLUploadDialog *gExportDialog = NULL; - -// void handle_export_selected( void * ) -// { -// LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); -// if (selection->isEmpty()) -// { -// return; -// } -// llinfos << "Exporting selected objects:" << llendl; - -// gExporterRequestID.generate(); -// gExportDirectory = ""; - -// LLMessageSystem* msg = gMessageSystem; -// msg->newMessageFast(_PREHASH_ObjectExportSelected); -// msg->nextBlockFast(_PREHASH_AgentData); -// msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); -// msg->addUUIDFast(_PREHASH_RequestID, gExporterRequestID); -// msg->addS16Fast(_PREHASH_VolumeDetail, 4); - -// for (LLObjectSelection::root_iterator iter = selection->root_begin(); -// iter != selection->root_end(); iter++) -// { -// LLSelectNode* node = *iter; -// LLViewerObject* object = node->getObject(); -// msg->nextBlockFast(_PREHASH_ObjectData); -// msg->addUUIDFast(_PREHASH_ObjectID, object->getID()); -// llinfos << "Object: " << object->getID() << llendl; -// } -// msg->sendReliable(gAgent.getRegion()->getHost()); - -// gExportDialog = LLUploadDialog::modalUploadDialog("Exporting selected objects..."); -// } -// - - -class LLWorldSetHomeLocation : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - // we just send the message and let the server check for failure cases - // server will echo back a "Home position set." alert if it succeeds - // and the home location screencapture happens when that alert is recieved - gAgent.setStartPosition(START_LOCATION_ID_HOME); - return true; - } -}; - -class LLWorldTeleportHome : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gAgent.teleportHome(); - return true; - } -}; - -class LLWorldAlwaysRun : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - // as well as altering the default walk-vs-run state, - // we also change the *current* walk-vs-run state. - if (gAgent.getAlwaysRun()) - { - gAgent.clearAlwaysRun(); - gAgent.clearRunning(); - } - else - { - gAgent.setAlwaysRun(); - gAgent.setRunning(); - } - - // tell the simulator. - gAgent.sendWalkRun(gAgent.getAlwaysRun()); - - // Update Movement Controls according to AlwaysRun mode - LLFloaterMove::setAlwaysRunMode(gAgent.getAlwaysRun()); - - return true; - } -}; - -class LLWorldCheckAlwaysRun : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = gAgent.getAlwaysRun(); - return new_value; - } -}; - -class LLWorldSetAway : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if (gAgent.getAFK()) - { - gAgent.clearAFK(); - } - else - { - gAgent.setAFK(); - } - return true; - } -}; - -class LLWorldSetBusy : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if (gAgent.getBusy()) - { - gAgent.clearBusy(); - } - else - { - gAgent.setBusy(); - LLNotificationsUtil::add("BusyModeSet"); - } - return true; - } -}; - -class LLWorldCreateLandmark : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLSideTray::getInstance()->showPanel("panel_places", LLSD().with("type", "create_landmark")); - - return true; - } -}; - -class LLWorldPlaceProfile : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLSideTray::getInstance()->showPanel("panel_places", LLSD().with("type", "agent")); - - return true; - } -}; - -void handle_look_at_selection(const LLSD& param) -{ - const F32 PADDING_FACTOR = 1.75f; - BOOL zoom = (param.asString() == "zoom"); - if (!LLSelectMgr::getInstance()->getSelection()->isEmpty()) - { - gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); - - LLBBox selection_bbox = LLSelectMgr::getInstance()->getBBoxOfSelection(); - F32 angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getAspect() > 1.f ? LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect() : LLViewerCamera::getInstance()->getView()); - F32 distance = selection_bbox.getExtentLocal().magVec() * PADDING_FACTOR / atan(angle_of_view); - - LLVector3 obj_to_cam = LLViewerCamera::getInstance()->getOrigin() - selection_bbox.getCenterAgent(); - obj_to_cam.normVec(); - - LLUUID object_id; - if (LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()) - { - object_id = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()->mID; - } - if (zoom) - { - // Make sure we are not increasing the distance between the camera and object - LLVector3d orig_distance = gAgentCamera.getCameraPositionGlobal() - LLSelectMgr::getInstance()->getSelectionCenterGlobal(); - distance = llmin(distance, (F32) orig_distance.length()); - - gAgentCamera.setCameraPosAndFocusGlobal(LLSelectMgr::getInstance()->getSelectionCenterGlobal() + LLVector3d(obj_to_cam * distance), - LLSelectMgr::getInstance()->getSelectionCenterGlobal(), - object_id ); - - } - else - { - gAgentCamera.setFocusGlobal( LLSelectMgr::getInstance()->getSelectionCenterGlobal(), object_id ); - } - } -} - -void handle_zoom_to_object(LLUUID object_id) -{ - const F32 PADDING_FACTOR = 2.f; - - LLViewerObject* object = gObjectList.findObject(object_id); - - if (object) - { - gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); - - LLBBox bbox = object->getBoundingBoxAgent() ; - F32 angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getAspect() > 1.f ? LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect() : LLViewerCamera::getInstance()->getView()); - F32 distance = bbox.getExtentLocal().magVec() * PADDING_FACTOR / atan(angle_of_view); - - LLVector3 obj_to_cam = LLViewerCamera::getInstance()->getOrigin() - bbox.getCenterAgent(); - obj_to_cam.normVec(); - - - LLVector3d object_center_global = gAgent.getPosGlobalFromAgent(bbox.getCenterAgent()); - - gAgentCamera.setCameraPosAndFocusGlobal(object_center_global + LLVector3d(obj_to_cam * distance), - object_center_global, - object_id ); - } -} - -class LLAvatarInviteToGroup : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); - if(avatar) - { - LLAvatarActions::inviteToGroup(avatar->getID()); - } - return true; - } -}; - -class LLAvatarAddFriend : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); - if(avatar && !LLAvatarActions::isFriend(avatar->getID())) - { - request_friendship(avatar->getID()); - } - return true; - } -}; - -class LLAvatarAddContact : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); - if(avatar) - { - create_inventory_callingcard(avatar->getID()); - } - return true; - } -}; - -bool complete_give_money(const LLSD& notification, const LLSD& response, LLObjectSelectionHandle selection) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option == 0) - { - gAgent.clearBusy(); - } - - LLViewerObject* objectp = selection->getPrimaryObject(); - - // Show avatar's name if paying attachment - if (objectp && objectp->isAttachment()) - { - while (objectp && !objectp->isAvatar()) - { - objectp = (LLViewerObject*)objectp->getParent(); - } - } - - if (objectp) - { - if (objectp->isAvatar()) - { - const bool is_group = false; - LLFloaterPayUtil::payDirectly(&give_money, - objectp->getID(), - is_group); - } - else - { - LLFloaterPayUtil::payViaObject(&give_money, selection); - } - } - return false; -} - -void handle_give_money_dialog() -{ - LLNotification::Params params("BusyModePay"); - params.functor.function(boost::bind(complete_give_money, _1, _2, LLSelectMgr::getInstance()->getSelection())); - - if (gAgent.getBusy()) - { - // warn users of being in busy mode during a transaction - LLNotifications::instance().add(params); - } - else - { - LLNotifications::instance().forceResponse(params, 1); - } -} - -bool enable_pay_avatar() -{ - LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - LLVOAvatar* avatar = find_avatar_from_object(obj); - return (avatar != NULL); -} - -bool enable_pay_object() -{ - LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if( object ) - { - LLViewerObject *parent = (LLViewerObject *)object->getParent(); - if((object->flagTakesMoney()) || (parent && parent->flagTakesMoney())) - { - return true; - } - } - return false; -} - -bool enable_object_stand_up() -{ - // 'Object Stand Up' menu item is enabled when agent is sitting on selection - return sitting_on_selection(); -} - -bool enable_object_sit(LLUICtrl* ctrl) -{ - // 'Object Sit' menu item is enabled when agent is not sitting on selection - bool sitting_on_sel = sitting_on_selection(); - if (!sitting_on_sel) - { - std::string item_name = ctrl->getName(); - - // init default labels - init_default_item_label(item_name); - - // Update label - LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); - if (node && node->mValid && !node->mSitName.empty()) - { - gMenuHolder->childSetText(item_name, node->mSitName); - } - else - { - gMenuHolder->childSetText(item_name, get_default_item_label(item_name)); - } - } - return !sitting_on_sel && is_object_sittable(); -} - -void dump_select_mgr(void*) -{ - LLSelectMgr::getInstance()->dump(); -} - -void dump_inventory(void*) -{ - gInventory.dumpInventory(); -} - - -void handle_dump_followcam(void*) -{ - LLFollowCamMgr::dump(); -} - -void handle_viewer_enable_message_log(void*) -{ - gMessageSystem->startLogging(); -} - -void handle_viewer_disable_message_log(void*) -{ - gMessageSystem->stopLogging(); -} - -void handle_customize_avatar() -{ - LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "my_outfits")); -} - -void handle_edit_outfit() -{ - LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "edit_outfit")); -} - -void handle_edit_shape() -{ - LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "edit_shape")); -} - -void handle_report_abuse() -{ - // Prevent menu from appearing in screen shot. - gMenuHolder->hideMenus(); - LLFloaterReporter::showFromMenu(COMPLAINT_REPORT); -} - -void handle_buy_currency() -{ - LLBuyCurrencyHTML::openCurrencyFloater(); -} - -class LLFloaterVisible : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string floater_name = userdata.asString(); - bool new_value = false; - { - new_value = LLFloaterReg::instanceVisible(floater_name); - } - return new_value; - } -}; - -class LLShowHelp : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string help_topic = userdata.asString(); - LLViewerHelp* vhelp = LLViewerHelp::getInstance(); - vhelp->showTopic(help_topic); - return true; - } -}; - -class LLToggleHelp : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLFloater* help_browser = (LLFloaterReg::findInstance("help_browser")); - if (help_browser && help_browser->isInVisibleChain()) - { - help_browser->closeFloater(); - } - else - { - std::string help_topic = userdata.asString(); - LLViewerHelp* vhelp = LLViewerHelp::getInstance(); - vhelp->showTopic(help_topic); - } - return true; - } -}; - -class LLShowSidetrayPanel : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string panel_name = userdata.asString(); - - LLPanel* panel = LLSideTray::getInstance()->getPanel(panel_name); - if (panel) - { - if (panel->isInVisibleChain()) - { - LLSideTray::getInstance()->hidePanel(panel_name); - } - else - { - LLSideTray::getInstance()->showPanel(panel_name); - } - } - return true; - } -}; - -class LLSidetrayPanelVisible : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string panel_name = userdata.asString(); - // Toggle the panel - if (LLSideTray::getInstance()->isPanelActive(panel_name)) - { - return true; - } - else - { - return false; - } - - } -}; - - -bool callback_show_url(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (0 == option) - { - LLWeb::loadURL(notification["payload"]["url"].asString()); - } - return false; -} - -class LLPromptShowURL : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string param = userdata.asString(); - std::string::size_type offset = param.find(","); - if (offset != param.npos) - { - std::string alert = param.substr(0, offset); - std::string url = param.substr(offset+1); - - if(gSavedSettings.getBOOL("UseExternalBrowser")) - { - LLSD payload; - payload["url"] = url; - LLNotificationsUtil::add(alert, LLSD(), payload, callback_show_url); - } - else - { - LLWeb::loadURL(url); - } - } - else - { - llinfos << "PromptShowURL invalid parameters! Expecting \"ALERT,URL\"." << llendl; - } - return true; - } -}; - -bool callback_show_file(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (0 == option) - { - LLWeb::loadURL(notification["payload"]["url"]); - } - return false; -} - -class LLPromptShowFile : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string param = userdata.asString(); - std::string::size_type offset = param.find(","); - if (offset != param.npos) - { - std::string alert = param.substr(0, offset); - std::string file = param.substr(offset+1); - - LLSD payload; - payload["url"] = file; - LLNotificationsUtil::add(alert, LLSD(), payload, callback_show_file); - } - else - { - llinfos << "PromptShowFile invalid parameters! Expecting \"ALERT,FILE\"." << llendl; - } - return true; - } -}; - -class LLShowAgentProfile : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLUUID agent_id; - if (userdata.asString() == "agent") - { - agent_id = gAgent.getID(); - } - else if (userdata.asString() == "hit object") - { - LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if (objectp) - { - agent_id = objectp->getID(); - } - } - else - { - agent_id = userdata.asUUID(); - } - - LLVOAvatar* avatar = find_avatar_from_object(agent_id); - if (avatar) - { - LLAvatarActions::showProfile(avatar->getID()); - } - return true; - } -}; - -class LLToggleAgentProfile : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLUUID agent_id; - if (userdata.asString() == "agent") - { - agent_id = gAgent.getID(); - } - else if (userdata.asString() == "hit object") - { - LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if (objectp) - { - agent_id = objectp->getID(); - } - } - else - { - agent_id = userdata.asUUID(); - } - - LLVOAvatar* avatar = find_avatar_from_object(agent_id); - if (avatar) - { - if (!LLAvatarActions::profileVisible(avatar->getID())) - { - LLAvatarActions::showProfile(avatar->getID()); - } - else - { - LLAvatarActions::hideProfile(avatar->getID()); - } - } - return true; - } -}; - -class LLLandEdit : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - if (gAgentCamera.getFocusOnAvatar() && gSavedSettings.getBOOL("EditCameraMovement") ) - { - // zoom in if we're looking at the avatar - gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); - gAgentCamera.setFocusGlobal(LLToolPie::getInstance()->getPick()); - - gAgentCamera.cameraOrbitOver( F_PI * 0.25f ); - gViewerWindow->moveCursorToCenter(); - } - else if ( gSavedSettings.getBOOL("EditCameraMovement") ) - { - gAgentCamera.setFocusGlobal(LLToolPie::getInstance()->getPick()); - gViewerWindow->moveCursorToCenter(); - } - - - LLViewerParcelMgr::getInstance()->selectParcelAt( LLToolPie::getInstance()->getPick().mPosGlobal ); - - LLFloaterReg::showInstance("build"); - - // Switch to land edit toolset - LLToolMgr::getInstance()->getCurrentToolset()->selectTool( LLToolSelectLand::getInstance() ); - return true; - } -}; - -class LLWorldEnableBuyLand : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLViewerParcelMgr::getInstance()->canAgentBuyParcel( - LLViewerParcelMgr::getInstance()->selectionEmpty() - ? LLViewerParcelMgr::getInstance()->getAgentParcel() - : LLViewerParcelMgr::getInstance()->getParcelSelection()->getParcel(), - false); - return new_value; - } -}; - -BOOL enable_buy_land(void*) -{ - return LLViewerParcelMgr::getInstance()->canAgentBuyParcel( - LLViewerParcelMgr::getInstance()->getParcelSelection()->getParcel(), false); -} - -void handle_buy_land() -{ - LLViewerParcelMgr* vpm = LLViewerParcelMgr::getInstance(); - if (vpm->selectionEmpty()) - { - vpm->selectParcelAt(gAgent.getPositionGlobal()); - } - vpm->startBuyLand(); -} - -class LLObjectAttachToAvatar : public view_listener_t -{ -public: - LLObjectAttachToAvatar(bool replace) : mReplace(replace) {} - static void setObjectSelection(LLObjectSelectionHandle selection) { sObjectSelection = selection; } - -private: - bool handleEvent(const LLSD& userdata) - { - setObjectSelection(LLSelectMgr::getInstance()->getSelection()); - LLViewerObject* selectedObject = sObjectSelection->getFirstRootObject(); - if (selectedObject) - { - S32 index = userdata.asInteger(); - LLViewerJointAttachment* attachment_point = NULL; - if (index > 0) - attachment_point = get_if_there(gAgentAvatarp->mAttachmentPoints, index, (LLViewerJointAttachment*)NULL); - confirmReplaceAttachment(0, attachment_point); - } - return true; - } - - static void onNearAttachObject(BOOL success, void *user_data); - void confirmReplaceAttachment(S32 option, LLViewerJointAttachment* attachment_point); - - struct CallbackData - { - CallbackData(LLViewerJointAttachment* point, bool replace) : mAttachmentPoint(point), mReplace(replace) {} - - LLViewerJointAttachment* mAttachmentPoint; - bool mReplace; - }; - -protected: - static LLObjectSelectionHandle sObjectSelection; - bool mReplace; -}; - -LLObjectSelectionHandle LLObjectAttachToAvatar::sObjectSelection; - -// static -void LLObjectAttachToAvatar::onNearAttachObject(BOOL success, void *user_data) -{ - if (!user_data) return; - CallbackData* cb_data = static_cast(user_data); - - if (success) - { - const LLViewerJointAttachment *attachment = cb_data->mAttachmentPoint; - - U8 attachment_id = 0; - if (attachment) - { - for (LLVOAvatar::attachment_map_t::const_iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); - iter != gAgentAvatarp->mAttachmentPoints.end(); ++iter) - { - if (iter->second == attachment) - { - attachment_id = iter->first; - break; - } - } - } - else - { - // interpret 0 as "default location" - attachment_id = 0; - } - LLSelectMgr::getInstance()->sendAttach(attachment_id, cb_data->mReplace); - } - LLObjectAttachToAvatar::setObjectSelection(NULL); - - delete cb_data; -} - -// static -void LLObjectAttachToAvatar::confirmReplaceAttachment(S32 option, LLViewerJointAttachment* attachment_point) -{ - if (option == 0/*YES*/) - { - LLViewerObject* selectedObject = LLSelectMgr::getInstance()->getSelection()->getFirstRootObject(); - if (selectedObject) - { - const F32 MIN_STOP_DISTANCE = 1.f; // meters - const F32 ARM_LENGTH = 0.5f; // meters - const F32 SCALE_FUDGE = 1.5f; - - F32 stop_distance = SCALE_FUDGE * selectedObject->getMaxScale() + ARM_LENGTH; - if (stop_distance < MIN_STOP_DISTANCE) - { - stop_distance = MIN_STOP_DISTANCE; - } - - LLVector3 walkToSpot = selectedObject->getPositionAgent(); - - // make sure we stop in front of the object - LLVector3 delta = walkToSpot - gAgent.getPositionAgent(); - delta.normVec(); - delta = delta * 0.5f; - walkToSpot -= delta; - - // The callback will be called even if avatar fails to get close enough to the object, so we won't get a memory leak. - CallbackData* user_data = new CallbackData(attachment_point, mReplace); - gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(walkToSpot), "Attach", NULL, onNearAttachObject, user_data, stop_distance); - gAgentCamera.clearFocusObject(); - } - } -} - -void callback_attachment_drop(const LLSD& notification, const LLSD& response) -{ - // Ensure user confirmed the drop - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option != 0) return; - - // Called when the user clicked on an object attached to them - // and selected "Drop". - LLUUID object_id = notification["payload"]["object_id"].asUUID(); - LLViewerObject *object = gObjectList.findObject(object_id); - - if (!object) - { - llwarns << "handle_drop_attachment() - no object to drop" << llendl; - return; - } - - LLViewerObject *parent = (LLViewerObject*)object->getParent(); - while (parent) - { - if(parent->isAvatar()) - { - break; - } - object = parent; - parent = (LLViewerObject*)parent->getParent(); - } - - if (!object) - { - llwarns << "handle_detach() - no object to detach" << llendl; - return; - } - - if (object->isAvatar()) - { - llwarns << "Trying to detach avatar from avatar." << llendl; - return; - } - - // reselect the object - LLSelectMgr::getInstance()->selectObjectAndFamily(object); - - LLSelectMgr::getInstance()->sendDropAttachment(); - - return; -} - -class LLAttachmentDrop : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLSD payload; - LLViewerObject *object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - - if (object) - { - payload["object_id"] = object->getID(); - } - else - { - llwarns << "Drop object not found" << llendl; - return true; - } - - LLNotificationsUtil::add("AttachmentDrop", LLSD(), payload, &callback_attachment_drop); - return true; - } -}; - -// called from avatar pie menu -class LLAttachmentDetachFromPoint : public view_listener_t -{ - bool handleEvent(const LLSD& user_data) - { - const LLViewerJointAttachment *attachment = get_if_there(gAgentAvatarp->mAttachmentPoints, user_data.asInteger(), (LLViewerJointAttachment*)NULL); - if (attachment->getNumObjects() > 0) - { - gMessageSystem->newMessage("ObjectDetach"); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - - for (LLViewerJointAttachment::attachedobjs_vec_t::const_iterator iter = attachment->mAttachedObjects.begin(); - iter != attachment->mAttachedObjects.end(); - iter++) - { - LLViewerObject *attached_object = (*iter); - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, attached_object->getLocalID()); - } - gMessageSystem->sendReliable( gAgent.getRegionHost() ); - } - return true; - } -}; - -static bool onEnableAttachmentLabel(LLUICtrl* ctrl, const LLSD& data) -{ - std::string label; - LLMenuItemGL* menu = dynamic_cast(ctrl); - if (menu) - { - const LLViewerJointAttachment *attachment = get_if_there(gAgentAvatarp->mAttachmentPoints, data["index"].asInteger(), (LLViewerJointAttachment*)NULL); - if (attachment) - { - label = data["label"].asString(); - for (LLViewerJointAttachment::attachedobjs_vec_t::const_iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) - { - const LLViewerObject* attached_object = (*attachment_iter); - if (attached_object) - { - LLViewerInventoryItem* itemp = gInventory.getItem(attached_object->getAttachmentItemID()); - if (itemp) - { - label += std::string(" (") + itemp->getName() + std::string(")"); - break; - } - } - } - } - menu->setLabel(label); - } - return true; -} - -class LLAttachmentDetach : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - // Called when the user clicked on an object attached to them - // and selected "Detach". - LLViewerObject *object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if (!object) - { - llwarns << "handle_detach() - no object to detach" << llendl; - return true; - } - - LLViewerObject *parent = (LLViewerObject*)object->getParent(); - while (parent) - { - if(parent->isAvatar()) - { - break; - } - object = parent; - parent = (LLViewerObject*)parent->getParent(); - } - - if (!object) - { - llwarns << "handle_detach() - no object to detach" << llendl; - return true; - } - - if (object->isAvatar()) - { - llwarns << "Trying to detach avatar from avatar." << llendl; - return true; - } - - // The sendDetach() method works on the list of selected - // objects. Thus we need to clear the list, make sure it only - // contains the object the user clicked, send the message, - // then clear the list. - // We use deselectAll to update the simulator's notion of what's - // selected, and removeAll just to change things locally. - //RN: I thought it was more useful to detach everything that was selected - if (LLSelectMgr::getInstance()->getSelection()->isAttachment()) - { - LLSelectMgr::getInstance()->sendDetach(); - } - return true; - } -}; - -//Adding an observer for a Jira 2422 and needs to be a fetch observer -//for Jira 3119 -class LLWornItemFetchedObserver : public LLInventoryFetchItemsObserver -{ -public: - LLWornItemFetchedObserver(const LLUUID& worn_item_id) : - LLInventoryFetchItemsObserver(worn_item_id) - {} - virtual ~LLWornItemFetchedObserver() {} - -protected: - virtual void done() - { - gMenuAttachmentSelf->buildDrawLabels(); - gInventory.removeObserver(this); - delete this; - } -}; - -// You can only drop items on parcels where you can build. -class LLAttachmentEnableDrop : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - BOOL can_build = gAgent.isGodlike() || (LLViewerParcelMgr::getInstance()->allowAgentBuild()); - - //Add an inventory observer to only allow dropping the newly attached item - //once it exists in your inventory. Look at Jira 2422. - //-jwolk - - // A bug occurs when you wear/drop an item before it actively is added to your inventory - // if this is the case (you're on a slow sim, etc.) a copy of the object, - // well, a newly created object with the same properties, is placed - // in your inventory. Therefore, we disable the drop option until the - // item is in your inventory - - LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - LLViewerJointAttachment* attachment = NULL; - LLInventoryItem* item = NULL; - - // Do not enable drop if all faces of object are not enabled - if (object && LLSelectMgr::getInstance()->getSelection()->contains(object,SELECT_ALL_TES )) - { - S32 attachmentID = ATTACHMENT_ID_FROM_STATE(object->getState()); - attachment = get_if_there(gAgentAvatarp->mAttachmentPoints, attachmentID, (LLViewerJointAttachment*)NULL); - - if (attachment) - { - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) - { - // make sure item is in your inventory (it could be a delayed attach message being sent from the sim) - // so check to see if the item is in the inventory already - item = gInventory.getItem((*attachment_iter)->getAttachmentItemID()); - if (!item) - { - // Item does not exist, make an observer to enable the pie menu - // when the item finishes fetching worst case scenario - // if a fetch is already out there (being sent from a slow sim) - // we refetch and there are 2 fetches - LLWornItemFetchedObserver* worn_item_fetched = new LLWornItemFetchedObserver((*attachment_iter)->getAttachmentItemID()); - worn_item_fetched->startFetch(); - gInventory.addObserver(worn_item_fetched); - } - } - } - } - - //now check to make sure that the item is actually in the inventory before we enable dropping it - bool new_value = enable_detach() && can_build && item; - - return new_value; - } -}; - -BOOL enable_detach(const LLSD&) -{ - LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - - // Only enable detach if all faces of object are selected - if (!object || - !object->isAttachment() || - !LLSelectMgr::getInstance()->getSelection()->contains(object,SELECT_ALL_TES )) - { - return FALSE; - } - - // Find the avatar who owns this attachment - LLViewerObject* avatar = object; - while (avatar) - { - // ...if it's you, good to detach - if (avatar->getID() == gAgent.getID()) - { - return TRUE; - } - - avatar = (LLViewerObject*)avatar->getParent(); - } - - return FALSE; -} - -class LLAttachmentEnableDetach : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = enable_detach(); - return new_value; - } -}; - -// Used to tell if the selected object can be attached to your avatar. -BOOL object_selected_and_point_valid() -{ - LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); - for (LLObjectSelection::root_iterator iter = selection->root_begin(); - iter != selection->root_end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* object = node->getObject(); - LLViewerObject::const_child_list_t& child_list = object->getChildren(); - for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); - iter != child_list.end(); iter++) - { - LLViewerObject* child = *iter; - if (child->isAvatar()) - { - return FALSE; - } - } - } - - return (selection->getRootObjectCount() == 1) && - (selection->getFirstRootObject()->getPCode() == LL_PCODE_VOLUME) && - selection->getFirstRootObject()->permYouOwner() && - selection->getFirstRootObject()->flagObjectMove() && - !((LLViewerObject*)selection->getFirstRootObject()->getRoot())->isAvatar() && - (selection->getFirstRootObject()->getNVPair("AssetContainer") == NULL); -} - - -BOOL object_is_wearable() -{ - if (!object_selected_and_point_valid()) - { - return FALSE; - } - if (sitting_on_selection()) - { - return FALSE; - } - LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); - for (LLObjectSelection::valid_root_iterator iter = LLSelectMgr::getInstance()->getSelection()->valid_root_begin(); - iter != LLSelectMgr::getInstance()->getSelection()->valid_root_end(); iter++) - { - LLSelectNode* node = *iter; - if (node->mPermissions->getOwner() == gAgent.getID()) - { - return TRUE; - } - } - return FALSE; -} - - -class LLAttachmentPointFilled : public view_listener_t -{ - bool handleEvent(const LLSD& user_data) - { - bool enable = false; - LLVOAvatar::attachment_map_t::iterator found_it = gAgentAvatarp->mAttachmentPoints.find(user_data.asInteger()); - if (found_it != gAgentAvatarp->mAttachmentPoints.end()) - { - enable = found_it->second->getNumObjects() > 0; - } - return enable; - } -}; - -class LLAvatarSendIM : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); - if(avatar) - { - LLAvatarActions::startIM(avatar->getID()); - } - return true; - } -}; - -class LLAvatarCall : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); - if(avatar) - { - LLAvatarActions::startCall(avatar->getID()); - } - return true; - } -}; - -namespace -{ - struct QueueObjects : public LLSelectedObjectFunctor - { - BOOL scripted; - BOOL modifiable; - LLFloaterScriptQueue* mQueue; - QueueObjects(LLFloaterScriptQueue* q) : mQueue(q), scripted(FALSE), modifiable(FALSE) {} - virtual bool apply(LLViewerObject* obj) - { - scripted = obj->flagScripted(); - modifiable = obj->permModify(); - - if( scripted && modifiable ) - { - mQueue->addObject(obj->getID()); - return false; - } - else - { - return true; // fail: stop applying - } - } - }; -} - -void queue_actions(LLFloaterScriptQueue* q, const std::string& msg) -{ - QueueObjects func(q); - LLSelectMgr *mgr = LLSelectMgr::getInstance(); - LLObjectSelectionHandle selectHandle = mgr->getSelection(); - bool fail = selectHandle->applyToObjects(&func); - if(fail) - { - if ( !func.scripted ) - { - std::string noscriptmsg = std::string("Cannot") + msg + "SelectObjectsNoScripts"; - LLNotificationsUtil::add(noscriptmsg); - } - else if ( !func.modifiable ) - { - std::string nomodmsg = std::string("Cannot") + msg + "SelectObjectsNoPermission"; - LLNotificationsUtil::add(nomodmsg); - } - else - { - llerrs << "Bad logic." << llendl; - } - } - else - { - if (!q->start()) - { - llwarns << "Unexpected script compile failure." << llendl; - } - } -} - -class LLToolsSelectedScriptAction : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string action = userdata.asString(); - bool mono = false; - std::string msg, name; - if (action == "compile mono") - { - name = "compile_queue"; - mono = true; - msg = "Recompile"; - } - if (action == "compile lsl") - { - name = "compile_queue"; - msg = "Recompile"; - } - else if (action == "reset") - { - name = "reset_queue"; - msg = "Reset"; - } - else if (action == "start") - { - name = "start_queue"; - msg = "Running"; - } - else if (action == "stop") - { - name = "stop_queue"; - msg = "RunningNot"; - } - LLUUID id; id.generate(); - - LLFloaterScriptQueue* queue =LLFloaterReg::getTypedInstance(name, LLSD(id)); - if (queue) - { - queue->setMono(mono); - queue_actions(queue, msg); - } - else - { - llwarns << "Failed to generate LLFloaterScriptQueue with action: " << action << llendl; - } - return true; - } -}; - -void handle_selected_texture_info(void*) -{ - for (LLObjectSelection::valid_iterator iter = LLSelectMgr::getInstance()->getSelection()->valid_begin(); - iter != LLSelectMgr::getInstance()->getSelection()->valid_end(); iter++) - { - LLSelectNode* node = *iter; - - std::string msg; - msg.assign("Texture info for: "); - msg.append(node->mName); - - LLSD args; - args["MESSAGE"] = msg; - LLNotificationsUtil::add("SystemMessage", args); - - U8 te_count = node->getObject()->getNumTEs(); - // map from texture ID to list of faces using it - typedef std::map< LLUUID, std::vector > map_t; - map_t faces_per_texture; - for (U8 i = 0; i < te_count; i++) - { - if (!node->isTESelected(i)) continue; - - LLViewerTexture* img = node->getObject()->getTEImage(i); - LLUUID image_id = img->getID(); - faces_per_texture[image_id].push_back(i); - } - // Per-texture, dump which faces are using it. - map_t::iterator it; - for (it = faces_per_texture.begin(); it != faces_per_texture.end(); ++it) - { - LLUUID image_id = it->first; - U8 te = it->second[0]; - LLViewerTexture* img = node->getObject()->getTEImage(te); - S32 height = img->getHeight(); - S32 width = img->getWidth(); - S32 components = img->getComponents(); - msg = llformat("%dx%d %s on face ", - width, - height, - (components == 4 ? "alpha" : "opaque")); - for (U8 i = 0; i < it->second.size(); ++i) - { - msg.append( llformat("%d ", (S32)(it->second[i]))); - } - - LLSD args; - args["MESSAGE"] = msg; - LLNotificationsUtil::add("SystemMessage", args); - } - } -} - -void handle_test_male(void*) -{ - LLAppearanceMgr::instance().wearOutfitByName("Male Shape & Outfit"); - //gGestureList.requestResetFromServer( TRUE ); -} - -void handle_test_female(void*) -{ - LLAppearanceMgr::instance().wearOutfitByName("Female Shape & Outfit"); - //gGestureList.requestResetFromServer( FALSE ); -} - -void handle_toggle_pg(void*) -{ - gAgent.setTeen( !gAgent.isTeen() ); - - LLFloaterWorldMap::reloadIcons(NULL); - - llinfos << "PG status set to " << (S32)gAgent.isTeen() << llendl; -} - -void handle_dump_attachments(void*) -{ - if(!isAgentAvatarValid()) return; - - for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); - iter != gAgentAvatarp->mAttachmentPoints.end(); ) - { - LLVOAvatar::attachment_map_t::iterator curiter = iter++; - LLViewerJointAttachment* attachment = curiter->second; - S32 key = curiter->first; - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) - { - LLViewerObject *attached_object = (*attachment_iter); - BOOL visible = (attached_object != NULL && - attached_object->mDrawable.notNull() && - !attached_object->mDrawable->isRenderType(0)); - LLVector3 pos; - if (visible) pos = attached_object->mDrawable->getPosition(); - llinfos << "ATTACHMENT " << key << ": item_id=" << attached_object->getAttachmentItemID() - << (attached_object ? " present " : " absent ") - << (visible ? "visible " : "invisible ") - << " at " << pos - << " and " << (visible ? attached_object->getPosition() : LLVector3::zero) - << llendl; - } - } -} - - -// these are used in the gl menus to set control values, generically. -class LLToggleControl : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string control_name = userdata.asString(); - BOOL checked = gSavedSettings.getBOOL( control_name ); - gSavedSettings.setBOOL( control_name, !checked ); - return true; - } -}; - -class LLCheckControl : public view_listener_t -{ - bool handleEvent( const LLSD& userdata) - { - std::string callback_data = userdata.asString(); - bool new_value = gSavedSettings.getBOOL(callback_data); - return new_value; - } -}; - -// not so generic - -class LLAdvancedCheckRenderShadowOption: public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string control_name = userdata.asString(); - S32 current_shadow_level = gSavedSettings.getS32(control_name); - if (current_shadow_level == 0) // is off - { - return false; - } - else // is on - { - return true; - } - } -}; - -class LLAdvancedClickRenderShadowOption: public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string control_name = userdata.asString(); - S32 current_shadow_level = gSavedSettings.getS32(control_name); - if (current_shadow_level == 0) // upgrade to level 2 - { - gSavedSettings.setS32(control_name, 2); - } - else // downgrade to level 0 - { - gSavedSettings.setS32(control_name, 0); - } - return true; - } -}; - -void menu_toggle_attached_lights(void* user_data) -{ - LLPipeline::sRenderAttachedLights = gSavedSettings.getBOOL("RenderAttachedLights"); -} - -void menu_toggle_attached_particles(void* user_data) -{ - LLPipeline::sRenderAttachedParticles = gSavedSettings.getBOOL("RenderAttachedParticles"); -} - -class LLAdvancedHandleAttachedLightParticles: public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string control_name = userdata.asString(); - - // toggle the control - gSavedSettings.setBOOL(control_name, - !gSavedSettings.getBOOL(control_name)); - - // update internal flags - if (control_name == "RenderAttachedLights") - { - menu_toggle_attached_lights(NULL); - } - else if (control_name == "RenderAttachedParticles") - { - menu_toggle_attached_particles(NULL); - } - return true; - } -}; - -class LLSomethingSelected : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = !(LLSelectMgr::getInstance()->getSelection()->isEmpty()); - return new_value; - } -}; - -class LLSomethingSelectedNoHUD : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); - bool new_value = !(selection->isEmpty()) && !(selection->getSelectType() == SELECT_TYPE_HUD); - return new_value; - } -}; - -static bool is_editable_selected() -{ - return (LLSelectMgr::getInstance()->getSelection()->getFirstEditableObject() != NULL); -} - -class LLEditableSelected : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - return is_editable_selected(); - } -}; - -class LLEditableSelectedMono : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = false; - LLViewerRegion* region = gAgent.getRegion(); - if(region && gMenuHolder) - { - bool have_cap = (! region->getCapability("UpdateScriptTask").empty()); - new_value = is_editable_selected() && have_cap; - } - return new_value; - } -}; - -bool enable_object_take_copy() -{ - bool all_valid = false; - if (LLSelectMgr::getInstance()) - { - if (!LLSelectMgr::getInstance()->getSelection()->isEmpty()) - { - all_valid = true; -#ifndef HACKED_GODLIKE_VIEWER -# ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (LLGridManager::getInstance()->isInProductionGrid() - || !gAgent.isGodlike()) -# endif - { - struct f : public LLSelectedObjectFunctor - { - virtual bool apply(LLViewerObject* obj) - { - return (!obj->permCopy() || obj->isAttachment()); - } - } func; - const bool firstonly = true; - bool any_invalid = LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, firstonly); - all_valid = !any_invalid; - } -#endif // HACKED_GODLIKE_VIEWER - } - } - - return all_valid; -} - - -class LLHasAsset : public LLInventoryCollectFunctor -{ -public: - LLHasAsset(const LLUUID& id) : mAssetID(id), mHasAsset(FALSE) {} - virtual ~LLHasAsset() {} - virtual bool operator()(LLInventoryCategory* cat, - LLInventoryItem* item); - BOOL hasAsset() const { return mHasAsset; } - -protected: - LLUUID mAssetID; - BOOL mHasAsset; -}; - -bool LLHasAsset::operator()(LLInventoryCategory* cat, - LLInventoryItem* item) -{ - if(item && item->getAssetUUID() == mAssetID) - { - mHasAsset = TRUE; - } - return FALSE; -} - -BOOL enable_save_into_inventory(void*) -{ - // *TODO: clean this up - // find the last root - LLSelectNode* last_node = NULL; - for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); - iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++) - { - last_node = *iter; - } - -#ifdef HACKED_GODLIKE_VIEWER - return TRUE; -#else -# ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLGridManager::getInstance()->isInProductionGrid() - && gAgent.isGodlike()) - { - return TRUE; - } -# endif - // check all pre-req's for save into inventory. - if(last_node && last_node->mValid && !last_node->mItemID.isNull() - && (last_node->mPermissions->getOwner() == gAgent.getID()) - && (gInventory.getItem(last_node->mItemID) != NULL)) - { - LLViewerObject* obj = last_node->getObject(); - if( obj && !obj->isAttachment() ) - { - return TRUE; - } - } -#endif - return FALSE; -} - -class LLToolsEnableSaveToInventory : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = enable_save_into_inventory(NULL); - return new_value; - } -}; - -BOOL enable_save_into_task_inventory(void*) -{ - LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); - if(node && (node->mValid) && (!node->mFromTaskID.isNull())) - { - // *TODO: check to see if the fromtaskid object exists. - LLViewerObject* obj = node->getObject(); - if( obj && !obj->isAttachment() ) - { - return TRUE; - } - } - return FALSE; -} - -class LLToolsEnableSaveToObjectInventory : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = enable_save_into_task_inventory(NULL); - return new_value; - } -}; - - -class LLViewEnableMouselook : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - // You can't go directly from customize avatar to mouselook. - // TODO: write code with appropriate dialogs to handle this transition. - bool new_value = (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgentCamera.getCameraMode() && !gSavedSettings.getBOOL("FreezeTime")); - return new_value; - } -}; - -class LLToolsEnableToolNotPie : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = ( LLToolMgr::getInstance()->getBaseTool() != LLToolPie::getInstance() ); - return new_value; - } -}; - -class LLWorldEnableCreateLandmark : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - return !LLLandmarkActions::landmarkAlreadyExists(); - } -}; - -class LLWorldEnableSetHomeLocation : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = gAgent.isGodlike() || - (gAgent.getRegion() && gAgent.getRegion()->getAllowSetHome()); - return new_value; - } -}; - -class LLWorldEnableTeleportHome : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLViewerRegion* regionp = gAgent.getRegion(); - bool agent_on_prelude = (regionp && regionp->isPrelude()); - bool enable_teleport_home = gAgent.isGodlike() || !agent_on_prelude; - return enable_teleport_home; - } -}; - -BOOL enable_god_full(void*) -{ - return gAgent.getGodLevel() >= GOD_FULL; -} - -BOOL enable_god_liaison(void*) -{ - return gAgent.getGodLevel() >= GOD_LIAISON; -} - -bool is_god_customer_service() -{ - return gAgent.getGodLevel() >= GOD_CUSTOMER_SERVICE; -} - -BOOL enable_god_basic(void*) -{ - return gAgent.getGodLevel() > GOD_NOT; -} - - -void toggle_show_xui_names(void *) -{ - gSavedSettings.setBOOL("DebugShowXUINames", !gSavedSettings.getBOOL("DebugShowXUINames")); -} - -BOOL check_show_xui_names(void *) -{ - return gSavedSettings.getBOOL("DebugShowXUINames"); -} - -class LLToolsSelectOnlyMyObjects : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - BOOL cur_val = gSavedSettings.getBOOL("SelectOwnedOnly"); - - gSavedSettings.setBOOL("SelectOwnedOnly", ! cur_val ); - - return true; - } -}; - -class LLToolsSelectOnlyMovableObjects : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - BOOL cur_val = gSavedSettings.getBOOL("SelectMovableOnly"); - - gSavedSettings.setBOOL("SelectMovableOnly", ! cur_val ); - - return true; - } -}; - -class LLToolsSelectBySurrounding : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLSelectMgr::sRectSelectInclusive = !LLSelectMgr::sRectSelectInclusive; - - gSavedSettings.setBOOL("RectangleSelectInclusive", LLSelectMgr::sRectSelectInclusive); - return true; - } -}; - -class LLToolsShowHiddenSelection : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - // TomY TODO Merge these - LLSelectMgr::sRenderHiddenSelections = !LLSelectMgr::sRenderHiddenSelections; - - gSavedSettings.setBOOL("RenderHiddenSelections", LLSelectMgr::sRenderHiddenSelections); - return true; - } -}; - -class LLToolsShowSelectionLightRadius : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - // TomY TODO merge these - LLSelectMgr::sRenderLightRadius = !LLSelectMgr::sRenderLightRadius; - - gSavedSettings.setBOOL("RenderLightRadius", LLSelectMgr::sRenderLightRadius); - return true; - } -}; - -class LLToolsEditLinkedParts : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - BOOL select_individuals = !gSavedSettings.getBOOL("EditLinkedParts"); - gSavedSettings.setBOOL( "EditLinkedParts", select_individuals ); - if (select_individuals) - { - LLSelectMgr::getInstance()->demoteSelectionToIndividuals(); - } - else - { - LLSelectMgr::getInstance()->promoteSelectionToRoot(); - } - return true; - } -}; - -void reload_vertex_shader(void *) -{ - //THIS WOULD BE AN AWESOME PLACE TO RELOAD SHADERS... just a thought - DaveP -} - -void handle_dump_avatar_local_textures(void*) -{ - gAgentAvatarp->dumpLocalTextures(); -} - -void handle_dump_timers() -{ - LLFastTimer::dumpCurTimes(); -} - -void handle_debug_avatar_textures(void*) -{ - LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); - if (objectp) - { - LLFloaterReg::showInstance( "avatar_textures", LLSD(objectp->getID()) ); - } -} - -void handle_grab_baked_texture(void* data) -{ - EBakedTextureIndex baked_tex_index = (EBakedTextureIndex)((intptr_t)data); - if (!isAgentAvatarValid()) return; - - const LLUUID& asset_id = gAgentAvatarp->grabBakedTexture(baked_tex_index); - LL_INFOS("texture") << "Adding baked texture " << asset_id << " to inventory." << llendl; - LLAssetType::EType asset_type = LLAssetType::AT_TEXTURE; - LLInventoryType::EType inv_type = LLInventoryType::IT_TEXTURE; - const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(asset_type)); - if(folder_id.notNull()) - { - std::string name; - name = "Baked " + LLVOAvatarDictionary::getInstance()->getBakedTexture(baked_tex_index)->mNameCapitalized + " Texture"; - - LLUUID item_id; - item_id.generate(); - LLPermissions perm; - perm.init(gAgentID, - gAgentID, - LLUUID::null, - LLUUID::null); - U32 next_owner_perm = PERM_MOVE | PERM_TRANSFER; - perm.initMasks(PERM_ALL, - PERM_ALL, - PERM_NONE, - PERM_NONE, - next_owner_perm); - time_t creation_date_now = time_corrected(); - LLPointer item - = new LLViewerInventoryItem(item_id, - folder_id, - perm, - asset_id, - asset_type, - inv_type, - name, - LLStringUtil::null, - LLSaleInfo::DEFAULT, - LLInventoryItemFlags::II_FLAGS_NONE, - creation_date_now); - - item->updateServer(TRUE); - gInventory.updateItem(item); - gInventory.notifyObservers(); - - // Show the preview panel for textures to let - // user know that the image is now in inventory. - LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); - if(active_panel) - { - LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus(); - - active_panel->setSelection(item_id, TAKE_FOCUS_NO); - active_panel->openSelected(); - //LLFloaterInventory::dumpSelectionInformation((void*)view); - // restore keyboard focus - gFocusMgr.setKeyboardFocus(focus_ctrl); - } - } - else - { - llwarns << "Can't find a folder to put it in" << llendl; - } -} - -BOOL enable_grab_baked_texture(void* data) -{ - EBakedTextureIndex index = (EBakedTextureIndex)((intptr_t)data); - if (isAgentAvatarValid()) - { - return gAgentAvatarp->canGrabBakedTexture(index); - } - return FALSE; -} - -// Returns a pointer to the avatar give the UUID of the avatar OR of an attachment the avatar is wearing. -// Returns NULL on failure. -LLVOAvatar* find_avatar_from_object( LLViewerObject* object ) -{ - if (object) - { - if( object->isAttachment() ) - { - do - { - object = (LLViewerObject*) object->getParent(); - } - while( object && !object->isAvatar() ); - } - else if( !object->isAvatar() ) - { - object = NULL; - } - } - - return (LLVOAvatar*) object; -} - - -// Returns a pointer to the avatar give the UUID of the avatar OR of an attachment the avatar is wearing. -// Returns NULL on failure. -LLVOAvatar* find_avatar_from_object( const LLUUID& object_id ) -{ - return find_avatar_from_object( gObjectList.findObject(object_id) ); -} - - -void handle_disconnect_viewer(void *) -{ - LLAppViewer::instance()->forceDisconnect(LLTrans::getString("TestingDisconnect")); -} - -void force_error_breakpoint(void *) -{ - LLAppViewer::instance()->forceErrorBreakpoint(); -} - -void force_error_llerror(void *) -{ - LLAppViewer::instance()->forceErrorLLError(); -} - -void force_error_bad_memory_access(void *) -{ - LLAppViewer::instance()->forceErrorBadMemoryAccess(); -} - -void force_error_infinite_loop(void *) -{ - LLAppViewer::instance()->forceErrorInfiniteLoop(); -} - -void force_error_software_exception(void *) -{ - LLAppViewer::instance()->forceErrorSoftwareException(); -} - -void force_error_driver_crash(void *) -{ - LLAppViewer::instance()->forceErrorDriverCrash(); -} - -class LLToolsUseSelectionForGrid : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLSelectMgr::getInstance()->clearGridObjects(); - struct f : public LLSelectedObjectFunctor - { - virtual bool apply(LLViewerObject* objectp) - { - LLSelectMgr::getInstance()->addGridObject(objectp); - return true; - } - } func; - LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func); - LLSelectMgr::getInstance()->setGridMode(GRID_MODE_REF_OBJECT); - if (gFloaterTools) - { - gFloaterTools->mComboGridMode->setCurrentByIndex((S32)GRID_MODE_REF_OBJECT); - } - return true; - } -}; - -void handle_test_load_url(void*) -{ - LLWeb::loadURL(""); - LLWeb::loadURL("hacker://www.google.com/"); - LLWeb::loadURL("http"); - LLWeb::loadURL("http://www.google.com/"); -} - -// -// LLViewerMenuHolderGL -// -static LLDefaultChildRegistry::Register r("menu_holder"); - -LLViewerMenuHolderGL::LLViewerMenuHolderGL(const LLViewerMenuHolderGL::Params& p) -: LLMenuHolderGL(p) -{} - -BOOL LLViewerMenuHolderGL::hideMenus() -{ - BOOL handled = LLMenuHolderGL::hideMenus(); - - // drop pie menu selection - mParcelSelection = NULL; - mObjectSelection = NULL; - - if (gMenuBarView) - { - gMenuBarView->clearHoverItem(); - gMenuBarView->resetMenuTrigger(); - } - - return handled; -} - -void LLViewerMenuHolderGL::setParcelSelection(LLSafeHandle selection) -{ - mParcelSelection = selection; -} - -void LLViewerMenuHolderGL::setObjectSelection(LLSafeHandle selection) -{ - mObjectSelection = selection; -} - - -const LLRect LLViewerMenuHolderGL::getMenuRect() const -{ - return LLRect(0, getRect().getHeight() - MENU_BAR_HEIGHT, getRect().getWidth(), STATUS_BAR_HEIGHT); -} - -void handle_web_browser_test(const LLSD& param) -{ - std::string url = param.asString(); - if (url.empty()) - { - url = "about:blank"; - } - LLWeb::loadURLInternal(url); -} - -void handle_web_content_test(const LLSD& param) -{ - std::string url = param.asString(); - LLWeb::loadWebURLInternal(url); -} - -void handle_buy_currency_test(void*) -{ - std::string url = - "http://sarahd-sl-13041.webdev.lindenlab.com/app/lindex/index.php?agent_id=[AGENT_ID]&secure_session_id=[SESSION_ID]&lang=[LANGUAGE]"; - - LLStringUtil::format_map_t replace; - replace["[AGENT_ID]"] = gAgent.getID().asString(); - replace["[SESSION_ID]"] = gAgent.getSecureSessionID().asString(); - replace["[LANGUAGE]"] = LLUI::getLanguage(); - LLStringUtil::format(url, replace); - - llinfos << "buy currency url " << url << llendl; - - LLFloaterReg::showInstance("buy_currency_html", LLSD(url)); -} - -void handle_rebake_textures(void*) -{ - if (!isAgentAvatarValid()) return; - - // Slam pending upload count to "unstick" things - bool slam_for_debug = true; - gAgentAvatarp->forceBakeAllTextures(slam_for_debug); -} - -void toggle_visibility(void* user_data) -{ - LLView* viewp = (LLView*)user_data; - viewp->setVisible(!viewp->getVisible()); -} - -BOOL get_visibility(void* user_data) -{ - LLView* viewp = (LLView*)user_data; - return viewp->getVisible(); -} - -// TomY TODO: Get rid of these? -class LLViewShowHoverTips : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - gSavedSettings.setBOOL("ShowHoverTips", !gSavedSettings.getBOOL("ShowHoverTips")); - return true; - } -}; - -class LLViewCheckShowHoverTips : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = gSavedSettings.getBOOL("ShowHoverTips"); - return new_value; - } -}; - -// TomY TODO: Get rid of these? -class LLViewHighlightTransparent : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLDrawPoolAlpha::sShowDebugAlpha = !LLDrawPoolAlpha::sShowDebugAlpha; - return true; - } -}; - -class LLViewCheckHighlightTransparent : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLDrawPoolAlpha::sShowDebugAlpha; - return new_value; - } -}; - -class LLViewBeaconWidth : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string width = userdata.asString(); - if(width == "1") - { - gSavedSettings.setS32("DebugBeaconLineWidth", 1); - } - else if(width == "4") - { - gSavedSettings.setS32("DebugBeaconLineWidth", 4); - } - else if(width == "16") - { - gSavedSettings.setS32("DebugBeaconLineWidth", 16); - } - else if(width == "32") - { - gSavedSettings.setS32("DebugBeaconLineWidth", 32); - } - - return true; - } -}; - - -class LLViewToggleBeacon : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string beacon = userdata.asString(); - if (beacon == "scriptsbeacon") - { - LLPipeline::toggleRenderScriptedBeacons(NULL); - gSavedSettings.setBOOL( "scriptsbeacon", LLPipeline::getRenderScriptedBeacons(NULL) ); - // toggle the other one off if it's on - if (LLPipeline::getRenderScriptedBeacons(NULL) && LLPipeline::getRenderScriptedTouchBeacons(NULL)) - { - LLPipeline::toggleRenderScriptedTouchBeacons(NULL); - gSavedSettings.setBOOL( "scripttouchbeacon", LLPipeline::getRenderScriptedTouchBeacons(NULL) ); - } - } - else if (beacon == "physicalbeacon") - { - LLPipeline::toggleRenderPhysicalBeacons(NULL); - gSavedSettings.setBOOL( "physicalbeacon", LLPipeline::getRenderPhysicalBeacons(NULL) ); - } - else if (beacon == "soundsbeacon") - { - LLPipeline::toggleRenderSoundBeacons(NULL); - gSavedSettings.setBOOL( "soundsbeacon", LLPipeline::getRenderSoundBeacons(NULL) ); - } - else if (beacon == "particlesbeacon") - { - LLPipeline::toggleRenderParticleBeacons(NULL); - gSavedSettings.setBOOL( "particlesbeacon", LLPipeline::getRenderParticleBeacons(NULL) ); - } - else if (beacon == "scripttouchbeacon") - { - LLPipeline::toggleRenderScriptedTouchBeacons(NULL); - gSavedSettings.setBOOL( "scripttouchbeacon", LLPipeline::getRenderScriptedTouchBeacons(NULL) ); - // toggle the other one off if it's on - if (LLPipeline::getRenderScriptedBeacons(NULL) && LLPipeline::getRenderScriptedTouchBeacons(NULL)) - { - LLPipeline::toggleRenderScriptedBeacons(NULL); - gSavedSettings.setBOOL( "scriptsbeacon", LLPipeline::getRenderScriptedBeacons(NULL) ); - } - } - else if (beacon == "renderbeacons") - { - LLPipeline::toggleRenderBeacons(NULL); - gSavedSettings.setBOOL( "renderbeacons", LLPipeline::getRenderBeacons(NULL) ); - // toggle the other one on if it's not - if (!LLPipeline::getRenderBeacons(NULL) && !LLPipeline::getRenderHighlights(NULL)) - { - LLPipeline::toggleRenderHighlights(NULL); - gSavedSettings.setBOOL( "renderhighlights", LLPipeline::getRenderHighlights(NULL) ); - } - } - else if (beacon == "renderhighlights") - { - LLPipeline::toggleRenderHighlights(NULL); - gSavedSettings.setBOOL( "renderhighlights", LLPipeline::getRenderHighlights(NULL) ); - // toggle the other one on if it's not - if (!LLPipeline::getRenderBeacons(NULL) && !LLPipeline::getRenderHighlights(NULL)) - { - LLPipeline::toggleRenderBeacons(NULL); - gSavedSettings.setBOOL( "renderbeacons", LLPipeline::getRenderBeacons(NULL) ); - } - } - - return true; - } -}; - -class LLViewCheckBeaconEnabled : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string beacon = userdata.asString(); - bool new_value = false; - if (beacon == "scriptsbeacon") - { - new_value = gSavedSettings.getBOOL( "scriptsbeacon"); - LLPipeline::setRenderScriptedBeacons(new_value); - } - else if (beacon == "physicalbeacon") - { - new_value = gSavedSettings.getBOOL( "physicalbeacon"); - LLPipeline::setRenderPhysicalBeacons(new_value); - } - else if (beacon == "soundsbeacon") - { - new_value = gSavedSettings.getBOOL( "soundsbeacon"); - LLPipeline::setRenderSoundBeacons(new_value); - } - else if (beacon == "particlesbeacon") - { - new_value = gSavedSettings.getBOOL( "particlesbeacon"); - LLPipeline::setRenderParticleBeacons(new_value); - } - else if (beacon == "scripttouchbeacon") - { - new_value = gSavedSettings.getBOOL( "scripttouchbeacon"); - LLPipeline::setRenderScriptedTouchBeacons(new_value); - } - else if (beacon == "renderbeacons") - { - new_value = gSavedSettings.getBOOL( "renderbeacons"); - LLPipeline::setRenderBeacons(new_value); - } - else if (beacon == "renderhighlights") - { - new_value = gSavedSettings.getBOOL( "renderhighlights"); - LLPipeline::setRenderHighlights(new_value); - } - return new_value; - } -}; - -class LLViewToggleRenderType : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string type = userdata.asString(); - if (type == "hideparticles") - { - LLPipeline::toggleRenderType(LLPipeline::RENDER_TYPE_PARTICLES); - } - return true; - } -}; - -class LLViewCheckRenderType : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string type = userdata.asString(); - bool new_value = false; - if (type == "hideparticles") - { - new_value = LLPipeline::toggleRenderTypeControlNegated((void *)LLPipeline::RENDER_TYPE_PARTICLES); - } - return new_value; - } -}; - -class LLViewShowHUDAttachments : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLPipeline::sShowHUDAttachments = !LLPipeline::sShowHUDAttachments; - return true; - } -}; - -class LLViewCheckHUDAttachments : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool new_value = LLPipeline::sShowHUDAttachments; - return new_value; - } -}; - -class LLEditEnableTakeOff : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string clothing = userdata.asString(); - LLWearableType::EType type = LLWearableType::typeNameToType(clothing); - if (type >= LLWearableType::WT_SHAPE && type < LLWearableType::WT_COUNT) - return LLAgentWearables::selfHasWearable(type); - return false; - } -}; - -class LLEditTakeOff : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string clothing = userdata.asString(); - if (clothing == "all") - LLWearableBridge::removeAllClothesFromAvatar(); - else - { - LLWearableType::EType type = LLWearableType::typeNameToType(clothing); - if (type >= LLWearableType::WT_SHAPE - && type < LLWearableType::WT_COUNT - && (gAgentWearables.getWearableCount(type) > 0)) - { - // MULTI-WEARABLES: assuming user wanted to remove top shirt. - U32 wearable_index = gAgentWearables.getWearableCount(type) - 1; - LLViewerInventoryItem *item = dynamic_cast(gAgentWearables.getWearableInventoryItem(type,wearable_index)); - LLWearableBridge::removeItemFromAvatar(item); - } - - } - return true; - } -}; - -class LLToolsSelectTool : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string tool_name = userdata.asString(); - if (tool_name == "focus") - { - LLToolMgr::getInstance()->getCurrentToolset()->selectToolByIndex(1); - } - else if (tool_name == "move") - { - LLToolMgr::getInstance()->getCurrentToolset()->selectToolByIndex(2); - } - else if (tool_name == "edit") - { - LLToolMgr::getInstance()->getCurrentToolset()->selectToolByIndex(3); - } - else if (tool_name == "create") - { - LLToolMgr::getInstance()->getCurrentToolset()->selectToolByIndex(4); - } - else if (tool_name == "land") - { - LLToolMgr::getInstance()->getCurrentToolset()->selectToolByIndex(5); - } - return true; - } -}; - -/// WINDLIGHT callbacks -class LLWorldEnvSettings : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - std::string tod = userdata.asString(); - LLVector3 sun_direction; - - if (tod == "editor") - { - // if not there or is hidden, show it - LLFloaterReg::toggleInstance("env_settings"); - return true; - } - - if (tod == "sunrise") - { - // set the value, turn off animation - LLWLParamManager::instance()->mAnimator.setDayTime(0.25); - LLWLParamManager::instance()->mAnimator.mIsRunning = false; - LLWLParamManager::instance()->mAnimator.mUseLindenTime = false; - - // then call update once - LLWLParamManager::instance()->mAnimator.update( - LLWLParamManager::instance()->mCurParams); - } - else if (tod == "noon") - { - // set the value, turn off animation - LLWLParamManager::instance()->mAnimator.setDayTime(0.567); - LLWLParamManager::instance()->mAnimator.mIsRunning = false; - LLWLParamManager::instance()->mAnimator.mUseLindenTime = false; - - // then call update once - LLWLParamManager::instance()->mAnimator.update( - LLWLParamManager::instance()->mCurParams); - } - else if (tod == "sunset") - { - // set the value, turn off animation - LLWLParamManager::instance()->mAnimator.setDayTime(0.75); - LLWLParamManager::instance()->mAnimator.mIsRunning = false; - LLWLParamManager::instance()->mAnimator.mUseLindenTime = false; - - // then call update once - LLWLParamManager::instance()->mAnimator.update( - LLWLParamManager::instance()->mCurParams); - } - else if (tod == "midnight") - { - // set the value, turn off animation - LLWLParamManager::instance()->mAnimator.setDayTime(0.0); - LLWLParamManager::instance()->mAnimator.mIsRunning = false; - LLWLParamManager::instance()->mAnimator.mUseLindenTime = false; - - // then call update once - LLWLParamManager::instance()->mAnimator.update( - LLWLParamManager::instance()->mCurParams); - } - else - { - LLWLParamManager::instance()->mAnimator.mIsRunning = true; - LLWLParamManager::instance()->mAnimator.mUseLindenTime = true; - } - return true; - } -}; - -/// Water Menu callbacks -class LLWorldWaterSettings : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLFloaterReg::toggleInstance("env_water"); - return true; - } -}; - -/// Post-Process callbacks -class LLWorldPostProcess : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLFloaterReg::showInstance("env_post_process"); - return true; - } -}; - -/// Day Cycle callbacks -class LLWorldDayCycle : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLFloaterReg::showInstance("env_day_cycle"); - return true; - } -}; - -class LLWorldToggleMovementControls : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLBottomTray::getInstance()->toggleMovementControls(); - return true; - } -}; - -class LLWorldToggleCameraControls : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLBottomTray::getInstance()->toggleCameraControls(); - return true; - } -}; - -void handle_flush_name_caches() -{ - // Toggle display names on and off to flush - bool use_display_names = LLAvatarNameCache::useDisplayNames(); - LLAvatarNameCache::setUseDisplayNames(!use_display_names); - LLAvatarNameCache::setUseDisplayNames(use_display_names); - - if (gCacheName) gCacheName->clear(); -} - -class LLUploadCostCalculator : public view_listener_t -{ - std::string mCostStr; - - bool handleEvent(const LLSD& userdata) - { - std::string menu_name = userdata.asString(); - gMenuHolder->childSetLabelArg(menu_name, "[COST]", mCostStr); - - return true; - } - - void calculateCost(); - -public: - LLUploadCostCalculator() - { - calculateCost(); - } -}; - -class LLToggleUIHints : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - bool ui_hints_enabled = gSavedSettings.getBOOL("EnableUIHints"); - // toggle - ui_hints_enabled = !ui_hints_enabled; - gSavedSettings.setBOOL("EnableUIHints", ui_hints_enabled); - return true; - } -}; - -void LLUploadCostCalculator::calculateCost() -{ - S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); - - // getPriceUpload() returns -1 if no data available yet. - if(upload_cost >= 0) - { - mCostStr = llformat("%d", upload_cost); - } - else - { - mCostStr = llformat("%d", gSavedSettings.getU32("DefaultUploadCost")); - } -} - -void show_navbar_context_menu(LLView* ctrl, S32 x, S32 y) -{ - static LLMenuGL* show_navbar_context_menu = LLUICtrlFactory::getInstance()->createFromFile("menu_hide_navbar.xml", - gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(gMenuHolder->hasVisibleMenu()) - { - gMenuHolder->hideMenus(); - } - show_navbar_context_menu->buildDrawLabels(); - show_navbar_context_menu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(ctrl, show_navbar_context_menu, x, y); -} - -void show_topinfobar_context_menu(LLView* ctrl, S32 x, S32 y) -{ - static LLMenuGL* show_topbarinfo_context_menu = LLUICtrlFactory::getInstance()->createFromFile("menu_topinfobar.xml", - gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - - LLMenuItemGL* landmark_item = show_topbarinfo_context_menu->getChild("Landmark"); - if (!LLLandmarkActions::landmarkAlreadyExists()) - { - landmark_item->setLabel(LLTrans::getString("AddLandmarkNavBarMenu")); - } - else - { - landmark_item->setLabel(LLTrans::getString("EditLandmarkNavBarMenu")); - } - - if(gMenuHolder->hasVisibleMenu()) - { - gMenuHolder->hideMenus(); - } - - show_topbarinfo_context_menu->buildDrawLabels(); - show_topbarinfo_context_menu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(ctrl, show_topbarinfo_context_menu, x, y); -} - -void initialize_edit_menu() -{ - view_listener_t::addMenu(new LLEditUndo(), "Edit.Undo"); - view_listener_t::addMenu(new LLEditRedo(), "Edit.Redo"); - view_listener_t::addMenu(new LLEditCut(), "Edit.Cut"); - view_listener_t::addMenu(new LLEditCopy(), "Edit.Copy"); - view_listener_t::addMenu(new LLEditPaste(), "Edit.Paste"); - view_listener_t::addMenu(new LLEditDelete(), "Edit.Delete"); - view_listener_t::addMenu(new LLEditSelectAll(), "Edit.SelectAll"); - view_listener_t::addMenu(new LLEditDeselect(), "Edit.Deselect"); - view_listener_t::addMenu(new LLEditDuplicate(), "Edit.Duplicate"); - view_listener_t::addMenu(new LLEditTakeOff(), "Edit.TakeOff"); - view_listener_t::addMenu(new LLEditEnableUndo(), "Edit.EnableUndo"); - view_listener_t::addMenu(new LLEditEnableRedo(), "Edit.EnableRedo"); - view_listener_t::addMenu(new LLEditEnableCut(), "Edit.EnableCut"); - view_listener_t::addMenu(new LLEditEnableCopy(), "Edit.EnableCopy"); - view_listener_t::addMenu(new LLEditEnablePaste(), "Edit.EnablePaste"); - view_listener_t::addMenu(new LLEditEnableDelete(), "Edit.EnableDelete"); - view_listener_t::addMenu(new LLEditEnableSelectAll(), "Edit.EnableSelectAll"); - view_listener_t::addMenu(new LLEditEnableDeselect(), "Edit.EnableDeselect"); - view_listener_t::addMenu(new LLEditEnableDuplicate(), "Edit.EnableDuplicate"); - -} - -void initialize_menus() -{ - // A parameterized event handler used as ctrl-8/9/0 zoom controls below. - class LLZoomer : public view_listener_t - { - public: - // The "mult" parameter says whether "val" is a multiplier or used to set the value. - LLZoomer(F32 val, bool mult=true) : mVal(val), mMult(mult) {} - bool handleEvent(const LLSD& userdata) - { - F32 new_fov_rad = mMult ? LLViewerCamera::getInstance()->getDefaultFOV() * mVal : mVal; - LLViewerCamera::getInstance()->setDefaultFOV(new_fov_rad); - gSavedSettings.setF32("CameraAngle", LLViewerCamera::getInstance()->getView()); // setView may have clamped it. - return true; - } - private: - F32 mVal; - bool mMult; - }; - - LLUICtrl::EnableCallbackRegistry::Registrar& enable = LLUICtrl::EnableCallbackRegistry::currentRegistrar(); - LLUICtrl::CommitCallbackRegistry::Registrar& commit = LLUICtrl::CommitCallbackRegistry::currentRegistrar(); - - // Generic enable and visible - // Don't prepend MenuName.Foo because these can be used in any menu. - enable.add("IsGodCustomerService", boost::bind(&is_god_customer_service)); - - view_listener_t::addEnable(new LLUploadCostCalculator(), "Upload.CalculateCosts"); - - // Agent - commit.add("Agent.toggleFlying", boost::bind(&LLAgent::toggleFlying)); - enable.add("Agent.enableFlying", boost::bind(&LLAgent::enableFlying)); - - // File menu - init_menu_file(); - - view_listener_t::addMenu(new LLEditEnableTakeOff(), "Edit.EnableTakeOff"); - view_listener_t::addMenu(new LLEditEnableCustomizeAvatar(), "Edit.EnableCustomizeAvatar"); - view_listener_t::addMenu(new LLEnableEditShape(), "Edit.EnableEditShape"); - commit.add("CustomizeAvatar", boost::bind(&handle_customize_avatar)); - commit.add("EditOutfit", boost::bind(&handle_edit_outfit)); - commit.add("EditShape", boost::bind(&handle_edit_shape)); - - // View menu - view_listener_t::addMenu(new LLViewMouselook(), "View.Mouselook"); - view_listener_t::addMenu(new LLViewJoystickFlycam(), "View.JoystickFlycam"); - view_listener_t::addMenu(new LLViewResetView(), "View.ResetView"); - view_listener_t::addMenu(new LLViewLookAtLastChatter(), "View.LookAtLastChatter"); - view_listener_t::addMenu(new LLViewShowHoverTips(), "View.ShowHoverTips"); - view_listener_t::addMenu(new LLViewHighlightTransparent(), "View.HighlightTransparent"); - view_listener_t::addMenu(new LLViewToggleRenderType(), "View.ToggleRenderType"); - view_listener_t::addMenu(new LLViewShowHUDAttachments(), "View.ShowHUDAttachments"); - view_listener_t::addMenu(new LLZoomer(1.2f), "View.ZoomOut"); - view_listener_t::addMenu(new LLZoomer(1/1.2f), "View.ZoomIn"); - view_listener_t::addMenu(new LLZoomer(DEFAULT_FIELD_OF_VIEW, false), "View.ZoomDefault"); - view_listener_t::addMenu(new LLViewDefaultUISize(), "View.DefaultUISize"); - - view_listener_t::addMenu(new LLViewEnableMouselook(), "View.EnableMouselook"); - view_listener_t::addMenu(new LLViewEnableJoystickFlycam(), "View.EnableJoystickFlycam"); - view_listener_t::addMenu(new LLViewEnableLastChatter(), "View.EnableLastChatter"); - - view_listener_t::addMenu(new LLViewCheckJoystickFlycam(), "View.CheckJoystickFlycam"); - view_listener_t::addMenu(new LLViewCheckShowHoverTips(), "View.CheckShowHoverTips"); - view_listener_t::addMenu(new LLViewCheckHighlightTransparent(), "View.CheckHighlightTransparent"); - view_listener_t::addMenu(new LLViewCheckRenderType(), "View.CheckRenderType"); - view_listener_t::addMenu(new LLViewCheckHUDAttachments(), "View.CheckHUDAttachments"); - - // Me > Movement - view_listener_t::addMenu(new LLAdvancedAgentFlyingInfo(), "Agent.getFlying"); - - // World menu - commit.add("World.Chat", boost::bind(&handle_chat, (void*)NULL)); - view_listener_t::addMenu(new LLWorldAlwaysRun(), "World.AlwaysRun"); - view_listener_t::addMenu(new LLWorldCreateLandmark(), "World.CreateLandmark"); - view_listener_t::addMenu(new LLWorldPlaceProfile(), "World.PlaceProfile"); - view_listener_t::addMenu(new LLWorldSetHomeLocation(), "World.SetHomeLocation"); - view_listener_t::addMenu(new LLWorldTeleportHome(), "World.TeleportHome"); - view_listener_t::addMenu(new LLWorldSetAway(), "World.SetAway"); - view_listener_t::addMenu(new LLWorldSetBusy(), "World.SetBusy"); - - view_listener_t::addMenu(new LLWorldEnableCreateLandmark(), "World.EnableCreateLandmark"); - view_listener_t::addMenu(new LLWorldEnableSetHomeLocation(), "World.EnableSetHomeLocation"); - view_listener_t::addMenu(new LLWorldEnableTeleportHome(), "World.EnableTeleportHome"); - view_listener_t::addMenu(new LLWorldEnableBuyLand(), "World.EnableBuyLand"); - - view_listener_t::addMenu(new LLWorldCheckAlwaysRun(), "World.CheckAlwaysRun"); - - view_listener_t::addMenu(new LLWorldEnvSettings(), "World.EnvSettings"); - view_listener_t::addMenu(new LLWorldWaterSettings(), "World.WaterSettings"); - view_listener_t::addMenu(new LLWorldPostProcess(), "World.PostProcess"); - view_listener_t::addMenu(new LLWorldDayCycle(), "World.DayCycle"); - - view_listener_t::addMenu(new LLWorldToggleMovementControls(), "World.Toggle.MovementControls"); - view_listener_t::addMenu(new LLWorldToggleCameraControls(), "World.Toggle.CameraControls"); - - // Tools menu - view_listener_t::addMenu(new LLToolsSelectTool(), "Tools.SelectTool"); - view_listener_t::addMenu(new LLToolsSelectOnlyMyObjects(), "Tools.SelectOnlyMyObjects"); - view_listener_t::addMenu(new LLToolsSelectOnlyMovableObjects(), "Tools.SelectOnlyMovableObjects"); - view_listener_t::addMenu(new LLToolsSelectBySurrounding(), "Tools.SelectBySurrounding"); - view_listener_t::addMenu(new LLToolsShowHiddenSelection(), "Tools.ShowHiddenSelection"); - view_listener_t::addMenu(new LLToolsShowSelectionLightRadius(), "Tools.ShowSelectionLightRadius"); - view_listener_t::addMenu(new LLToolsEditLinkedParts(), "Tools.EditLinkedParts"); - view_listener_t::addMenu(new LLToolsSnapObjectXY(), "Tools.SnapObjectXY"); - view_listener_t::addMenu(new LLToolsUseSelectionForGrid(), "Tools.UseSelectionForGrid"); - view_listener_t::addMenu(new LLToolsSelectNextPart(), "Tools.SelectNextPart"); - view_listener_t::addMenu(new LLToolsLink(), "Tools.Link"); - view_listener_t::addMenu(new LLToolsUnlink(), "Tools.Unlink"); - view_listener_t::addMenu(new LLToolsStopAllAnimations(), "Tools.StopAllAnimations"); - view_listener_t::addMenu(new LLToolsReleaseKeys(), "Tools.ReleaseKeys"); - view_listener_t::addMenu(new LLToolsEnableReleaseKeys(), "Tools.EnableReleaseKeys"); - commit.add("Tools.LookAtSelection", boost::bind(&handle_look_at_selection, _2)); - commit.add("Tools.BuyOrTake", boost::bind(&handle_buy_or_take)); - commit.add("Tools.TakeCopy", boost::bind(&handle_take_copy)); - view_listener_t::addMenu(new LLToolsSaveToInventory(), "Tools.SaveToInventory"); - view_listener_t::addMenu(new LLToolsSaveToObjectInventory(), "Tools.SaveToObjectInventory"); - view_listener_t::addMenu(new LLToolsSelectedScriptAction(), "Tools.SelectedScriptAction"); - - view_listener_t::addMenu(new LLToolsEnableToolNotPie(), "Tools.EnableToolNotPie"); - view_listener_t::addMenu(new LLToolsEnableSelectNextPart(), "Tools.EnableSelectNextPart"); - view_listener_t::addMenu(new LLToolsEnableLink(), "Tools.EnableLink"); - view_listener_t::addMenu(new LLToolsEnableUnlink(), "Tools.EnableUnlink"); - view_listener_t::addMenu(new LLToolsEnableBuyOrTake(), "Tools.EnableBuyOrTake"); - enable.add("Tools.EnableTakeCopy", boost::bind(&enable_object_take_copy)); - enable.add("Tools.VisibleBuyObject", boost::bind(&tools_visible_buy_object)); - enable.add("Tools.VisibleTakeObject", boost::bind(&tools_visible_take_object)); - view_listener_t::addMenu(new LLToolsEnableSaveToInventory(), "Tools.EnableSaveToInventory"); - view_listener_t::addMenu(new LLToolsEnableSaveToObjectInventory(), "Tools.EnableSaveToObjectInventory"); - - // Help menu - // most items use the ShowFloater method - - // Advanced menu - view_listener_t::addMenu(new LLAdvancedToggleConsole(), "Advanced.ToggleConsole"); - view_listener_t::addMenu(new LLAdvancedCheckConsole(), "Advanced.CheckConsole"); - view_listener_t::addMenu(new LLAdvancedDumpInfoToConsole(), "Advanced.DumpInfoToConsole"); - - // Advanced > HUD Info - view_listener_t::addMenu(new LLAdvancedToggleHUDInfo(), "Advanced.ToggleHUDInfo"); - view_listener_t::addMenu(new LLAdvancedCheckHUDInfo(), "Advanced.CheckHUDInfo"); - - // Advanced Other Settings - view_listener_t::addMenu(new LLAdvancedClearGroupCache(), "Advanced.ClearGroupCache"); - - // Advanced > Render > Types - view_listener_t::addMenu(new LLAdvancedToggleRenderType(), "Advanced.ToggleRenderType"); - view_listener_t::addMenu(new LLAdvancedCheckRenderType(), "Advanced.CheckRenderType"); - - //// Advanced > Render > Features - view_listener_t::addMenu(new LLAdvancedToggleFeature(), "Advanced.ToggleFeature"); - view_listener_t::addMenu(new LLAdvancedCheckFeature(), "Advanced.CheckFeature"); - // Advanced > Render > Info Displays - view_listener_t::addMenu(new LLAdvancedToggleInfoDisplay(), "Advanced.ToggleInfoDisplay"); - view_listener_t::addMenu(new LLAdvancedCheckInfoDisplay(), "Advanced.CheckInfoDisplay"); - view_listener_t::addMenu(new LLAdvancedSelectedTextureInfo(), "Advanced.SelectedTextureInfo"); - view_listener_t::addMenu(new LLAdvancedToggleWireframe(), "Advanced.ToggleWireframe"); - view_listener_t::addMenu(new LLAdvancedCheckWireframe(), "Advanced.CheckWireframe"); - // Develop > Render - view_listener_t::addMenu(new LLAdvancedToggleTextureAtlas(), "Advanced.ToggleTextureAtlas"); - view_listener_t::addMenu(new LLAdvancedCheckTextureAtlas(), "Advanced.CheckTextureAtlas"); - view_listener_t::addMenu(new LLAdvancedEnableObjectObjectOcclusion(), "Advanced.EnableObjectObjectOcclusion"); - view_listener_t::addMenu(new LLAdvancedEnableRenderFBO(), "Advanced.EnableRenderFBO"); - view_listener_t::addMenu(new LLAdvancedEnableRenderDeferred(), "Advanced.EnableRenderDeferred"); - view_listener_t::addMenu(new LLAdvancedEnableRenderDeferredOptions(), "Advanced.EnableRenderDeferredOptions"); - view_listener_t::addMenu(new LLAdvancedToggleRandomizeFramerate(), "Advanced.ToggleRandomizeFramerate"); - view_listener_t::addMenu(new LLAdvancedCheckRandomizeFramerate(), "Advanced.CheckRandomizeFramerate"); - view_listener_t::addMenu(new LLAdvancedTogglePeriodicSlowFrame(), "Advanced.TogglePeriodicSlowFrame"); - view_listener_t::addMenu(new LLAdvancedCheckPeriodicSlowFrame(), "Advanced.CheckPeriodicSlowFrame"); - view_listener_t::addMenu(new LLAdvancedVectorizePerfTest(), "Advanced.VectorizePerfTest"); - view_listener_t::addMenu(new LLAdvancedToggleFrameTest(), "Advanced.ToggleFrameTest"); - view_listener_t::addMenu(new LLAdvancedCheckFrameTest(), "Advanced.CheckFrameTest"); - view_listener_t::addMenu(new LLAdvancedHandleAttachedLightParticles(), "Advanced.HandleAttachedLightParticles"); - view_listener_t::addMenu(new LLAdvancedCheckRenderShadowOption(), "Advanced.CheckRenderShadowOption"); - view_listener_t::addMenu(new LLAdvancedClickRenderShadowOption(), "Advanced.ClickRenderShadowOption"); - - - #ifdef TOGGLE_HACKED_GODLIKE_VIEWER - view_listener_t::addMenu(new LLAdvancedHandleToggleHackedGodmode(), "Advanced.HandleToggleHackedGodmode"); - view_listener_t::addMenu(new LLAdvancedCheckToggleHackedGodmode(), "Advanced.CheckToggleHackedGodmode"); - view_listener_t::addMenu(new LLAdvancedEnableToggleHackedGodmode(), "Advanced.EnableToggleHackedGodmode"); - #endif - - // Advanced > World - view_listener_t::addMenu(new LLAdvancedDumpScriptedCamera(), "Advanced.DumpScriptedCamera"); - view_listener_t::addMenu(new LLAdvancedDumpRegionObjectCache(), "Advanced.DumpRegionObjectCache"); - - // Advanced > UI - commit.add("Advanced.WebBrowserTest", boost::bind(&handle_web_browser_test, _2)); // sigh! this one opens the MEDIA browser - commit.add("Advanced.WebContentTest", boost::bind(&handle_web_content_test, _2)); // this one opens the Web Content floater - view_listener_t::addMenu(new LLAdvancedBuyCurrencyTest(), "Advanced.BuyCurrencyTest"); - view_listener_t::addMenu(new LLAdvancedDumpSelectMgr(), "Advanced.DumpSelectMgr"); - view_listener_t::addMenu(new LLAdvancedDumpInventory(), "Advanced.DumpInventory"); - commit.add("Advanced.DumpTimers", boost::bind(&handle_dump_timers) ); - commit.add("Advanced.DumpFocusHolder", boost::bind(&handle_dump_focus) ); - view_listener_t::addMenu(new LLAdvancedPrintSelectedObjectInfo(), "Advanced.PrintSelectedObjectInfo"); - view_listener_t::addMenu(new LLAdvancedPrintAgentInfo(), "Advanced.PrintAgentInfo"); - view_listener_t::addMenu(new LLAdvancedPrintTextureMemoryStats(), "Advanced.PrintTextureMemoryStats"); - view_listener_t::addMenu(new LLAdvancedToggleDebugClicks(), "Advanced.ToggleDebugClicks"); - view_listener_t::addMenu(new LLAdvancedCheckDebugClicks(), "Advanced.CheckDebugClicks"); - view_listener_t::addMenu(new LLAdvancedCheckDebugViews(), "Advanced.CheckDebugViews"); - view_listener_t::addMenu(new LLAdvancedToggleDebugViews(), "Advanced.ToggleDebugViews"); - view_listener_t::addMenu(new LLAdvancedToggleXUINameTooltips(), "Advanced.ToggleXUINameTooltips"); - view_listener_t::addMenu(new LLAdvancedCheckXUINameTooltips(), "Advanced.CheckXUINameTooltips"); - view_listener_t::addMenu(new LLAdvancedToggleDebugMouseEvents(), "Advanced.ToggleDebugMouseEvents"); - view_listener_t::addMenu(new LLAdvancedCheckDebugMouseEvents(), "Advanced.CheckDebugMouseEvents"); - view_listener_t::addMenu(new LLAdvancedToggleDebugKeys(), "Advanced.ToggleDebugKeys"); - view_listener_t::addMenu(new LLAdvancedCheckDebugKeys(), "Advanced.CheckDebugKeys"); - view_listener_t::addMenu(new LLAdvancedToggleDebugWindowProc(), "Advanced.ToggleDebugWindowProc"); - view_listener_t::addMenu(new LLAdvancedCheckDebugWindowProc(), "Advanced.CheckDebugWindowProc"); - commit.add("Advanced.ShowSideTray", boost::bind(&handle_show_side_tray)); - - // Advanced > XUI - commit.add("Advanced.ReloadColorSettings", boost::bind(&LLUIColorTable::loadFromSettings, LLUIColorTable::getInstance())); - view_listener_t::addMenu(new LLAdvancedToggleXUINames(), "Advanced.ToggleXUINames"); - view_listener_t::addMenu(new LLAdvancedCheckXUINames(), "Advanced.CheckXUINames"); - view_listener_t::addMenu(new LLAdvancedSendTestIms(), "Advanced.SendTestIMs"); - commit.add("Advanced.FlushNameCaches", boost::bind(&handle_flush_name_caches)); - - // Advanced > Character > Grab Baked Texture - view_listener_t::addMenu(new LLAdvancedGrabBakedTexture(), "Advanced.GrabBakedTexture"); - view_listener_t::addMenu(new LLAdvancedEnableGrabBakedTexture(), "Advanced.EnableGrabBakedTexture"); - - // Advanced > Character > Character Tests - view_listener_t::addMenu(new LLAdvancedAppearanceToXML(), "Advanced.AppearanceToXML"); - view_listener_t::addMenu(new LLAdvancedToggleCharacterGeometry(), "Advanced.ToggleCharacterGeometry"); - - view_listener_t::addMenu(new LLAdvancedTestMale(), "Advanced.TestMale"); - view_listener_t::addMenu(new LLAdvancedTestFemale(), "Advanced.TestFemale"); - view_listener_t::addMenu(new LLAdvancedTogglePG(), "Advanced.TogglePG"); - - // Advanced > Character (toplevel) - view_listener_t::addMenu(new LLAdvancedForceParamsToDefault(), "Advanced.ForceParamsToDefault"); - view_listener_t::addMenu(new LLAdvancedReloadVertexShader(), "Advanced.ReloadVertexShader"); - view_listener_t::addMenu(new LLAdvancedToggleAnimationInfo(), "Advanced.ToggleAnimationInfo"); - view_listener_t::addMenu(new LLAdvancedCheckAnimationInfo(), "Advanced.CheckAnimationInfo"); - view_listener_t::addMenu(new LLAdvancedToggleShowLookAt(), "Advanced.ToggleShowLookAt"); - view_listener_t::addMenu(new LLAdvancedCheckShowLookAt(), "Advanced.CheckShowLookAt"); - view_listener_t::addMenu(new LLAdvancedToggleShowPointAt(), "Advanced.ToggleShowPointAt"); - view_listener_t::addMenu(new LLAdvancedCheckShowPointAt(), "Advanced.CheckShowPointAt"); - view_listener_t::addMenu(new LLAdvancedToggleDebugJointUpdates(), "Advanced.ToggleDebugJointUpdates"); - view_listener_t::addMenu(new LLAdvancedCheckDebugJointUpdates(), "Advanced.CheckDebugJointUpdates"); - view_listener_t::addMenu(new LLAdvancedToggleDisableLOD(), "Advanced.ToggleDisableLOD"); - view_listener_t::addMenu(new LLAdvancedCheckDisableLOD(), "Advanced.CheckDisableLOD"); - view_listener_t::addMenu(new LLAdvancedToggleDebugCharacterVis(), "Advanced.ToggleDebugCharacterVis"); - view_listener_t::addMenu(new LLAdvancedCheckDebugCharacterVis(), "Advanced.CheckDebugCharacterVis"); - view_listener_t::addMenu(new LLAdvancedDumpAttachments(), "Advanced.DumpAttachments"); - view_listener_t::addMenu(new LLAdvancedRebakeTextures(), "Advanced.RebakeTextures"); - view_listener_t::addMenu(new LLAdvancedDebugAvatarTextures(), "Advanced.DebugAvatarTextures"); - view_listener_t::addMenu(new LLAdvancedDumpAvatarLocalTextures(), "Advanced.DumpAvatarLocalTextures"); - // Advanced > Network - view_listener_t::addMenu(new LLAdvancedEnableMessageLog(), "Advanced.EnableMessageLog"); - view_listener_t::addMenu(new LLAdvancedDisableMessageLog(), "Advanced.DisableMessageLog"); - view_listener_t::addMenu(new LLAdvancedDropPacket(), "Advanced.DropPacket"); - - // Advanced > Recorder - view_listener_t::addMenu(new LLAdvancedAgentPilot(), "Advanced.AgentPilot"); - view_listener_t::addMenu(new LLAdvancedToggleAgentPilotLoop(), "Advanced.ToggleAgentPilotLoop"); - view_listener_t::addMenu(new LLAdvancedCheckAgentPilotLoop(), "Advanced.CheckAgentPilotLoop"); - - // Advanced > Debugging - view_listener_t::addMenu(new LLAdvancedForceErrorBreakpoint(), "Advanced.ForceErrorBreakpoint"); - view_listener_t::addMenu(new LLAdvancedForceErrorLlerror(), "Advanced.ForceErrorLlerror"); - view_listener_t::addMenu(new LLAdvancedForceErrorBadMemoryAccess(), "Advanced.ForceErrorBadMemoryAccess"); - view_listener_t::addMenu(new LLAdvancedForceErrorInfiniteLoop(), "Advanced.ForceErrorInfiniteLoop"); - view_listener_t::addMenu(new LLAdvancedForceErrorSoftwareException(), "Advanced.ForceErrorSoftwareException"); - view_listener_t::addMenu(new LLAdvancedForceErrorDriverCrash(), "Advanced.ForceErrorDriverCrash"); - view_listener_t::addMenu(new LLAdvancedForceErrorDisconnectViewer(), "Advanced.ForceErrorDisconnectViewer"); - - // Advanced (toplevel) - view_listener_t::addMenu(new LLAdvancedToggleShowObjectUpdates(), "Advanced.ToggleShowObjectUpdates"); - view_listener_t::addMenu(new LLAdvancedCheckShowObjectUpdates(), "Advanced.CheckShowObjectUpdates"); - view_listener_t::addMenu(new LLAdvancedCompressImage(), "Advanced.CompressImage"); - view_listener_t::addMenu(new LLAdvancedShowDebugSettings(), "Advanced.ShowDebugSettings"); - view_listener_t::addMenu(new LLAdvancedEnableViewAdminOptions(), "Advanced.EnableViewAdminOptions"); - view_listener_t::addMenu(new LLAdvancedToggleViewAdminOptions(), "Advanced.ToggleViewAdminOptions"); - view_listener_t::addMenu(new LLAdvancedCheckViewAdminOptions(), "Advanced.CheckViewAdminOptions"); - view_listener_t::addMenu(new LLAdvancedRequestAdminStatus(), "Advanced.RequestAdminStatus"); - view_listener_t::addMenu(new LLAdvancedLeaveAdminStatus(), "Advanced.LeaveAdminStatus"); - - - // Admin >Object - view_listener_t::addMenu(new LLAdminForceTakeCopy(), "Admin.ForceTakeCopy"); - view_listener_t::addMenu(new LLAdminHandleObjectOwnerSelf(), "Admin.HandleObjectOwnerSelf"); - view_listener_t::addMenu(new LLAdminHandleObjectOwnerPermissive(), "Admin.HandleObjectOwnerPermissive"); - view_listener_t::addMenu(new LLAdminHandleForceDelete(), "Admin.HandleForceDelete"); - view_listener_t::addMenu(new LLAdminHandleObjectLock(), "Admin.HandleObjectLock"); - view_listener_t::addMenu(new LLAdminHandleObjectAssetIDs(), "Admin.HandleObjectAssetIDs"); - - // Admin >Parcel - view_listener_t::addMenu(new LLAdminHandleForceParcelOwnerToMe(), "Admin.HandleForceParcelOwnerToMe"); - view_listener_t::addMenu(new LLAdminHandleForceParcelToContent(), "Admin.HandleForceParcelToContent"); - view_listener_t::addMenu(new LLAdminHandleClaimPublicLand(), "Admin.HandleClaimPublicLand"); - - // Admin >Region - view_listener_t::addMenu(new LLAdminHandleRegionDumpTempAssetData(), "Admin.HandleRegionDumpTempAssetData"); - // Admin top level - view_listener_t::addMenu(new LLAdminOnSaveState(), "Admin.OnSaveState"); - - // Self context menu - view_listener_t::addMenu(new LLSelfStandUp(), "Self.StandUp"); - enable.add("Self.EnableStandUp", boost::bind(&enable_standup_self)); - view_listener_t::addMenu(new LLSelfSitDown(), "Self.SitDown"); - enable.add("Self.EnableSitDown", boost::bind(&enable_sitdown_self)); - view_listener_t::addMenu(new LLSelfRemoveAllAttachments(), "Self.RemoveAllAttachments"); - - view_listener_t::addMenu(new LLSelfEnableRemoveAllAttachments(), "Self.EnableRemoveAllAttachments"); - - // we don't use boost::bind directly to delay side tray construction - view_listener_t::addMenu( new LLTogglePanelPeopleTab(), "SideTray.PanelPeopleTab"); - - // Avatar pie menu - view_listener_t::addMenu(new LLObjectMute(), "Avatar.Mute"); - view_listener_t::addMenu(new LLAvatarAddFriend(), "Avatar.AddFriend"); - view_listener_t::addMenu(new LLAvatarAddContact(), "Avatar.AddContact"); - commit.add("Avatar.Freeze", boost::bind(&handle_avatar_freeze, LLSD())); - view_listener_t::addMenu(new LLAvatarDebug(), "Avatar.Debug"); - view_listener_t::addMenu(new LLAvatarVisibleDebug(), "Avatar.VisibleDebug"); - view_listener_t::addMenu(new LLAvatarInviteToGroup(), "Avatar.InviteToGroup"); - commit.add("Avatar.Eject", boost::bind(&handle_avatar_eject, LLSD())); - commit.add("Avatar.ShowInspector", boost::bind(&handle_avatar_show_inspector)); - view_listener_t::addMenu(new LLAvatarSendIM(), "Avatar.SendIM"); - view_listener_t::addMenu(new LLAvatarCall(), "Avatar.Call"); - enable.add("Avatar.EnableCall", boost::bind(&LLAvatarActions::canCall)); - view_listener_t::addMenu(new LLAvatarReportAbuse(), "Avatar.ReportAbuse"); - - view_listener_t::addMenu(new LLAvatarEnableAddFriend(), "Avatar.EnableAddFriend"); - enable.add("Avatar.EnableFreezeEject", boost::bind(&enable_freeze_eject, _2)); - - // Object pie menu - view_listener_t::addMenu(new LLObjectBuild(), "Object.Build"); - commit.add("Object.Touch", boost::bind(&handle_object_touch)); - commit.add("Object.SitOrStand", boost::bind(&handle_object_sit_or_stand)); - commit.add("Object.Delete", boost::bind(&handle_object_delete)); - view_listener_t::addMenu(new LLObjectAttachToAvatar(true), "Object.AttachToAvatar"); - view_listener_t::addMenu(new LLObjectAttachToAvatar(false), "Object.AttachAddToAvatar"); - view_listener_t::addMenu(new LLObjectReturn(), "Object.Return"); - view_listener_t::addMenu(new LLObjectReportAbuse(), "Object.ReportAbuse"); - view_listener_t::addMenu(new LLObjectMute(), "Object.Mute"); - - enable.add("Object.VisibleTake", boost::bind(&visible_take_object)); - enable.add("Object.VisibleBuy", boost::bind(&visible_buy_object)); - - commit.add("Object.Buy", boost::bind(&handle_buy)); - commit.add("Object.Edit", boost::bind(&handle_object_edit)); - commit.add("Object.Inspect", boost::bind(&handle_object_inspect)); - commit.add("Object.Open", boost::bind(&handle_object_open)); - commit.add("Object.Take", boost::bind(&handle_take)); - commit.add("Object.ShowInspector", boost::bind(&handle_object_show_inspector)); - enable.add("Object.EnableOpen", boost::bind(&enable_object_open)); - enable.add("Object.EnableTouch", boost::bind(&enable_object_touch, _1)); - enable.add("Object.EnableDelete", boost::bind(&enable_object_delete)); - enable.add("Object.EnableWear", boost::bind(&object_selected_and_point_valid)); - - enable.add("Object.EnableStandUp", boost::bind(&enable_object_stand_up)); - enable.add("Object.EnableSit", boost::bind(&enable_object_sit, _1)); - - view_listener_t::addMenu(new LLObjectEnableReturn(), "Object.EnableReturn"); - view_listener_t::addMenu(new LLObjectEnableReportAbuse(), "Object.EnableReportAbuse"); - - enable.add("Avatar.EnableMute", boost::bind(&enable_object_mute)); - enable.add("Object.EnableMute", boost::bind(&enable_object_mute)); - enable.add("Object.EnableBuy", boost::bind(&enable_buy_object)); - commit.add("Object.ZoomIn", boost::bind(&handle_look_at_selection, "zoom")); - - // Attachment pie menu - enable.add("Attachment.Label", boost::bind(&onEnableAttachmentLabel, _1, _2)); - view_listener_t::addMenu(new LLAttachmentDrop(), "Attachment.Drop"); - view_listener_t::addMenu(new LLAttachmentDetachFromPoint(), "Attachment.DetachFromPoint"); - view_listener_t::addMenu(new LLAttachmentDetach(), "Attachment.Detach"); - view_listener_t::addMenu(new LLAttachmentPointFilled(), "Attachment.PointFilled"); - view_listener_t::addMenu(new LLAttachmentEnableDrop(), "Attachment.EnableDrop"); - view_listener_t::addMenu(new LLAttachmentEnableDetach(), "Attachment.EnableDetach"); - - // Land pie menu - view_listener_t::addMenu(new LLLandBuild(), "Land.Build"); - view_listener_t::addMenu(new LLLandSit(), "Land.Sit"); - view_listener_t::addMenu(new LLLandBuyPass(), "Land.BuyPass"); - view_listener_t::addMenu(new LLLandEdit(), "Land.Edit"); - - view_listener_t::addMenu(new LLLandEnableBuyPass(), "Land.EnableBuyPass"); - commit.add("Land.Buy", boost::bind(&handle_buy_land)); - - // Generic actions - commit.add("ReportAbuse", boost::bind(&handle_report_abuse)); - commit.add("BuyCurrency", boost::bind(&handle_buy_currency)); - view_listener_t::addMenu(new LLShowHelp(), "ShowHelp"); - view_listener_t::addMenu(new LLToggleHelp(), "ToggleHelp"); - view_listener_t::addMenu(new LLPromptShowURL(), "PromptShowURL"); - view_listener_t::addMenu(new LLShowAgentProfile(), "ShowAgentProfile"); - view_listener_t::addMenu(new LLToggleAgentProfile(), "ToggleAgentProfile"); - view_listener_t::addMenu(new LLToggleControl(), "ToggleControl"); - view_listener_t::addMenu(new LLCheckControl(), "CheckControl"); - view_listener_t::addMenu(new LLGoToObject(), "GoToObject"); - commit.add("PayObject", boost::bind(&handle_give_money_dialog)); - - enable.add("EnablePayObject", boost::bind(&enable_pay_object)); - enable.add("EnablePayAvatar", boost::bind(&enable_pay_avatar)); - enable.add("EnableEdit", boost::bind(&enable_object_edit)); - enable.add("VisibleBuild", boost::bind(&enable_object_build)); - - view_listener_t::addMenu(new LLFloaterVisible(), "FloaterVisible"); - view_listener_t::addMenu(new LLShowSidetrayPanel(), "ShowSidetrayPanel"); - view_listener_t::addMenu(new LLSidetrayPanelVisible(), "SidetrayPanelVisible"); - view_listener_t::addMenu(new LLSomethingSelected(), "SomethingSelected"); - view_listener_t::addMenu(new LLSomethingSelectedNoHUD(), "SomethingSelectedNoHUD"); - view_listener_t::addMenu(new LLEditableSelected(), "EditableSelected"); - view_listener_t::addMenu(new LLEditableSelectedMono(), "EditableSelectedMono"); - - view_listener_t::addMenu(new LLToggleUIHints(), "ToggleUIHints"); - - commit.add("DestinationAndAvatar.show", boost::bind(&toggle_destination_and_avatar_picker, _2)); -} +/** + * @file llviewermenu.cpp + * @brief Builds menus out of items. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llviewermenu.h" + +// linden library includes +#include "llavatarnamecache.h" // IDEVO +#include "llfloaterreg.h" +#include "llcombobox.h" +#include "llinventorypanel.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" + +// newview includes +#include "llagent.h" +#include "llagentcamera.h" +#include "llagentwearables.h" +#include "llagentpilot.h" +#include "llbottomtray.h" +#include "llcompilequeue.h" +#include "llconsole.h" +#include "lldebugview.h" +#include "llfilepicker.h" +#include "llfirstuse.h" +#include "llfloaterbuy.h" +#include "llfloaterbuycontents.h" +#include "llbuycurrencyhtml.h" +#include "llfloatergodtools.h" +#include "llfloaterinventory.h" +#include "llfloaterland.h" +#include "llfloaterpay.h" +#include "llfloaterreporter.h" +#include "llfloatersearch.h" +#include "llfloaterscriptdebug.h" +#include "llfloatersnapshot.h" +#include "llfloatertools.h" +#include "llfloaterworldmap.h" +#include "llavataractions.h" +#include "lllandmarkactions.h" +#include "llgroupmgr.h" +#include "lltooltip.h" +#include "llhints.h" +#include "llhudeffecttrail.h" +#include "llhudmanager.h" +#include "llimview.h" +#include "llinventorybridge.h" +#include "llinventorydefines.h" +#include "llinventoryfunctions.h" +#include "llpanellogin.h" +#include "llpanelblockedlist.h" +#include "llmenucommands.h" +#include "llmoveview.h" +#include "llparcel.h" +#include "llrootview.h" +#include "llselectmgr.h" +#include "llsidetray.h" +#include "llstatusbar.h" +#include "lltextureview.h" +#include "lltoolcomp.h" +#include "lltoolmgr.h" +#include "lltoolpie.h" +#include "lltoolselectland.h" +#include "lltrans.h" +#include "llviewergenericmessage.h" +#include "llviewerhelp.h" +#include "llviewermenufile.h" // init_menu_file() +#include "llviewermessage.h" +#include "llviewernetwork.h" +#include "llviewerobjectlist.h" +#include "llviewerparcelmgr.h" +#include "llviewerstats.h" +#include "llvoavatarself.h" +#include "llworldmap.h" +#include "pipeline.h" +#include "llviewerjoystick.h" +#include "llwlanimator.h" +#include "llwlparammanager.h" +#include "llfloatercamera.h" +#include "lluilistener.h" +#include "llappearancemgr.h" +#include "lltrans.h" +#include "lleconomy.h" +#include "boost/unordered_map.hpp" + +using namespace LLVOAvatarDefines; + +static boost::unordered_map sDefaultItemLabels; + +BOOL enable_land_build(void*); +BOOL enable_object_build(void*); + +LLVOAvatar* find_avatar_from_object( LLViewerObject* object ); +LLVOAvatar* find_avatar_from_object( const LLUUID& object_id ); + +void handle_test_load_url(void*); + +// +// Evil hackish imported globals + +//extern BOOL gHideSelectedObjects; +//extern BOOL gAllowSelectAvatar; +//extern BOOL gDebugAvatarRotation; +extern BOOL gDebugClicks; +extern BOOL gDebugWindowProc; +//extern BOOL gDebugTextEditorTips; +//extern BOOL gDebugSelectMgr; + +// +// Globals +// + +LLMenuBarGL *gMenuBarView = NULL; +LLViewerMenuHolderGL *gMenuHolder = NULL; +LLMenuGL *gPopupMenuView = NULL; +LLMenuGL *gEditMenu = NULL; +LLMenuBarGL *gLoginMenuBarView = NULL; + +// Pie menus +LLContextMenu *gMenuAvatarSelf = NULL; +LLContextMenu *gMenuAvatarOther = NULL; +LLContextMenu *gMenuObject = NULL; +LLContextMenu *gMenuAttachmentSelf = NULL; +LLContextMenu *gMenuAttachmentOther = NULL; +LLContextMenu *gMenuLand = NULL; + +const std::string SAVE_INTO_INVENTORY("Save Object Back to My Inventory"); +const std::string SAVE_INTO_TASK_INVENTORY("Save Object Back to Object Contents"); + +LLMenuGL* gAttachSubMenu = NULL; +LLMenuGL* gDetachSubMenu = NULL; +LLMenuGL* gTakeOffClothes = NULL; +LLContextMenu* gAttachScreenPieMenu = NULL; +LLContextMenu* gAttachPieMenu = NULL; +LLContextMenu* gAttachBodyPartPieMenus[8]; +LLContextMenu* gDetachPieMenu = NULL; +LLContextMenu* gDetachScreenPieMenu = NULL; +LLContextMenu* gDetachBodyPartPieMenus[8]; + +LLMenuItemCallGL* gAFKMenu = NULL; +LLMenuItemCallGL* gBusyMenu = NULL; + +// +// Local prototypes + +// File Menu +const char* upload_pick(void* data); +void handle_compress_image(void*); + + +// Edit menu +void handle_dump_group_info(void *); +void handle_dump_capabilities_info(void *); + +// Advanced->Consoles menu +void handle_region_dump_settings(void*); +void handle_region_dump_temp_asset_data(void*); +void handle_region_clear_temp_asset_data(void*); + +// Object pie menu +BOOL sitting_on_selection(); + +void near_sit_object(); +//void label_sit_or_stand(std::string& label, void*); +// buy and take alias into the same UI positions, so these +// declarations handle this mess. +BOOL is_selection_buy_not_take(); +S32 selection_price(); +BOOL enable_take(); +void handle_take(); +void handle_object_show_inspector(); +void handle_avatar_show_inspector(); +bool confirm_take(const LLSD& notification, const LLSD& response); + +void handle_buy_object(LLSaleInfo sale_info); +void handle_buy_contents(LLSaleInfo sale_info); + +// Land pie menu +void near_sit_down_point(BOOL success, void *); + +// Avatar pie menu + +// Debug menu + + +void velocity_interpolate( void* ); + +void handle_rebake_textures(void*); +BOOL check_admin_override(void*); +void handle_admin_override_toggle(void*); +#ifdef TOGGLE_HACKED_GODLIKE_VIEWER +void handle_toggle_hacked_godmode(void*); +BOOL check_toggle_hacked_godmode(void*); +bool enable_toggle_hacked_godmode(void*); +#endif + +void toggle_show_xui_names(void *); +BOOL check_show_xui_names(void *); + +// Debug UI + +void handle_buy_currency_test(void*); + +void handle_god_mode(void*); + +// God menu +void handle_leave_god_mode(void*); + + +void handle_reset_view(); + +void handle_duplicate_in_place(void*); + + +void handle_object_owner_self(void*); +void handle_object_owner_permissive(void*); +void handle_object_lock(void*); +void handle_object_asset_ids(void*); +void force_take_copy(void*); +#ifdef _CORY_TESTING +void force_export_copy(void*); +void force_import_geometry(void*); +#endif + +void handle_force_parcel_owner_to_me(void*); +void handle_force_parcel_to_content(void*); +void handle_claim_public_land(void*); + +void handle_god_request_avatar_geometry(void *); // Hack for easy testing of new avatar geometry +void reload_vertex_shader(void *); +void handle_disconnect_viewer(void *); + +void force_error_breakpoint(void *); +void force_error_llerror(void *); +void force_error_bad_memory_access(void *); +void force_error_infinite_loop(void *); +void force_error_software_exception(void *); +void force_error_driver_crash(void *); + +void handle_force_delete(void*); +void print_object_info(void*); +void print_agent_nvpairs(void*); +void toggle_debug_menus(void*); +void upload_done_callback(const LLUUID& uuid, void* user_data, S32 result, LLExtStat ext_status); +void dump_select_mgr(void*); + +void dump_inventory(void*); +void toggle_visibility(void*); +BOOL get_visibility(void*); + +// Avatar Pie menu +void request_friendship(const LLUUID& agent_id); + +// Tools menu +void handle_selected_texture_info(void*); + +void handle_dump_followcam(void*); +void handle_viewer_enable_message_log(void*); +void handle_viewer_disable_message_log(void*); + +BOOL enable_buy_land(void*); + +// Help menu + +void handle_test_male(void *); +void handle_test_female(void *); +void handle_toggle_pg(void*); +void handle_dump_attachments(void *); +void handle_dump_avatar_local_textures(void*); +void handle_debug_avatar_textures(void*); +void handle_grab_baked_texture(void*); +BOOL enable_grab_baked_texture(void*); +void handle_dump_region_object_cache(void*); + +BOOL enable_save_into_inventory(void*); +BOOL enable_save_into_task_inventory(void*); + +BOOL enable_detach(const LLSD& = LLSD()); +void menu_toggle_attached_lights(void* user_data); +void menu_toggle_attached_particles(void* user_data); + +class LLMenuParcelObserver : public LLParcelObserver +{ +public: + LLMenuParcelObserver(); + ~LLMenuParcelObserver(); + virtual void changed(); +}; + +static LLMenuParcelObserver* gMenuParcelObserver = NULL; + +static LLUIListener sUIListener; + +LLMenuParcelObserver::LLMenuParcelObserver() +{ + LLViewerParcelMgr::getInstance()->addObserver(this); +} + +LLMenuParcelObserver::~LLMenuParcelObserver() +{ + LLViewerParcelMgr::getInstance()->removeObserver(this); +} + +void LLMenuParcelObserver::changed() +{ + gMenuHolder->childSetEnabled("Land Buy Pass", LLPanelLandGeneral::enableBuyPass(NULL)); + + BOOL buyable = enable_buy_land(NULL); + gMenuHolder->childSetEnabled("Land Buy", buyable); + gMenuHolder->childSetEnabled("Buy Land...", buyable); +} + + +void initialize_menus(); + +//----------------------------------------------------------------------------- +// Initialize main menus +// +// HOW TO NAME MENUS: +// +// First Letter Of Each Word Is Capitalized, Even At Or And +// +// Items that lead to dialog boxes end in "..." +// +// Break up groups of more than 6 items with separators +//----------------------------------------------------------------------------- + +void set_underclothes_menu_options() +{ + if (gMenuHolder && gAgent.isTeen()) + { + gMenuHolder->getChild("Self Underpants")->setVisible(FALSE); + gMenuHolder->getChild("Self Undershirt")->setVisible(FALSE); + } + if (gMenuBarView && gAgent.isTeen()) + { + gMenuBarView->getChild("Menu Underpants")->setVisible(FALSE); + gMenuBarView->getChild("Menu Undershirt")->setVisible(FALSE); + } +} + +void init_menus() +{ + S32 top = gViewerWindow->getRootView()->getRect().getHeight(); + + // Initialize actions + initialize_menus(); + + /// + /// Popup menu + /// + /// The popup menu is now populated by the show_context_menu() + /// method. + + LLMenuGL::Params menu_params; + menu_params.name = "Popup"; + menu_params.visible = false; + gPopupMenuView = LLUICtrlFactory::create(menu_params); + gMenuHolder->addChild( gPopupMenuView ); + + /// + /// Context menus + /// + + const widget_registry_t& registry = + LLViewerMenuHolderGL::child_registry_t::instance(); + gEditMenu = LLUICtrlFactory::createFromFile("menu_edit.xml", gMenuHolder, registry); + gMenuAvatarSelf = LLUICtrlFactory::createFromFile( + "menu_avatar_self.xml", gMenuHolder, registry); + gMenuAvatarOther = LLUICtrlFactory::createFromFile( + "menu_avatar_other.xml", gMenuHolder, registry); + + gDetachScreenPieMenu = gMenuHolder->getChild("Object Detach HUD", true); + gDetachPieMenu = gMenuHolder->getChild("Object Detach", true); + + gMenuObject = LLUICtrlFactory::createFromFile( + "menu_object.xml", gMenuHolder, registry); + + gAttachScreenPieMenu = gMenuHolder->getChild("Object Attach HUD"); + gAttachPieMenu = gMenuHolder->getChild("Object Attach"); + + gMenuAttachmentSelf = LLUICtrlFactory::createFromFile( + "menu_attachment_self.xml", gMenuHolder, registry); + gMenuAttachmentOther = LLUICtrlFactory::createFromFile( + "menu_attachment_other.xml", gMenuHolder, registry); + + gMenuLand = LLUICtrlFactory::createFromFile( + "menu_land.xml", gMenuHolder, registry); + + /// + /// set up the colors + /// + LLColor4 color; + + LLColor4 context_menu_color = LLUIColorTable::instance().getColor("MenuPopupBgColor"); + + gMenuAvatarSelf->setBackgroundColor( context_menu_color ); + gMenuAvatarOther->setBackgroundColor( context_menu_color ); + gMenuObject->setBackgroundColor( context_menu_color ); + gMenuAttachmentSelf->setBackgroundColor( context_menu_color ); + gMenuAttachmentOther->setBackgroundColor( context_menu_color ); + + gMenuLand->setBackgroundColor( context_menu_color ); + + color = LLUIColorTable::instance().getColor( "MenuPopupBgColor" ); + gPopupMenuView->setBackgroundColor( color ); + + // If we are not in production, use a different color to make it apparent. + if (LLGridManager::getInstance()->isInProductionGrid()) + { + color = LLUIColorTable::instance().getColor( "MenuBarBgColor" ); + } + else + { + color = LLUIColorTable::instance().getColor( "MenuNonProductionBgColor" ); + } + gMenuBarView = LLUICtrlFactory::getInstance()->createFromFile("menu_viewer.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + gMenuBarView->setRect(LLRect(0, top, 0, top - MENU_BAR_HEIGHT)); + gMenuBarView->setBackgroundColor( color ); + + LLView* menu_bar_holder = gViewerWindow->getRootView()->getChildView("menu_bar_holder"); + menu_bar_holder->addChild(gMenuBarView); + + gViewerWindow->setMenuBackgroundColor(false, + LLGridManager::getInstance()->isInProductionGrid()); + + // Assume L$10 for now, the server will tell us the real cost at login + // *TODO:Also fix cost in llfolderview.cpp for Inventory menus + const std::string upload_cost("10"); + gMenuHolder->childSetLabelArg("Upload Image", "[COST]", upload_cost); + gMenuHolder->childSetLabelArg("Upload Sound", "[COST]", upload_cost); + gMenuHolder->childSetLabelArg("Upload Animation", "[COST]", upload_cost); + gMenuHolder->childSetLabelArg("Bulk Upload", "[COST]", upload_cost); + + gAFKMenu = gMenuBarView->getChild("Set Away", TRUE); + gBusyMenu = gMenuBarView->getChild("Set Busy", TRUE); + gAttachSubMenu = gMenuBarView->findChildMenuByName("Attach Object", TRUE); + gDetachSubMenu = gMenuBarView->findChildMenuByName("Detach Object", TRUE); + +#if !MEM_TRACK_MEM + // Don't display the Memory console menu if the feature is turned off + LLMenuItemCheckGL *memoryMenu = gMenuBarView->getChild("Memory", TRUE); + if (memoryMenu) + { + memoryMenu->setVisible(FALSE); + } +#endif + + gMenuBarView->createJumpKeys(); + + // Let land based option enable when parcel changes + gMenuParcelObserver = new LLMenuParcelObserver(); + + gLoginMenuBarView = LLUICtrlFactory::getInstance()->createFromFile("menu_login.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + gLoginMenuBarView->arrangeAndClear(); + LLRect menuBarRect = gLoginMenuBarView->getRect(); + menuBarRect.setLeftTopAndSize(0, menu_bar_holder->getRect().getHeight(), menuBarRect.getWidth(), menuBarRect.getHeight()); + gLoginMenuBarView->setRect(menuBarRect); + gLoginMenuBarView->setBackgroundColor( color ); + menu_bar_holder->addChild(gLoginMenuBarView); + + // tooltips are on top of EVERYTHING, including menus + gViewerWindow->getRootView()->sendChildToFront(gToolTipView); +} + +/////////////////// +// SHOW CONSOLES // +/////////////////// + + +class LLAdvancedToggleConsole : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string console_type = userdata.asString(); + if ("texture" == console_type) + { + toggle_visibility( (void*)gTextureView ); + } + else if ("debug" == console_type) + { + toggle_visibility( (void*)static_cast(gDebugView->mDebugConsolep)); + } + else if (gTextureSizeView && "texture size" == console_type) + { + toggle_visibility( (void*)gTextureSizeView ); + } + else if (gTextureCategoryView && "texture category" == console_type) + { + toggle_visibility( (void*)gTextureCategoryView ); + } + else if ("fast timers" == console_type) + { + toggle_visibility( (void*)gDebugView->mFastTimerView ); + } +#if MEM_TRACK_MEM + else if ("memory view" == console_type) + { + toggle_visibility( (void*)gDebugView->mMemoryView ); + } +#endif + return true; + } +}; +class LLAdvancedCheckConsole : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string console_type = userdata.asString(); + bool new_value = false; + if ("texture" == console_type) + { + new_value = get_visibility( (void*)gTextureView ); + } + else if ("debug" == console_type) + { + new_value = get_visibility( (void*)((LLView*)gDebugView->mDebugConsolep) ); + } + else if (gTextureSizeView && "texture size" == console_type) + { + new_value = get_visibility( (void*)gTextureSizeView ); + } + else if (gTextureCategoryView && "texture category" == console_type) + { + new_value = get_visibility( (void*)gTextureCategoryView ); + } + else if ("fast timers" == console_type) + { + new_value = get_visibility( (void*)gDebugView->mFastTimerView ); + } +#if MEM_TRACK_MEM + else if ("memory view" == console_type) + { + new_value = get_visibility( (void*)gDebugView->mMemoryView ); + } +#endif + + return new_value; + } +}; + + +////////////////////////// +// DUMP INFO TO CONSOLE // +////////////////////////// + + +class LLAdvancedDumpInfoToConsole : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string info_type = userdata.asString(); + if ("region" == info_type) + { + handle_region_dump_settings(NULL); + } + else if ("group" == info_type) + { + handle_dump_group_info(NULL); + } + else if ("capabilities" == info_type) + { + handle_dump_capabilities_info(NULL); + } + return true; + } +}; + + +////////////// +// HUD INFO // +////////////// + + +class LLAdvancedToggleHUDInfo : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string info_type = userdata.asString(); + + if ("camera" == info_type) + { + gDisplayCameraPos = !(gDisplayCameraPos); + } + else if ("wind" == info_type) + { + gDisplayWindInfo = !(gDisplayWindInfo); + } + else if ("fov" == info_type) + { + gDisplayFOV = !(gDisplayFOV); + } + else if ("badge" == info_type) + { + gDisplayBadge = !(gDisplayBadge); + } + return true; + } +}; + +class LLAdvancedCheckHUDInfo : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string info_type = userdata.asString(); + bool new_value = false; + if ("camera" == info_type) + { + new_value = gDisplayCameraPos; + } + else if ("wind" == info_type) + { + new_value = gDisplayWindInfo; + } + else if ("fov" == info_type) + { + new_value = gDisplayFOV; + } + else if ("badge" == info_type) + { + new_value = gDisplayBadge; + } + return new_value; + } +}; + + +////////////// +// FLYING // +////////////// + +class LLAdvancedAgentFlyingInfo : public view_listener_t +{ + bool handleEvent(const LLSD&) + { + return gAgent.getFlying(); + } +}; + + +/////////////////////// +// CLEAR GROUP CACHE // +/////////////////////// + +class LLAdvancedClearGroupCache : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLGroupMgr::debugClearAllGroups(NULL); + return true; + } +}; + + + + +///////////////// +// RENDER TYPE // +///////////////// +U32 render_type_from_string(std::string render_type) +{ + if ("simple" == render_type) + { + return LLPipeline::RENDER_TYPE_SIMPLE; + } + else if ("alpha" == render_type) + { + return LLPipeline::RENDER_TYPE_ALPHA; + } + else if ("tree" == render_type) + { + return LLPipeline::RENDER_TYPE_TREE; + } + else if ("character" == render_type) + { + return LLPipeline::RENDER_TYPE_AVATAR; + } + else if ("surfacePath" == render_type) + { + return LLPipeline::RENDER_TYPE_TERRAIN; + } + else if ("sky" == render_type) + { + return LLPipeline::RENDER_TYPE_SKY; + } + else if ("water" == render_type) + { + return LLPipeline::RENDER_TYPE_WATER; + } + else if ("ground" == render_type) + { + return LLPipeline::RENDER_TYPE_GROUND; + } + else if ("volume" == render_type) + { + return LLPipeline::RENDER_TYPE_VOLUME; + } + else if ("grass" == render_type) + { + return LLPipeline::RENDER_TYPE_GRASS; + } + else if ("clouds" == render_type) + { + return LLPipeline::RENDER_TYPE_CLOUDS; + } + else if ("particles" == render_type) + { + return LLPipeline::RENDER_TYPE_PARTICLES; + } + else if ("bump" == render_type) + { + return LLPipeline::RENDER_TYPE_BUMP; + } + else + { + return 0; + } +} + + +class LLAdvancedToggleRenderType : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + U32 render_type = render_type_from_string( userdata.asString() ); + if ( render_type != 0 ) + { + LLPipeline::toggleRenderTypeControl( (void*)render_type ); + } + return true; + } +}; + + +class LLAdvancedCheckRenderType : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + U32 render_type = render_type_from_string( userdata.asString() ); + bool new_value = false; + + if ( render_type != 0 ) + { + new_value = LLPipeline::hasRenderTypeControl( (void*)render_type ); + } + + return new_value; + } +}; + + +///////////// +// FEATURE // +///////////// +U32 feature_from_string(std::string feature) +{ + if ("ui" == feature) + { + return LLPipeline::RENDER_DEBUG_FEATURE_UI; + } + else if ("selected" == feature) + { + return LLPipeline::RENDER_DEBUG_FEATURE_SELECTED; + } + else if ("highlighted" == feature) + { + return LLPipeline::RENDER_DEBUG_FEATURE_HIGHLIGHTED; + } + else if ("dynamic textures" == feature) + { + return LLPipeline::RENDER_DEBUG_FEATURE_DYNAMIC_TEXTURES; + } + else if ("foot shadows" == feature) + { + return LLPipeline::RENDER_DEBUG_FEATURE_FOOT_SHADOWS; + } + else if ("fog" == feature) + { + return LLPipeline::RENDER_DEBUG_FEATURE_FOG; + } + else if ("fr info" == feature) + { + return LLPipeline::RENDER_DEBUG_FEATURE_FR_INFO; + } + else if ("flexible" == feature) + { + return LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE; + } + else + { + return 0; + } +}; + + +class LLAdvancedToggleFeature : public view_listener_t{ + bool handleEvent(const LLSD& userdata) + { + U32 feature = feature_from_string( userdata.asString() ); + if ( feature != 0 ) + { + LLPipeline::toggleRenderDebugFeature( (void*)feature ); + } + return true; + } +}; + +class LLAdvancedCheckFeature : public view_listener_t +{bool handleEvent(const LLSD& userdata) +{ + U32 feature = feature_from_string( userdata.asString() ); + bool new_value = false; + + if ( feature != 0 ) + { + new_value = LLPipeline::toggleRenderDebugFeatureControl( (void*)feature ); + } + + return new_value; +} +}; + +void toggle_destination_and_avatar_picker(const LLSD& show) +{ + S32 panel_idx = show.isDefined() ? show.asInteger() : -1; + LLView* container = gViewerWindow->getRootView()->getChildView("avatar_picker_and_destination_guide_container"); + LLMediaCtrl* destinations = container->findChild("destination_guide_contents"); + LLMediaCtrl* avatar_picker = container->findChild("avatar_picker_contents"); + + switch(panel_idx) + { + case 0: + container->setVisible(true); + destinations->setVisible(true); + avatar_picker->setVisible(false); + LLFirstUse::notUsingDestinationGuide(false); + break; + case 1: + container->setVisible(true); + destinations->setVisible(false); + avatar_picker->setVisible(true); + break; + default: + container->setVisible(false); + destinations->setVisible(false); + avatar_picker->setVisible(false); + break; + } + + gViewerWindow->getRootView()->getChildView("bottom_tray")->getChild("avatar_and_destination_btns")->setValue(show); +}; + + +////////////////// +// INFO DISPLAY // +////////////////// +U32 info_display_from_string(std::string info_display) +{ + if ("verify" == info_display) + { + return LLPipeline::RENDER_DEBUG_VERIFY; + } + else if ("bboxes" == info_display) + { + return LLPipeline::RENDER_DEBUG_BBOXES; + } + else if ("points" == info_display) + { + return LLPipeline::RENDER_DEBUG_POINTS; + } + else if ("octree" == info_display) + { + return LLPipeline::RENDER_DEBUG_OCTREE; + } + else if ("shadow frusta" == info_display) + { + return LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA; + } + else if ("occlusion" == info_display) + { + return LLPipeline::RENDER_DEBUG_OCCLUSION; + } + else if ("render batches" == info_display) + { + return LLPipeline::RENDER_DEBUG_BATCH_SIZE; + } + else if ("update type" == info_display) + { + return LLPipeline::RENDER_DEBUG_UPDATE_TYPE; + } + else if ("texture anim" == info_display) + { + return LLPipeline::RENDER_DEBUG_TEXTURE_ANIM; + } + else if ("texture priority" == info_display) + { + return LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY; + } + else if ("shame" == info_display) + { + return LLPipeline::RENDER_DEBUG_SHAME; + } + else if ("texture area" == info_display) + { + return LLPipeline::RENDER_DEBUG_TEXTURE_AREA; + } + else if ("face area" == info_display) + { + return LLPipeline::RENDER_DEBUG_FACE_AREA; + } + else if ("lights" == info_display) + { + return LLPipeline::RENDER_DEBUG_LIGHTS; + } + else if ("particles" == info_display) + { + return LLPipeline::RENDER_DEBUG_PARTICLES; + } + else if ("composition" == info_display) + { + return LLPipeline::RENDER_DEBUG_COMPOSITION; + } + else if ("glow" == info_display) + { + return LLPipeline::RENDER_DEBUG_GLOW; + } + else if ("collision skeleton" == info_display) + { + return LLPipeline::RENDER_DEBUG_AVATAR_VOLUME; + } + else if ("raycast" == info_display) + { + return LLPipeline::RENDER_DEBUG_RAYCAST; + } + else if ("agent target" == info_display) + { + return LLPipeline::RENDER_DEBUG_AGENT_TARGET; + } + else + { + return 0; + } +}; + +class LLAdvancedToggleInfoDisplay : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + U32 info_display = info_display_from_string( userdata.asString() ); + + if ( info_display != 0 ) + { + LLPipeline::toggleRenderDebug( (void*)info_display ); + } + + return true; + } +}; + + +class LLAdvancedCheckInfoDisplay : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + U32 info_display = info_display_from_string( userdata.asString() ); + bool new_value = false; + + if ( info_display != 0 ) + { + new_value = LLPipeline::toggleRenderDebugControl( (void*)info_display ); + } + + return new_value; + } +}; + + +/////////////////////////// +//// RANDOMIZE FRAMERATE // +/////////////////////////// + + +class LLAdvancedToggleRandomizeFramerate : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gRandomizeFramerate = !(gRandomizeFramerate); + return true; + } +}; + +class LLAdvancedCheckRandomizeFramerate : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = gRandomizeFramerate; + return new_value; + } +}; + +void run_vectorize_perf_test(void *) +{ + gSavedSettings.setBOOL("VectorizePerfTest", TRUE); +} + + +//////////////////////////////// +// RUN Vectorized Perform Test// +//////////////////////////////// + + +class LLAdvancedVectorizePerfTest : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + run_vectorize_perf_test(NULL); + return true; + } +}; + +/////////////////////////// +//// PERIODIC SLOW FRAME // +/////////////////////////// + + +class LLAdvancedTogglePeriodicSlowFrame : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gPeriodicSlowFrame = !(gPeriodicSlowFrame); + return true; + } +}; + +class LLAdvancedCheckPeriodicSlowFrame : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = gPeriodicSlowFrame; + return new_value; + } +}; + + + +//////////////// +// FRAME TEST // +//////////////// + + +class LLAdvancedToggleFrameTest : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLPipeline::sRenderFrameTest = !(LLPipeline::sRenderFrameTest); + return true; + } +}; + +class LLAdvancedCheckFrameTest : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLPipeline::sRenderFrameTest; + return new_value; + } +}; + + +/////////////////////////// +// SELECTED TEXTURE INFO // +/////////////////////////// + + +class LLAdvancedSelectedTextureInfo : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_selected_texture_info(NULL); + return true; + } +}; + +////////////////////// +// TOGGLE WIREFRAME // +////////////////////// + +class LLAdvancedToggleWireframe : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gUseWireframe = !(gUseWireframe); + LLPipeline::updateRenderDeferred(); + gPipeline.resetVertexBuffers(); + return true; + } +}; + +class LLAdvancedCheckWireframe : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = gUseWireframe; + return new_value; + } +}; + +////////////////////// +// TEXTURE ATLAS // +////////////////////// + +class LLAdvancedToggleTextureAtlas : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLViewerTexture::sUseTextureAtlas = !LLViewerTexture::sUseTextureAtlas; + gSavedSettings.setBOOL("EnableTextureAtlas", LLViewerTexture::sUseTextureAtlas) ; + return true; + } +}; + +class LLAdvancedCheckTextureAtlas : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLViewerTexture::sUseTextureAtlas; // <-- make this using LLCacheControl + return new_value; + } +}; + +////////////////////////// +// DUMP SCRIPTED CAMERA // +////////////////////////// + +class LLAdvancedDumpScriptedCamera : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_dump_followcam(NULL); + return true; +} +}; + + + +////////////////////////////// +// DUMP REGION OBJECT CACHE // +////////////////////////////// + + +class LLAdvancedDumpRegionObjectCache : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) +{ + handle_dump_region_object_cache(NULL); + return true; + } +}; + +class LLAdvancedBuyCurrencyTest : public view_listener_t + { + bool handleEvent(const LLSD& userdata) + { + handle_buy_currency_test(NULL); + return true; + } +}; + + +///////////////////// +// DUMP SELECT MGR // +///////////////////// + + +class LLAdvancedDumpSelectMgr : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + dump_select_mgr(NULL); + return true; + } +}; + + + +//////////////////// +// DUMP INVENTORY // +//////////////////// + + +class LLAdvancedDumpInventory : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + dump_inventory(NULL); + return true; + } +}; + + + +//////////////////////////////// +// PRINT SELECTED OBJECT INFO // +//////////////////////////////// + + +class LLAdvancedPrintSelectedObjectInfo : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + print_object_info(NULL); + return true; + } +}; + + + +////////////////////// +// PRINT AGENT INFO // +////////////////////// + + +class LLAdvancedPrintAgentInfo : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + print_agent_nvpairs(NULL); + return true; + } +}; + + + +//////////////////////////////// +// PRINT TEXTURE MEMORY STATS // +//////////////////////////////// + + +class LLAdvancedPrintTextureMemoryStats : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + output_statistics(NULL); + return true; + } +}; + +////////////////// +// DEBUG CLICKS // +////////////////// + + +class LLAdvancedToggleDebugClicks : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gDebugClicks = !(gDebugClicks); + return true; + } +}; + +class LLAdvancedCheckDebugClicks : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = gDebugClicks; + return new_value; + } +}; + + + +///////////////// +// DEBUG VIEWS // +///////////////// + + +class LLAdvancedToggleDebugViews : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLView::sDebugRects = !(LLView::sDebugRects); + return true; + } +}; + +class LLAdvancedCheckDebugViews : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLView::sDebugRects; + return new_value; + } +}; + + + +/////////////////////// +// XUI NAME TOOLTIPS // +/////////////////////// + + +class LLAdvancedToggleXUINameTooltips : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + toggle_show_xui_names(NULL); + return true; + } +}; + +class LLAdvancedCheckXUINameTooltips : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = check_show_xui_names(NULL); + return new_value; + } +}; + + + +//////////////////////// +// DEBUG MOUSE EVENTS // +//////////////////////// + + +class LLAdvancedToggleDebugMouseEvents : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLView::sDebugMouseHandling = !(LLView::sDebugMouseHandling); + return true; + } +}; + +class LLAdvancedCheckDebugMouseEvents : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLView::sDebugMouseHandling; + return new_value; + } +}; + + + +//////////////// +// DEBUG KEYS // +//////////////// + + +class LLAdvancedToggleDebugKeys : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLView::sDebugKeys = !(LLView::sDebugKeys); + return true; + } +}; + +class LLAdvancedCheckDebugKeys : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLView::sDebugKeys; + return new_value; + } +}; + + + +/////////////////////// +// DEBUG WINDOW PROC // +/////////////////////// + + +class LLAdvancedToggleDebugWindowProc : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gDebugWindowProc = !(gDebugWindowProc); + return true; + } +}; + +class LLAdvancedCheckDebugWindowProc : public view_listener_t + { + bool handleEvent(const LLSD& userdata) + { + bool new_value = gDebugWindowProc; + return new_value; + } +}; + +// ------------------------------XUI MENU --------------------------- + +class LLAdvancedSendTestIms : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLIMModel::instance().testMessages(); + return true; +} +}; + + +/////////////// +// XUI NAMES // +/////////////// + + +class LLAdvancedToggleXUINames : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + toggle_show_xui_names(NULL); + return true; + } +}; + +class LLAdvancedCheckXUINames : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = check_show_xui_names(NULL); + return new_value; + } +}; + + +//////////////////////// +// GRAB BAKED TEXTURE // +//////////////////////// + + +class LLAdvancedGrabBakedTexture : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string texture_type = userdata.asString(); + if ("iris" == texture_type) + { + handle_grab_baked_texture( (void*)BAKED_EYES ); + } + else if ("head" == texture_type) + { + handle_grab_baked_texture( (void*)BAKED_HEAD ); + } + else if ("upper" == texture_type) + { + handle_grab_baked_texture( (void*)BAKED_UPPER ); + } + else if ("lower" == texture_type) + { + handle_grab_baked_texture( (void*)BAKED_LOWER ); + } + else if ("skirt" == texture_type) + { + handle_grab_baked_texture( (void*)BAKED_SKIRT ); + } + else if ("hair" == texture_type) + { + handle_grab_baked_texture( (void*)BAKED_HAIR ); + } + + return true; + } +}; + +class LLAdvancedEnableGrabBakedTexture : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) +{ + std::string texture_type = userdata.asString(); + bool new_value = false; + + if ("iris" == texture_type) + { + new_value = enable_grab_baked_texture( (void*)BAKED_EYES ); + } + else if ("head" == texture_type) + { + new_value = enable_grab_baked_texture( (void*)BAKED_HEAD ); + } + else if ("upper" == texture_type) + { + new_value = enable_grab_baked_texture( (void*)BAKED_UPPER ); + } + else if ("lower" == texture_type) + { + new_value = enable_grab_baked_texture( (void*)BAKED_LOWER ); + } + else if ("skirt" == texture_type) + { + new_value = enable_grab_baked_texture( (void*)BAKED_SKIRT ); + } + else if ("hair" == texture_type) + { + new_value = enable_grab_baked_texture( (void*)BAKED_HAIR ); + } + + return new_value; +} +}; + +/////////////////////// +// APPEARANCE TO XML // +/////////////////////// + + +class LLAdvancedAppearanceToXML : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLVOAvatar::dumpArchetypeXML(NULL); + return true; + } +}; + + + +/////////////////////////////// +// TOGGLE CHARACTER GEOMETRY // +/////////////////////////////// + + +class LLAdvancedToggleCharacterGeometry : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_god_request_avatar_geometry(NULL); + return true; +} +}; + + + ///////////////////////////// +// TEST MALE / TEST FEMALE // +///////////////////////////// + +class LLAdvancedTestMale : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_test_male(NULL); + return true; + } +}; + + +class LLAdvancedTestFemale : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_test_female(NULL); + return true; + } +}; + + + +/////////////// +// TOGGLE PG // +/////////////// + + +class LLAdvancedTogglePG : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_toggle_pg(NULL); + return true; + } +}; + + +class LLAdvancedForceParamsToDefault : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLAgent::clearVisualParams(NULL); + return true; + } +}; + + + +////////////////////////// +// RELOAD VERTEX SHADER // +////////////////////////// + + +class LLAdvancedReloadVertexShader : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + reload_vertex_shader(NULL); + return true; + } +}; + + + +//////////////////// +// ANIMATION INFO // +//////////////////// + + +class LLAdvancedToggleAnimationInfo : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLVOAvatar::sShowAnimationDebug = !(LLVOAvatar::sShowAnimationDebug); + return true; + } +}; + +class LLAdvancedCheckAnimationInfo : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLVOAvatar::sShowAnimationDebug; + return new_value; + } +}; + + +////////////////// +// SHOW LOOK AT // +////////////////// + + +class LLAdvancedToggleShowLookAt : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLHUDEffectLookAt::sDebugLookAt = !(LLHUDEffectLookAt::sDebugLookAt); + return true; + } +}; + +class LLAdvancedCheckShowLookAt : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLHUDEffectLookAt::sDebugLookAt; + return new_value; + } +}; + + + +/////////////////// +// SHOW POINT AT // +/////////////////// + + +class LLAdvancedToggleShowPointAt : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLHUDEffectPointAt::sDebugPointAt = !(LLHUDEffectPointAt::sDebugPointAt); + return true; + } +}; + +class LLAdvancedCheckShowPointAt : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLHUDEffectPointAt::sDebugPointAt; + return new_value; + } +}; + + + +///////////////////////// +// DEBUG JOINT UPDATES // +///////////////////////// + + +class LLAdvancedToggleDebugJointUpdates : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLVOAvatar::sJointDebug = !(LLVOAvatar::sJointDebug); + return true; + } +}; + +class LLAdvancedCheckDebugJointUpdates : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLVOAvatar::sJointDebug; + return new_value; + } +}; + + + +///////////////// +// DISABLE LOD // +///////////////// + + +class LLAdvancedToggleDisableLOD : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLViewerJoint::sDisableLOD = !(LLViewerJoint::sDisableLOD); + return true; + } +}; + +class LLAdvancedCheckDisableLOD : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLViewerJoint::sDisableLOD; + return new_value; + } +}; + + + +///////////////////////// +// DEBUG CHARACTER VIS // +///////////////////////// + + +class LLAdvancedToggleDebugCharacterVis : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLVOAvatar::sDebugInvisible = !(LLVOAvatar::sDebugInvisible); + return true; + } +}; + +class LLAdvancedCheckDebugCharacterVis : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLVOAvatar::sDebugInvisible; + return new_value; + } +}; + + +////////////////////// +// DUMP ATTACHMENTS // +////////////////////// + + +class LLAdvancedDumpAttachments : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_dump_attachments(NULL); + return true; + } +}; + + + +///////////////////// +// REBAKE TEXTURES // +///////////////////// + + +class LLAdvancedRebakeTextures : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_rebake_textures(NULL); + return true; + } +}; + + +#if 1 //ndef LL_RELEASE_FOR_DOWNLOAD +/////////////////////////// +// DEBUG AVATAR TEXTURES // +/////////////////////////// + + +class LLAdvancedDebugAvatarTextures : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if (gAgent.isGodlike()) + { + handle_debug_avatar_textures(NULL); + } + return true; + } +}; + +//////////////////////////////// +// DUMP AVATAR LOCAL TEXTURES // +//////////////////////////////// + + +class LLAdvancedDumpAvatarLocalTextures : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { +#ifndef LL_RELEASE_FOR_DOWNLOAD + handle_dump_avatar_local_textures(NULL); +#endif + return true; + } +}; + +#endif + +///////////////// +// MESSAGE LOG // +///////////////// + + +class LLAdvancedEnableMessageLog : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_viewer_enable_message_log(NULL); + return true; + } +}; + +class LLAdvancedDisableMessageLog : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_viewer_disable_message_log(NULL); + return true; + } +}; + +///////////////// +// DROP PACKET // +///////////////// + + +class LLAdvancedDropPacket : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gMessageSystem->mPacketRing.dropPackets(1); + return true; + } +}; + + + +///////////////// +// AGENT PILOT // +///////////////// + + +class LLAdvancedAgentPilot : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string command = userdata.asString(); + if ("start playback" == command) + { + LLAgentPilot::startPlayback(NULL); + } + else if ("stop playback" == command) + { + LLAgentPilot::stopPlayback(NULL); + } + else if ("start record" == command) + { + LLAgentPilot::startRecord(NULL); + } + else if ("stop record" == command) + { + LLAgentPilot::saveRecord(NULL); + } + + return true; + } +}; + + + +////////////////////// +// AGENT PILOT LOOP // +////////////////////// + + +class LLAdvancedToggleAgentPilotLoop : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLAgentPilot::sLoop = !(LLAgentPilot::sLoop); + return true; + } +}; + +class LLAdvancedCheckAgentPilotLoop : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLAgentPilot::sLoop; + return new_value; + } +}; + + +///////////////////////// +// SHOW OBJECT UPDATES // +///////////////////////// + + +class LLAdvancedToggleShowObjectUpdates : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gShowObjectUpdates = !(gShowObjectUpdates); + return true; + } +}; + +class LLAdvancedCheckShowObjectUpdates : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = gShowObjectUpdates; + return new_value; + } +}; + + + +//////////////////// +// COMPRESS IMAGE // +//////////////////// + + +class LLAdvancedCompressImage : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_compress_image(NULL); + return true; + } +}; + + + +///////////////////////// +// SHOW DEBUG SETTINGS // +///////////////////////// + + +class LLAdvancedShowDebugSettings : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLFloaterReg::showInstance("settings_debug",userdata); + return true; + } +}; + + + +//////////////////////// +// VIEW ADMIN OPTIONS // +//////////////////////// + +class LLAdvancedEnableViewAdminOptions : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + // Don't enable in god mode since the admin menu is shown anyway. + // Only enable if the user has set the appropriate debug setting. + bool new_value = !gAgent.getAgentAccess().isGodlikeWithoutAdminMenuFakery() && gSavedSettings.getBOOL("AdminMenu"); + return new_value; + } +}; + +class LLAdvancedToggleViewAdminOptions : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_admin_override_toggle(NULL); + return true; + } +}; + +class LLAdvancedCheckViewAdminOptions : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = check_admin_override(NULL) || gAgent.isGodlike(); + return new_value; + } +}; + +///////////////////////////////////// +// Enable Object Object Occlusion /// +///////////////////////////////////// +class LLAdvancedEnableObjectObjectOcclusion: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + + bool new_value = gGLManager.mHasOcclusionQuery; // && LLFeatureManager::getInstance()->isFeatureAvailable(userdata.asString()); + return new_value; +} +}; + +///////////////////////////////////// +// Enable Framebuffer Objects /// +///////////////////////////////////// +class LLAdvancedEnableRenderFBO: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = gGLManager.mHasFramebufferObject; + return new_value; + } +}; + +///////////////////////////////////// +// Enable Deferred Rendering /// +///////////////////////////////////// +class LLAdvancedEnableRenderDeferred: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = gSavedSettings.getBOOL("RenderUseFBO") && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_WINDLIGHT > 0) && + LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_AVATAR) > 0; + return new_value; + } +}; + +///////////////////////////////////// +// Enable Deferred Rendering sub-options +///////////////////////////////////// +class LLAdvancedEnableRenderDeferredOptions: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = gSavedSettings.getBOOL("RenderUseFBO") && gSavedSettings.getBOOL("RenderDeferred"); + return new_value; + } +}; + + + +////////////////// +// ADMIN STATUS // +////////////////// + + +class LLAdvancedRequestAdminStatus : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_god_mode(NULL); + return true; + } +}; + +class LLAdvancedLeaveAdminStatus : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_leave_god_mode(NULL); + return true; + } +}; + +////////////////////////// +// Advanced > Debugging // +////////////////////////// + + +class LLAdvancedForceErrorBreakpoint : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + force_error_breakpoint(NULL); + return true; + } +}; + +class LLAdvancedForceErrorLlerror : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + force_error_llerror(NULL); + return true; + } +}; +class LLAdvancedForceErrorBadMemoryAccess : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + force_error_bad_memory_access(NULL); + return true; + } +}; + +class LLAdvancedForceErrorInfiniteLoop : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + force_error_infinite_loop(NULL); + return true; + } +}; + +class LLAdvancedForceErrorSoftwareException : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + force_error_software_exception(NULL); + return true; + } +}; + +class LLAdvancedForceErrorDriverCrash : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + force_error_driver_crash(NULL); + return true; + } +}; + +class LLAdvancedForceErrorDisconnectViewer : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_disconnect_viewer(NULL); + return true; +} +}; + + +#ifdef TOGGLE_HACKED_GODLIKE_VIEWER + +class LLAdvancedHandleToggleHackedGodmode : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_toggle_hacked_godmode(NULL); + return true; + } +}; + +class LLAdvancedCheckToggleHackedGodmode : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + check_toggle_hacked_godmode(NULL); + return true; + } +}; + +class LLAdvancedEnableToggleHackedGodmode : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = enable_toggle_hacked_godmode(NULL); + return new_value; + } +}; +#endif + + +// +////------------------------------------------------------------------- +//// Advanced menu +////------------------------------------------------------------------- + +////////////////// +// ADMIN MENU // +////////////////// + +// Admin > Object +class LLAdminForceTakeCopy : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + force_take_copy(NULL); + return true; + } +}; + +class LLAdminHandleObjectOwnerSelf : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_object_owner_self(NULL); + return true; + } +}; +class LLAdminHandleObjectOwnerPermissive : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_object_owner_permissive(NULL); + return true; + } +}; + +class LLAdminHandleForceDelete : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_force_delete(NULL); + return true; + } +}; + +class LLAdminHandleObjectLock : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_object_lock(NULL); + return true; + } +}; + +class LLAdminHandleObjectAssetIDs: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_object_asset_ids(NULL); + return true; + } +}; + +//Admin >Parcel +class LLAdminHandleForceParcelOwnerToMe: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_force_parcel_owner_to_me(NULL); + return true; + } +}; +class LLAdminHandleForceParcelToContent: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_force_parcel_to_content(NULL); + return true; + } +}; +class LLAdminHandleClaimPublicLand: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_claim_public_land(NULL); + return true; + } +}; + +// Admin > Region +class LLAdminHandleRegionDumpTempAssetData: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_region_dump_temp_asset_data(NULL); + return true; + } +}; +//Admin (Top Level) + +class LLAdminOnSaveState: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLPanelRegionTools::onSaveState(NULL); + return true; +} +}; + + +//----------------------------------------------------------------------------- +// cleanup_menus() +//----------------------------------------------------------------------------- +void cleanup_menus() +{ + delete gMenuParcelObserver; + gMenuParcelObserver = NULL; + + delete gMenuAvatarSelf; + gMenuAvatarSelf = NULL; + + delete gMenuAvatarOther; + gMenuAvatarOther = NULL; + + delete gMenuObject; + gMenuObject = NULL; + + delete gMenuAttachmentSelf; + gMenuAttachmentSelf = NULL; + + delete gMenuAttachmentOther; + gMenuAttachmentSelf = NULL; + + delete gMenuLand; + gMenuLand = NULL; + + delete gMenuBarView; + gMenuBarView = NULL; + + delete gPopupMenuView; + gPopupMenuView = NULL; + + delete gMenuHolder; + gMenuHolder = NULL; +} + +//----------------------------------------------------------------------------- +// Object pie menu +//----------------------------------------------------------------------------- + +class LLObjectReportAbuse : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if (objectp) + { + LLFloaterReporter::showFromObject(objectp->getID()); + } + return true; + } +}; + +// Enabled it you clicked an object +class LLObjectEnableReportAbuse : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLSelectMgr::getInstance()->getSelection()->getObjectCount() != 0; + return new_value; + } +}; + +void handle_object_touch() +{ + LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if (!object) return; + + LLPickInfo pick = LLToolPie::getInstance()->getPick(); + + LLMessageSystem *msg = gMessageSystem; + + msg->newMessageFast(_PREHASH_ObjectGrab); + msg->nextBlockFast( _PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast( _PREHASH_ObjectData); + msg->addU32Fast( _PREHASH_LocalID, object->mLocalID); + msg->addVector3Fast(_PREHASH_GrabOffset, LLVector3::zero ); + msg->nextBlock("SurfaceInfo"); + msg->addVector3("UVCoord", LLVector3(pick.mUVCoords)); + msg->addVector3("STCoord", LLVector3(pick.mSTCoords)); + msg->addS32Fast(_PREHASH_FaceIndex, pick.mObjectFace); + msg->addVector3("Position", pick.mIntersection); + msg->addVector3("Normal", pick.mNormal); + msg->addVector3("Binormal", pick.mBinormal); + msg->sendMessage( object->getRegion()->getHost()); + + // *NOTE: Hope the packets arrive safely and in order or else + // there will be some problems. + // *TODO: Just fix this bad assumption. + msg->newMessageFast(_PREHASH_ObjectDeGrab); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_ObjectData); + msg->addU32Fast(_PREHASH_LocalID, object->mLocalID); + msg->nextBlock("SurfaceInfo"); + msg->addVector3("UVCoord", LLVector3(pick.mUVCoords)); + msg->addVector3("STCoord", LLVector3(pick.mSTCoords)); + msg->addS32Fast(_PREHASH_FaceIndex, pick.mObjectFace); + msg->addVector3("Position", pick.mIntersection); + msg->addVector3("Normal", pick.mNormal); + msg->addVector3("Binormal", pick.mBinormal); + msg->sendMessage(object->getRegion()->getHost()); +} + +static void init_default_item_label(const std::string& item_name) +{ + boost::unordered_map::iterator it = sDefaultItemLabels.find(item_name); + if (it == sDefaultItemLabels.end()) + { + // *NOTE: This will not work for items of type LLMenuItemCheckGL because they return boolean value + // (doesn't seem to matter much ATM). + LLStringExplicit default_label = gMenuHolder->childGetValue(item_name).asString(); + if (!default_label.empty()) + { + sDefaultItemLabels.insert(std::pair(item_name, default_label)); + } + } +} + +static LLStringExplicit get_default_item_label(const std::string& item_name) +{ + LLStringExplicit res(""); + boost::unordered_map::iterator it = sDefaultItemLabels.find(item_name); + if (it != sDefaultItemLabels.end()) + { + res = it->second; + } + + return res; +} + + +bool enable_object_touch(LLUICtrl* ctrl) +{ + LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + + bool new_value = obj && obj->flagHandleTouch(); + + std::string item_name = ctrl->getName(); + init_default_item_label(item_name); + + // Update label based on the node touch name if available. + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); + if (node && node->mValid && !node->mTouchName.empty()) + { + gMenuHolder->childSetText(item_name, node->mTouchName); + } + else + { + gMenuHolder->childSetText(item_name, get_default_item_label(item_name)); + } + + return new_value; +}; + +//void label_touch(std::string& label, void*) +//{ +// LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); +// if (node && node->mValid && !node->mTouchName.empty()) +// { +// label.assign(node->mTouchName); +// } +// else +// { +// label.assign("Touch"); +// } +//} + +void handle_object_open() +{ + LLFloaterReg::showInstance("openobject"); +} + +bool enable_object_open() +{ + // Look for contents in root object, which is all the LLFloaterOpenObject + // understands. + LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if (!obj) return false; + + LLViewerObject* root = obj->getRootEdit(); + if (!root) return false; + + return root->allowOpen(); +} + + +class LLViewJoystickFlycam : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_toggle_flycam(); + return true; + } +}; + +class LLViewCheckJoystickFlycam : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLViewerJoystick::getInstance()->getOverrideCamera(); + return new_value; + } +}; + +void handle_toggle_flycam() +{ + LLViewerJoystick::getInstance()->toggleFlycam(); +} + +class LLObjectBuild : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if (gAgentCamera.getFocusOnAvatar() && !LLToolMgr::getInstance()->inEdit() && gSavedSettings.getBOOL("EditCameraMovement") ) + { + // zoom in if we're looking at the avatar + gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); + gAgentCamera.setFocusGlobal(LLToolPie::getInstance()->getPick()); + gAgentCamera.cameraZoomIn(0.666f); + gAgentCamera.cameraOrbitOver( 30.f * DEG_TO_RAD ); + gViewerWindow->moveCursorToCenter(); + } + else if ( gSavedSettings.getBOOL("EditCameraMovement") ) + { + gAgentCamera.setFocusGlobal(LLToolPie::getInstance()->getPick()); + gViewerWindow->moveCursorToCenter(); + } + + LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); + LLToolMgr::getInstance()->getCurrentToolset()->selectTool( LLToolCompCreate::getInstance() ); + + // Could be first use + //LLFirstUse::useBuild(); + return true; + } +}; + + +void handle_object_edit() +{ + LLViewerParcelMgr::getInstance()->deselectLand(); + + if (gAgentCamera.getFocusOnAvatar() && !LLToolMgr::getInstance()->inEdit()) + { + LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); + + if (selection->getSelectType() == SELECT_TYPE_HUD || !gSavedSettings.getBOOL("EditCameraMovement")) + { + // always freeze camera in space, even if camera doesn't move + // so, for example, follow cam scripts can't affect you when in build mode + gAgentCamera.setFocusGlobal(gAgentCamera.calcFocusPositionTargetGlobal(), LLUUID::null); + gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); + } + else + { + gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); + LLViewerObject* selected_objectp = selection->getFirstRootObject(); + if (selected_objectp) + { + // zoom in on object center instead of where we clicked, as we need to see the manipulator handles + gAgentCamera.setFocusGlobal(selected_objectp->getPositionGlobal(), selected_objectp->getID()); + gAgentCamera.cameraZoomIn(0.666f); + gAgentCamera.cameraOrbitOver( 30.f * DEG_TO_RAD ); + gViewerWindow->moveCursorToCenter(); + } + } + } + + LLFloaterReg::showInstance("build"); + + LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); + gFloaterTools->setEditTool( LLToolCompTranslate::getInstance() ); + + LLViewerJoystick::getInstance()->moveObjects(true); + LLViewerJoystick::getInstance()->setNeedsReset(true); + + // Could be first use + //LLFirstUse::useBuild(); + return; +} + +void handle_object_inspect() +{ + LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); + LLViewerObject* selected_objectp = selection->getFirstRootObject(); + if (selected_objectp) + { + LLSD key; + key["task"] = "task"; + LLSideTray::getInstance()->showPanel("sidepanel_inventory", key); + } + + /* + // Old floater properties + LLFloaterReg::showInstance("inspect", LLSD()); + */ +} + +//--------------------------------------------------------------------------- +// Land pie menu +//--------------------------------------------------------------------------- +class LLLandBuild : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLViewerParcelMgr::getInstance()->deselectLand(); + + if (gAgentCamera.getFocusOnAvatar() && !LLToolMgr::getInstance()->inEdit() && gSavedSettings.getBOOL("EditCameraMovement") ) + { + // zoom in if we're looking at the avatar + gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); + gAgentCamera.setFocusGlobal(LLToolPie::getInstance()->getPick()); + gAgentCamera.cameraZoomIn(0.666f); + gAgentCamera.cameraOrbitOver( 30.f * DEG_TO_RAD ); + gViewerWindow->moveCursorToCenter(); + } + else if ( gSavedSettings.getBOOL("EditCameraMovement") ) + { + // otherwise just move focus + gAgentCamera.setFocusGlobal(LLToolPie::getInstance()->getPick()); + gViewerWindow->moveCursorToCenter(); + } + + + LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); + LLToolMgr::getInstance()->getCurrentToolset()->selectTool( LLToolCompCreate::getInstance() ); + + // Could be first use + //LLFirstUse::useBuild(); + return true; + } +}; + +class LLLandBuyPass : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLPanelLandGeneral::onClickBuyPass((void *)FALSE); + return true; + } +}; + +class LLLandEnableBuyPass : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLPanelLandGeneral::enableBuyPass(NULL); + return new_value; + } +}; + +// BUG: Should really check if CLICK POINT is in a parcel where you can build. +BOOL enable_land_build(void*) +{ + if (gAgent.isGodlike()) return TRUE; + if (gAgent.inPrelude()) return FALSE; + + BOOL can_build = FALSE; + LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (agent_parcel) + { + can_build = agent_parcel->getAllowModify(); + } + return can_build; +} + +// BUG: Should really check if OBJECT is in a parcel where you can build. +BOOL enable_object_build(void*) +{ + if (gAgent.isGodlike()) return TRUE; + if (gAgent.inPrelude()) return FALSE; + + BOOL can_build = FALSE; + LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (agent_parcel) + { + can_build = agent_parcel->getAllowModify(); + } + return can_build; +} + +bool enable_object_edit() +{ + // *HACK: The new "prelude" Help Islands have a build sandbox area, + // so users need the Edit and Create pie menu options when they are + // there. Eventually this needs to be replaced with code that only + // lets you edit objects if you have permission to do so (edit perms, + // group edit, god). See also lltoolbar.cpp. JC + bool enable = false; + if (gAgent.inPrelude()) + { + enable = LLViewerParcelMgr::getInstance()->allowAgentBuild() + || LLSelectMgr::getInstance()->getSelection()->isAttachment(); + } + else if (LLSelectMgr::getInstance()->selectGetAllValidAndObjectsFound()) + { + enable = true; + } + + return enable; +} + +// mutually exclusive - show either edit option or build in menu +bool enable_object_build() +{ + return !enable_object_edit(); +} + +class LLSelfRemoveAllAttachments : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLAgentWearables::userRemoveAllAttachments(); + return true; + } +}; + +class LLSelfEnableRemoveAllAttachments : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = false; + if (isAgentAvatarValid()) + { + for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); + iter != gAgentAvatarp->mAttachmentPoints.end(); ) + { + LLVOAvatar::attachment_map_t::iterator curiter = iter++; + LLViewerJointAttachment* attachment = curiter->second; + if (attachment->getNumObjects() > 0) + { + new_value = true; + break; + } + } + } + return new_value; + } +}; + +BOOL enable_has_attachments(void*) +{ + + return FALSE; +} + +//--------------------------------------------------------------------------- +// Avatar pie menu +//--------------------------------------------------------------------------- +//void handle_follow(void *userdata) +//{ +// // follow a given avatar by ID +// LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); +// if (objectp) +// { +// gAgent.startFollowPilot(objectp->getID()); +// } +//} + +bool enable_object_mute() +{ + LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if (!object) return false; + + LLVOAvatar* avatar = find_avatar_from_object(object); + if (avatar) + { + // It's an avatar + LLNameValue *lastname = avatar->getNVPair("LastName"); + bool is_linden = + lastname && !LLStringUtil::compareStrings(lastname->getString(), "Linden"); + bool is_self = avatar->isSelf(); + return !is_linden && !is_self; + } + else + { + // Just a regular object + return LLSelectMgr::getInstance()->getSelection()-> + contains( object, SELECT_ALL_TES ); + } +} + +class LLObjectMute : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if (!object) return true; + + LLUUID id; + std::string name; + LLMute::EType type; + LLVOAvatar* avatar = find_avatar_from_object(object); + if (avatar) + { + id = avatar->getID(); + + LLNameValue *firstname = avatar->getNVPair("FirstName"); + LLNameValue *lastname = avatar->getNVPair("LastName"); + if (firstname && lastname) + { + name = LLCacheName::buildFullName( + firstname->getString(), lastname->getString()); + } + + type = LLMute::AGENT; + } + else + { + // it's an object + id = object->getID(); + + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); + if (node) + { + name = node->mName; + } + + type = LLMute::OBJECT; + } + + LLMute mute(id, name, type); + if (LLMuteList::getInstance()->isMuted(mute.mID)) + { + LLMuteList::getInstance()->remove(mute); + } + else + { + LLMuteList::getInstance()->add(mute); + LLPanelBlockedList::showPanelAndSelect(mute.mID); + } + + return true; + } +}; + +bool handle_go_to() +{ + // try simulator autopilot + std::vector strings; + std::string val; + LLVector3d pos = LLToolPie::getInstance()->getPick().mPosGlobal; + val = llformat("%g", pos.mdV[VX]); + strings.push_back(val); + val = llformat("%g", pos.mdV[VY]); + strings.push_back(val); + val = llformat("%g", pos.mdV[VZ]); + strings.push_back(val); + send_generic_message("autopilot", strings); + + LLViewerParcelMgr::getInstance()->deselectLand(); + + if (isAgentAvatarValid() && !gSavedSettings.getBOOL("AutoPilotLocksCamera")) + { + gAgentCamera.setFocusGlobal(gAgentCamera.getFocusTargetGlobal(), gAgentAvatarp->getID()); + } + else + { + // Snap camera back to behind avatar + gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); + } + + // Could be first use + //LLFirstUse::useGoTo(); + return true; +} + +class LLGoToObject : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + return handle_go_to(); + } +}; + +class LLAvatarReportAbuse : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); + if(avatar) + { + LLFloaterReporter::showFromObject(avatar->getID()); + } + return true; + } +}; + + +//--------------------------------------------------------------------------- +// Parcel freeze, eject, etc. +//--------------------------------------------------------------------------- +bool callback_freeze(const LLSD& notification, const LLSD& response) +{ + LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if (0 == option || 1 == option) + { + U32 flags = 0x0; + if (1 == option) + { + // unfreeze + flags |= 0x1; + } + + LLMessageSystem* msg = gMessageSystem; + LLViewerObject* avatar = gObjectList.findObject(avatar_id); + + if (avatar) + { + msg->newMessage("FreezeUser"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgent.getID()); + msg->addUUID("SessionID", gAgent.getSessionID()); + msg->nextBlock("Data"); + msg->addUUID("TargetID", avatar_id ); + msg->addU32("Flags", flags ); + msg->sendReliable( avatar->getRegion()->getHost() ); + } + } + return false; +} + + +void handle_avatar_freeze(const LLSD& avatar_id) +{ + // Use avatar_id if available, otherwise default to right-click avatar + LLVOAvatar* avatar = NULL; + if (avatar_id.asUUID().notNull()) + { + avatar = find_avatar_from_object(avatar_id.asUUID()); + } + else + { + avatar = find_avatar_from_object( + LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); + } + + if( avatar ) + { + std::string fullname = avatar->getFullname(); + LLSD payload; + payload["avatar_id"] = avatar->getID(); + + if (!fullname.empty()) + { + LLSD args; + args["AVATAR_NAME"] = fullname; + LLNotificationsUtil::add("FreezeAvatarFullname", + args, + payload, + callback_freeze); + } + else + { + LLNotificationsUtil::add("FreezeAvatar", + LLSD(), + payload, + callback_freeze); + } + } +} + +class LLAvatarVisibleDebug : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + return gAgent.isGodlike(); + } +}; + +class LLAvatarDebug : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); + if( avatar ) + { + if (avatar->isSelf()) + { + ((LLVOAvatarSelf *)avatar)->dumpLocalTextures(); + } + llinfos << "Dumping temporary asset data to simulator logs for avatar " << avatar->getID() << llendl; + std::vector strings; + strings.push_back(avatar->getID().asString()); + LLUUID invoice; + send_generic_message("dumptempassetdata", strings, invoice); + LLFloaterReg::showInstance( "avatar_textures", LLSD(avatar->getID()) ); + } + return true; + } +}; + +bool callback_eject(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (2 == option) + { + // Cancel button. + return false; + } + LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); + bool ban_enabled = notification["payload"]["ban_enabled"].asBoolean(); + + if (0 == option) + { + // Eject button + LLMessageSystem* msg = gMessageSystem; + LLViewerObject* avatar = gObjectList.findObject(avatar_id); + + if (avatar) + { + U32 flags = 0x0; + msg->newMessage("EjectUser"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgent.getID() ); + msg->addUUID("SessionID", gAgent.getSessionID() ); + msg->nextBlock("Data"); + msg->addUUID("TargetID", avatar_id ); + msg->addU32("Flags", flags ); + msg->sendReliable( avatar->getRegion()->getHost() ); + } + } + else if (ban_enabled) + { + // This is tricky. It is similar to say if it is not an 'Eject' button, + // and it is also not an 'Cancle' button, and ban_enabled==ture, + // it should be the 'Eject and Ban' button. + LLMessageSystem* msg = gMessageSystem; + LLViewerObject* avatar = gObjectList.findObject(avatar_id); + + if (avatar) + { + U32 flags = 0x1; + msg->newMessage("EjectUser"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgent.getID() ); + msg->addUUID("SessionID", gAgent.getSessionID() ); + msg->nextBlock("Data"); + msg->addUUID("TargetID", avatar_id ); + msg->addU32("Flags", flags ); + msg->sendReliable( avatar->getRegion()->getHost() ); + } + } + return false; +} + +void handle_avatar_eject(const LLSD& avatar_id) +{ + // Use avatar_id if available, otherwise default to right-click avatar + LLVOAvatar* avatar = NULL; + if (avatar_id.asUUID().notNull()) + { + avatar = find_avatar_from_object(avatar_id.asUUID()); + } + else + { + avatar = find_avatar_from_object( + LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); + } + + if( avatar ) + { + LLSD payload; + payload["avatar_id"] = avatar->getID(); + std::string fullname = avatar->getFullname(); + + const LLVector3d& pos = avatar->getPositionGlobal(); + LLParcel* parcel = LLViewerParcelMgr::getInstance()->selectParcelAt(pos)->getParcel(); + + if (LLViewerParcelMgr::getInstance()->isParcelOwnedByAgent(parcel,GP_LAND_MANAGE_BANNED)) + { + payload["ban_enabled"] = true; + if (!fullname.empty()) + { + LLSD args; + args["AVATAR_NAME"] = fullname; + LLNotificationsUtil::add("EjectAvatarFullname", + args, + payload, + callback_eject); + } + else + { + LLNotificationsUtil::add("EjectAvatarFullname", + LLSD(), + payload, + callback_eject); + } + } + else + { + payload["ban_enabled"] = false; + if (!fullname.empty()) + { + LLSD args; + args["AVATAR_NAME"] = fullname; + LLNotificationsUtil::add("EjectAvatarFullnameNoBan", + args, + payload, + callback_eject); + } + else + { + LLNotificationsUtil::add("EjectAvatarNoBan", + LLSD(), + payload, + callback_eject); + } + } + } +} + +bool enable_freeze_eject(const LLSD& avatar_id) +{ + // Use avatar_id if available, otherwise default to right-click avatar + LLVOAvatar* avatar = NULL; + if (avatar_id.asUUID().notNull()) + { + avatar = find_avatar_from_object(avatar_id.asUUID()); + } + else + { + avatar = find_avatar_from_object( + LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); + } + if (!avatar) return false; + + // Gods can always freeze + if (gAgent.isGodlike()) return true; + + // Estate owners / managers can freeze + // Parcel owners can also freeze + const LLVector3& pos = avatar->getPositionRegion(); + const LLVector3d& pos_global = avatar->getPositionGlobal(); + LLParcel* parcel = LLViewerParcelMgr::getInstance()->selectParcelAt(pos_global)->getParcel(); + LLViewerRegion* region = avatar->getRegion(); + if (!region) return false; + + bool new_value = region->isOwnedSelf(pos); + if (!new_value || region->isOwnedGroup(pos)) + { + new_value = LLViewerParcelMgr::getInstance()->isParcelOwnedByAgent(parcel,GP_LAND_ADMIN); + } + return new_value; +} + + +void login_done(S32 which, void *user) +{ + llinfos << "Login done " << which << llendl; + + LLPanelLogin::closePanel(); +} + + +bool callback_leave_group(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) + { + LLMessageSystem *msg = gMessageSystem; + + msg->newMessageFast(_PREHASH_LeaveGroupRequest); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_GroupData); + msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID() ); + gAgent.sendReliableMessage(); + } + return false; +} + +void append_aggregate(std::string& string, const LLAggregatePermissions& ag_perm, PermissionBit bit, const char* txt) +{ + LLAggregatePermissions::EValue val = ag_perm.getValue(bit); + std::string buffer; + switch(val) + { + case LLAggregatePermissions::AP_NONE: + buffer = llformat( "* %s None\n", txt); + break; + case LLAggregatePermissions::AP_SOME: + buffer = llformat( "* %s Some\n", txt); + break; + case LLAggregatePermissions::AP_ALL: + buffer = llformat( "* %s All\n", txt); + break; + case LLAggregatePermissions::AP_EMPTY: + default: + break; + } + string.append(buffer); +} + +bool enable_buy_object() +{ + // In order to buy, there must only be 1 purchaseable object in + // the selection manger. + if(LLSelectMgr::getInstance()->getSelection()->getRootObjectCount() != 1) return false; + LLViewerObject* obj = NULL; + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); + if(node) + { + obj = node->getObject(); + if(!obj) return false; + + if( for_sale_selection(node) ) + { + // *NOTE: Is this needed? This checks to see if anyone owns the + // object, dating back to when we had "public" objects owned by + // no one. JC + if(obj->permAnyOwner()) return true; + } + } + return false; +} + +// Note: This will only work if the selected object's data has been +// received by the viewer and cached in the selection manager. +void handle_buy_object(LLSaleInfo sale_info) +{ + if(!LLSelectMgr::getInstance()->selectGetAllRootsValid()) + { + LLNotificationsUtil::add("UnableToBuyWhileDownloading"); + return; + } + + LLUUID owner_id; + std::string owner_name; + BOOL owners_identical = LLSelectMgr::getInstance()->selectGetOwner(owner_id, owner_name); + if (!owners_identical) + { + LLNotificationsUtil::add("CannotBuyObjectsFromDifferentOwners"); + return; + } + + LLPermissions perm; + BOOL valid = LLSelectMgr::getInstance()->selectGetPermissions(perm); + LLAggregatePermissions ag_perm; + valid &= LLSelectMgr::getInstance()->selectGetAggregatePermissions(ag_perm); + if(!valid || !sale_info.isForSale() || !perm.allowTransferTo(gAgent.getID())) + { + LLNotificationsUtil::add("ObjectNotForSale"); + return; + } + + LLFloaterBuy::show(sale_info); +} + + +void handle_buy_contents(LLSaleInfo sale_info) +{ + LLFloaterBuyContents::show(sale_info); +} + +void handle_region_dump_temp_asset_data(void*) +{ + llinfos << "Dumping temporary asset data to simulator logs" << llendl; + std::vector strings; + LLUUID invoice; + send_generic_message("dumptempassetdata", strings, invoice); +} + +void handle_region_clear_temp_asset_data(void*) +{ + llinfos << "Clearing temporary asset data" << llendl; + std::vector strings; + LLUUID invoice; + send_generic_message("cleartempassetdata", strings, invoice); +} + +void handle_region_dump_settings(void*) +{ + LLViewerRegion* regionp = gAgent.getRegion(); + if (regionp) + { + llinfos << "Damage: " << (regionp->getAllowDamage() ? "on" : "off") << llendl; + llinfos << "Landmark: " << (regionp->getAllowLandmark() ? "on" : "off") << llendl; + llinfos << "SetHome: " << (regionp->getAllowSetHome() ? "on" : "off") << llendl; + llinfos << "ResetHome: " << (regionp->getResetHomeOnTeleport() ? "on" : "off") << llendl; + llinfos << "SunFixed: " << (regionp->getSunFixed() ? "on" : "off") << llendl; + llinfos << "BlockFly: " << (regionp->getBlockFly() ? "on" : "off") << llendl; + llinfos << "AllowP2P: " << (regionp->getAllowDirectTeleport() ? "on" : "off") << llendl; + llinfos << "Water: " << (regionp->getWaterHeight()) << llendl; + } +} + +void handle_dump_group_info(void *) +{ + gAgent.dumpGroupInfo(); +} + +void handle_dump_capabilities_info(void *) +{ + LLViewerRegion* regionp = gAgent.getRegion(); + if (regionp) + { + regionp->logActiveCapabilities(); + } +} + +void handle_dump_region_object_cache(void*) +{ + LLViewerRegion* regionp = gAgent.getRegion(); + if (regionp) + { + regionp->dumpCache(); + } +} + +void handle_dump_focus() +{ + LLUICtrl *ctrl = dynamic_cast(gFocusMgr.getKeyboardFocus()); + + llinfos << "Keyboard focus " << (ctrl ? ctrl->getName() : "(none)") << llendl; +} + +class LLSelfStandUp : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gAgent.standUp(); + return true; + } +}; + +bool enable_standup_self() +{ + return isAgentAvatarValid() && gAgentAvatarp->isSitting(); +} + +class LLSelfSitDown : public view_listener_t + { + bool handleEvent(const LLSD& userdata) + { + gAgent.sitDown(); + return true; + } + }; + +bool enable_sitdown_self() +{ + return isAgentAvatarValid() && !gAgentAvatarp->isSitting() && !gAgent.getFlying(); +} + +// Used from the login screen to aid in UI work on side tray +void handle_show_side_tray() +{ + LLSideTray* side_tray = LLSideTray::getInstance(); + LLView* root = gViewerWindow->getRootView(); + // automatically removes and re-adds if there already + root->addChild(side_tray); +} + +// Toggle one of "People" panel tabs in side tray. +class LLTogglePanelPeopleTab : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string panel_name = userdata.asString(); + + LLSD param; + param["people_panel_tab_name"] = panel_name; + + static LLPanel* friends_panel = NULL; + static LLPanel* groups_panel = NULL; + static LLPanel* nearby_panel = NULL; + + if (panel_name == "friends_panel") + { + return togglePeoplePanel(friends_panel, panel_name, param); + } + else if (panel_name == "groups_panel") + { + return togglePeoplePanel(groups_panel, panel_name, param); + } + else if (panel_name == "nearby_panel") + { + return togglePeoplePanel(nearby_panel, panel_name, param); + } + else + { + return false; + } + } + + static bool togglePeoplePanel(LLPanel* &panel, const std::string& panel_name, const LLSD& param) + { + if(!panel) + { + panel = LLSideTray::getInstance()->getPanel(panel_name); + if(!panel) + return false; + } + + LLSideTray::getInstance()->togglePanel(panel, "panel_people", param); + + return true; + } +}; + +BOOL check_admin_override(void*) +{ + return gAgent.getAdminOverride(); +} + +void handle_admin_override_toggle(void*) +{ + gAgent.setAdminOverride(!gAgent.getAdminOverride()); + + // The above may have affected which debug menus are visible + show_debug_menus(); +} + +void handle_god_mode(void*) +{ + gAgent.requestEnterGodMode(); +} + +void handle_leave_god_mode(void*) +{ + gAgent.requestLeaveGodMode(); +} + +void set_god_level(U8 god_level) +{ + U8 old_god_level = gAgent.getGodLevel(); + gAgent.setGodLevel( god_level ); + LLViewerParcelMgr::getInstance()->notifyObservers(); + + // God mode changes region visibility + LLWorldMap::getInstance()->reloadItems(true); + + // inventory in items may change in god mode + gObjectList.dirtyAllObjectInventory(); + + if(gViewerWindow) + { + gViewerWindow->setMenuBackgroundColor(god_level > GOD_NOT, + LLGridManager::getInstance()->isInProductionGrid()); + } + + LLSD args; + if(god_level > GOD_NOT) + { + args["LEVEL"] = llformat("%d",(S32)god_level); + LLNotificationsUtil::add("EnteringGodMode", args); + } + else + { + args["LEVEL"] = llformat("%d",(S32)old_god_level); + LLNotificationsUtil::add("LeavingGodMode", args); + } + + // changing god-level can affect which menus we see + show_debug_menus(); + + // changing god-level can invalidate search results + LLFloaterSearch *search = dynamic_cast(LLFloaterReg::getInstance("search")); + if (search) + { + search->godLevelChanged(god_level); + } +} + +#ifdef TOGGLE_HACKED_GODLIKE_VIEWER +void handle_toggle_hacked_godmode(void*) +{ + gHackGodmode = !gHackGodmode; + set_god_level(gHackGodmode ? GOD_MAINTENANCE : GOD_NOT); +} + +BOOL check_toggle_hacked_godmode(void*) +{ + return gHackGodmode; +} + +bool enable_toggle_hacked_godmode(void*) +{ + return !LLGridManager::getInstance()->isInProductionGrid(); +} +#endif + +void process_grant_godlike_powers(LLMessageSystem* msg, void**) +{ + LLUUID agent_id; + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); + LLUUID session_id; + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); + if((agent_id == gAgent.getID()) && (session_id == gAgent.getSessionID())) + { + U8 god_level; + msg->getU8Fast(_PREHASH_GrantData, _PREHASH_GodLevel, god_level); + set_god_level(god_level); + } + else + { + llwarns << "Grant godlike for wrong agent " << agent_id << llendl; + } +} + +/* +class LLHaveCallingcard : public LLInventoryCollectFunctor +{ +public: + LLHaveCallingcard(const LLUUID& agent_id); + virtual ~LLHaveCallingcard() {} + virtual bool operator()(LLInventoryCategory* cat, + LLInventoryItem* item); + BOOL isThere() const { return mIsThere;} +protected: + LLUUID mID; + BOOL mIsThere; +}; + +LLHaveCallingcard::LLHaveCallingcard(const LLUUID& agent_id) : + mID(agent_id), + mIsThere(FALSE) +{ +} + +bool LLHaveCallingcard::operator()(LLInventoryCategory* cat, + LLInventoryItem* item) +{ + if(item) + { + if((item->getType() == LLAssetType::AT_CALLINGCARD) + && (item->getCreatorUUID() == mID)) + { + mIsThere = TRUE; + } + } + return FALSE; +} +*/ + +BOOL is_agent_mappable(const LLUUID& agent_id) +{ + const LLRelationship* buddy_info = NULL; + bool is_friend = LLAvatarActions::isFriend(agent_id); + + if (is_friend) + buddy_info = LLAvatarTracker::instance().getBuddyInfo(agent_id); + + return (buddy_info && + buddy_info->isOnline() && + buddy_info->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION) + ); +} + + +// Enable a menu item when you don't have someone's card. +class LLAvatarEnableAddFriend : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); + bool new_value = avatar && !LLAvatarActions::isFriend(avatar->getID()); + return new_value; + } +}; + +void request_friendship(const LLUUID& dest_id) +{ + LLViewerObject* dest = gObjectList.findObject(dest_id); + if(dest && dest->isAvatar()) + { + std::string full_name; + LLNameValue* nvfirst = dest->getNVPair("FirstName"); + LLNameValue* nvlast = dest->getNVPair("LastName"); + if(nvfirst && nvlast) + { + full_name = LLCacheName::buildFullName( + nvfirst->getString(), nvlast->getString()); + } + if (!full_name.empty()) + { + LLAvatarActions::requestFriendshipDialog(dest_id, full_name); + } + else + { + LLNotificationsUtil::add("CantOfferFriendship"); + } + } +} + + +class LLEditEnableCustomizeAvatar : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = gAgentWearables.areWearablesLoaded(); + return new_value; + } +}; + +class LLEnableEditShape : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + return gAgentWearables.isWearableModifiable(LLWearableType::WT_SHAPE, 0); + } +}; + +bool is_object_sittable() +{ + LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + + if (object && object->getPCode() == LL_PCODE_VOLUME) + { + return true; + } + else + { + return false; + } +} + + +// only works on pie menu +void handle_object_sit_or_stand() +{ + LLPickInfo pick = LLToolPie::getInstance()->getPick(); + LLViewerObject *object = pick.getObject();; + if (!object || pick.mPickType == LLPickInfo::PICK_FLORA) + { + return; + } + + if (sitting_on_selection()) + { + gAgent.standUp(); + return; + } + + // get object selection offset + + if (object && object->getPCode() == LL_PCODE_VOLUME) + { + + gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_TargetObject); + gMessageSystem->addUUIDFast(_PREHASH_TargetID, object->mID); + gMessageSystem->addVector3Fast(_PREHASH_Offset, pick.mObjectOffset); + + object->getRegion()->sendReliableMessage(); + } +} + +void near_sit_down_point(BOOL success, void *) +{ + if (success) + { + gAgent.setFlying(FALSE); + gAgent.setControlFlags(AGENT_CONTROL_SIT_ON_GROUND); + + // Might be first sit + //LLFirstUse::useSit(); + } +} + +class LLLandSit : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gAgent.standUp(); + LLViewerParcelMgr::getInstance()->deselectLand(); + + LLVector3d posGlobal = LLToolPie::getInstance()->getPick().mPosGlobal; + + LLQuaternion target_rot; + if (isAgentAvatarValid()) + { + target_rot = gAgentAvatarp->getRotation(); + } + else + { + target_rot = gAgent.getFrameAgent().getQuaternion(); + } + gAgent.startAutoPilotGlobal(posGlobal, "Sit", &target_rot, near_sit_down_point, NULL, 0.7f); + return true; + } +}; + +//------------------------------------------------------------------- +// Help menu functions +//------------------------------------------------------------------- + +// +// Major mode switching +// +void reset_view_final( BOOL proceed ); + +void handle_reset_view() +{ + if (gAgentCamera.cameraCustomizeAvatar()) + { + // switching to outfit selector should automagically save any currently edited wearable + LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "my_outfits")); + } + + gAgentCamera.switchCameraPreset(CAMERA_PRESET_REAR_VIEW); + reset_view_final( TRUE ); + LLFloaterCamera::resetCameraMode(); +} + +class LLViewResetView : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + handle_reset_view(); + return true; + } +}; + +// Note: extra parameters allow this function to be called from dialog. +void reset_view_final( BOOL proceed ) +{ + if( !proceed ) + { + return; + } + + gAgentCamera.resetView(TRUE, TRUE); + gAgentCamera.setLookAt(LOOKAT_TARGET_CLEAR); +} + +class LLViewLookAtLastChatter : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gAgentCamera.lookAtLastChat(); + return true; + } +}; + +class LLViewMouselook : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if (!gAgentCamera.cameraMouselook()) + { + gAgentCamera.changeCameraToMouselook(); + } + else + { + gAgentCamera.changeCameraToDefault(); + } + return true; + } +}; + +class LLViewDefaultUISize : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gSavedSettings.setF32("UIScaleFactor", 1.0f); + gSavedSettings.setBOOL("UIAutoScale", FALSE); + gViewerWindow->reshape(gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw()); + return true; + } +}; + +class LLEditDuplicate : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if(LLEditMenuHandler::gEditMenuHandler) + { + LLEditMenuHandler::gEditMenuHandler->duplicate(); + } + return true; + } +}; + +class LLEditEnableDuplicate : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canDuplicate(); + return new_value; + } +}; + +void handle_duplicate_in_place(void*) +{ + llinfos << "handle_duplicate_in_place" << llendl; + + LLVector3 offset(0.f, 0.f, 0.f); + LLSelectMgr::getInstance()->selectDuplicate(offset, TRUE); +} + +/* dead code 30-apr-2008 +void handle_deed_object_to_group(void*) +{ + LLUUID group_id; + + LLSelectMgr::getInstance()->selectGetGroup(group_id); + LLSelectMgr::getInstance()->sendOwner(LLUUID::null, group_id, FALSE); + LLViewerStats::getInstance()->incStat(LLViewerStats::ST_RELEASE_COUNT); +} + +BOOL enable_deed_object_to_group(void*) +{ + if(LLSelectMgr::getInstance()->getSelection()->isEmpty()) return FALSE; + LLPermissions perm; + LLUUID group_id; + + if (LLSelectMgr::getInstance()->selectGetGroup(group_id) && + gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) && + LLSelectMgr::getInstance()->selectGetPermissions(perm) && + perm.deedToGroup(gAgent.getID(), group_id)) + { + return TRUE; + } + return FALSE; +} + +*/ + + +/* + * No longer able to support viewer side manipulations in this way + * +void god_force_inv_owner_permissive(LLViewerObject* object, + LLInventoryObject::object_list_t* inventory, + S32 serial_num, + void*) +{ + typedef std::vector > item_array_t; + item_array_t items; + + LLInventoryObject::object_list_t::const_iterator inv_it = inventory->begin(); + LLInventoryObject::object_list_t::const_iterator inv_end = inventory->end(); + for ( ; inv_it != inv_end; ++inv_it) + { + if(((*inv_it)->getType() != LLAssetType::AT_CATEGORY)) + { + LLInventoryObject* obj = *inv_it; + LLPointer new_item = new LLViewerInventoryItem((LLViewerInventoryItem*)obj); + LLPermissions perm(new_item->getPermissions()); + perm.setMaskBase(PERM_ALL); + perm.setMaskOwner(PERM_ALL); + new_item->setPermissions(perm); + items.push_back(new_item); + } + } + item_array_t::iterator end = items.end(); + item_array_t::iterator it; + for(it = items.begin(); it != end; ++it) + { + // since we have the inventory item in the callback, it should not + // invalidate iteration through the selection manager. + object->updateInventory((*it), TASK_INVENTORY_ITEM_KEY, false); + } +} +*/ + +void handle_object_owner_permissive(void*) +{ + // only send this if they're a god. + if(gAgent.isGodlike()) + { + // do the objects. + LLSelectMgr::getInstance()->selectionSetObjectPermissions(PERM_BASE, TRUE, PERM_ALL, TRUE); + LLSelectMgr::getInstance()->selectionSetObjectPermissions(PERM_OWNER, TRUE, PERM_ALL, TRUE); + } +} + +void handle_object_owner_self(void*) +{ + // only send this if they're a god. + if(gAgent.isGodlike()) + { + LLSelectMgr::getInstance()->sendOwner(gAgent.getID(), gAgent.getGroupID(), TRUE); + } +} + +// Shortcut to set owner permissions to not editable. +void handle_object_lock(void*) +{ + LLSelectMgr::getInstance()->selectionSetObjectPermissions(PERM_OWNER, FALSE, PERM_MODIFY); +} + +void handle_object_asset_ids(void*) +{ + // only send this if they're a god. + if (gAgent.isGodlike()) + { + LLSelectMgr::getInstance()->sendGodlikeRequest("objectinfo", "assetids"); + } +} + +void handle_force_parcel_owner_to_me(void*) +{ + LLViewerParcelMgr::getInstance()->sendParcelGodForceOwner( gAgent.getID() ); +} + +void handle_force_parcel_to_content(void*) +{ + LLViewerParcelMgr::getInstance()->sendParcelGodForceToContent(); +} + +void handle_claim_public_land(void*) +{ + if (LLViewerParcelMgr::getInstance()->getSelectionRegion() != gAgent.getRegion()) + { + LLNotificationsUtil::add("ClaimPublicLand"); + return; + } + + LLVector3d west_south_global; + LLVector3d east_north_global; + LLViewerParcelMgr::getInstance()->getSelection(west_south_global, east_north_global); + LLVector3 west_south = gAgent.getPosAgentFromGlobal(west_south_global); + LLVector3 east_north = gAgent.getPosAgentFromGlobal(east_north_global); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessage("GodlikeMessage"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgent.getID()); + msg->addUUID("SessionID", gAgent.getSessionID()); + msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null); //not used + msg->nextBlock("MethodData"); + msg->addString("Method", "claimpublicland"); + msg->addUUID("Invoice", LLUUID::null); + std::string buffer; + buffer = llformat( "%f", west_south.mV[VX]); + msg->nextBlock("ParamList"); + msg->addString("Parameter", buffer); + buffer = llformat( "%f", west_south.mV[VY]); + msg->nextBlock("ParamList"); + msg->addString("Parameter", buffer); + buffer = llformat( "%f", east_north.mV[VX]); + msg->nextBlock("ParamList"); + msg->addString("Parameter", buffer); + buffer = llformat( "%f", east_north.mV[VY]); + msg->nextBlock("ParamList"); + msg->addString("Parameter", buffer); + gAgent.sendReliableMessage(); +} + + + +// HACK for easily testing new avatar geometry +void handle_god_request_avatar_geometry(void *) +{ + if (gAgent.isGodlike()) + { + LLSelectMgr::getInstance()->sendGodlikeRequest("avatar toggle", ""); + } +} + + +void derez_objects(EDeRezDestination dest, const LLUUID& dest_id) +{ + if(gAgentCamera.cameraMouselook()) + { + gAgentCamera.changeCameraToDefault(); + } + //gInventoryView->setPanelOpen(TRUE); + + std::string error; + LLDynamicArray derez_objects; + + // Check conditions that we can't deal with, building a list of + // everything that we'll actually be derezzing. + LLViewerRegion* first_region = NULL; + for (LLObjectSelection::valid_root_iterator iter = LLSelectMgr::getInstance()->getSelection()->valid_root_begin(); + iter != LLSelectMgr::getInstance()->getSelection()->valid_root_end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + LLViewerRegion* region = object->getRegion(); + if (!first_region) + { + first_region = region; + } + else + { + if(region != first_region) + { + // Derez doesn't work at all if the some of the objects + // are in regions besides the first object selected. + + // ...crosses region boundaries + error = "AcquireErrorObjectSpan"; + break; + } + } + if (object->isAvatar()) + { + // ...don't acquire avatars + continue; + } + + // If AssetContainers are being sent back, they will appear as + // boxes in the owner's inventory. + if (object->getNVPair("AssetContainer") + && dest != DRD_RETURN_TO_OWNER) + { + // this object is an asset container, derez its contents, not it + llwarns << "Attempt to derez deprecated AssetContainer object type not supported." << llendl; + /* + object->requestInventory(container_inventory_arrived, + (void *)(BOOL)(DRD_TAKE_INTO_AGENT_INVENTORY == dest)); + */ + continue; + } + BOOL can_derez_current = FALSE; + switch(dest) + { + case DRD_TAKE_INTO_AGENT_INVENTORY: + case DRD_TRASH: + if( (node->mPermissions->allowTransferTo(gAgent.getID()) && object->permModify()) + || (node->allowOperationOnNode(PERM_OWNER, GP_OBJECT_MANIPULATE)) ) + { + can_derez_current = TRUE; + } + break; + + case DRD_RETURN_TO_OWNER: + can_derez_current = TRUE; + break; + + default: + if((node->mPermissions->allowTransferTo(gAgent.getID()) + && object->permCopy()) + || gAgent.isGodlike()) + { + can_derez_current = TRUE; + } + break; + } + if(can_derez_current) + { + derez_objects.put(object); + } + } + + // This constant is based on (1200 - HEADER_SIZE) / 4 bytes per + // root. I lopped off a few (33) to provide a bit + // pad. HEADER_SIZE is currently 67 bytes, most of which is UUIDs. + // This gives us a maximum of 63500 root objects - which should + // satisfy anybody. + const S32 MAX_ROOTS_PER_PACKET = 250; + const S32 MAX_PACKET_COUNT = 254; + F32 packets = ceil((F32)derez_objects.count() / (F32)MAX_ROOTS_PER_PACKET); + if(packets > (F32)MAX_PACKET_COUNT) + { + error = "AcquireErrorTooManyObjects"; + } + + if(error.empty() && derez_objects.count() > 0) + { + U8 d = (U8)dest; + LLUUID tid; + tid.generate(); + U8 packet_count = (U8)packets; + S32 object_index = 0; + S32 objects_in_packet = 0; + LLMessageSystem* msg = gMessageSystem; + for(U8 packet_number = 0; + packet_number < packet_count; + ++packet_number) + { + msg->newMessageFast(_PREHASH_DeRezObject); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_AgentBlock); + msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID()); + msg->addU8Fast(_PREHASH_Destination, d); + msg->addUUIDFast(_PREHASH_DestinationID, dest_id); + msg->addUUIDFast(_PREHASH_TransactionID, tid); + msg->addU8Fast(_PREHASH_PacketCount, packet_count); + msg->addU8Fast(_PREHASH_PacketNumber, packet_number); + objects_in_packet = 0; + while((object_index < derez_objects.count()) + && (objects_in_packet++ < MAX_ROOTS_PER_PACKET)) + + { + LLViewerObject* object = derez_objects.get(object_index++); + msg->nextBlockFast(_PREHASH_ObjectData); + msg->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID()); + // VEFFECT: DerezObject + LLHUDEffectSpiral* effectp = (LLHUDEffectSpiral*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); + effectp->setPositionGlobal(object->getPositionGlobal()); + effectp->setColor(LLColor4U(gAgent.getEffectColor())); + } + msg->sendReliable(first_region->getHost()); + } + make_ui_sound("UISndObjectRezOut"); + + // Busy count decremented by inventory update, so only increment + // if will be causing an update. + if (dest != DRD_RETURN_TO_OWNER) + { + gViewerWindow->getWindow()->incBusyCount(); + } + } + else if(!error.empty()) + { + LLNotificationsUtil::add(error); + } +} + +void handle_take_copy() +{ + if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) return; + + const LLUUID category_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OBJECT); + derez_objects(DRD_ACQUIRE_TO_AGENT_INVENTORY, category_id); +} + +// You can return an object to its owner if it is on your land. +class LLObjectReturn : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) return true; + + mObjectSelection = LLSelectMgr::getInstance()->getEditSelection(); + + LLNotificationsUtil::add("ReturnToOwner", LLSD(), LLSD(), boost::bind(&LLObjectReturn::onReturnToOwner, this, _1, _2)); + return true; + } + + bool onReturnToOwner(const LLSD& notification, const LLSD& response) + { + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 == option) + { + // Ignore category ID for this derez destination. + derez_objects(DRD_RETURN_TO_OWNER, LLUUID::null); + } + + // drop reference to current selection + mObjectSelection = NULL; + return false; + } + +protected: + LLObjectSelectionHandle mObjectSelection; +}; + + +// Allow return to owner if one or more of the selected items is +// over land you own. +class LLObjectEnableReturn : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) + { + // Do not enable if nothing selected + return false; + } +#ifdef HACKED_GODLIKE_VIEWER + bool new_value = true; +#else + bool new_value = false; + if (gAgent.isGodlike()) + { + new_value = true; + } + else + { + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + // Estate owners and managers can always return objects. + if (region->canManageEstate()) + { + new_value = true; + } + else + { + struct f : public LLSelectedObjectFunctor + { + virtual bool apply(LLViewerObject* obj) + { + return + obj->permModify() || + obj->isReturnable(); + } + } func; + const bool firstonly = true; + new_value = LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, firstonly); + } + } + } +#endif + return new_value; + } +}; + +void force_take_copy(void*) +{ + if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) return; + const LLUUID category_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OBJECT); + derez_objects(DRD_FORCE_TO_GOD_INVENTORY, category_id); +} + +void handle_take() +{ + // we want to use the folder this was derezzed from if it's + // available. Otherwise, derez to the normal place. + if(LLSelectMgr::getInstance()->getSelection()->isEmpty()) + { + return; + } + + BOOL you_own_everything = TRUE; + BOOL locked_but_takeable_object = FALSE; + LLUUID category_id; + + for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); + iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + if(object) + { + if(!object->permYouOwner()) + { + you_own_everything = FALSE; + } + + if(!object->permMove()) + { + locked_but_takeable_object = TRUE; + } + } + if(node->mFolderID.notNull()) + { + if(category_id.isNull()) + { + category_id = node->mFolderID; + } + else if(category_id != node->mFolderID) + { + // we have found two potential destinations. break out + // now and send to the default location. + category_id.setNull(); + break; + } + } + } + if(category_id.notNull()) + { + // there is an unambiguous destination. See if this agent has + // such a location and it is not in the trash or library + if(!gInventory.getCategory(category_id)) + { + // nope, set to NULL. + category_id.setNull(); + } + if(category_id.notNull()) + { + // check trash + const LLUUID trash = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + if(category_id == trash || gInventory.isObjectDescendentOf(category_id, trash)) + { + category_id.setNull(); + } + + // check library + if(gInventory.isObjectDescendentOf(category_id, gInventory.getLibraryRootFolderID())) + { + category_id.setNull(); + } + + } + } + if(category_id.isNull()) + { + category_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OBJECT); + } + LLSD payload; + payload["folder_id"] = category_id; + + LLNotification::Params params("ConfirmObjectTakeLock"); + params.payload(payload); + params.functor.function(confirm_take); + + if(locked_but_takeable_object || + !you_own_everything) + { + if(locked_but_takeable_object && you_own_everything) + { + params.name("ConfirmObjectTakeLock"); + } + else if(!locked_but_takeable_object && !you_own_everything) + { + params.name("ConfirmObjectTakeNoOwn"); + } + else + { + params.name("ConfirmObjectTakeLockNoOwn"); + } + + LLNotifications::instance().add(params); + } + else + { + LLNotifications::instance().forceResponse(params, 0); + } +} + +void handle_object_show_inspector() +{ + LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); + LLViewerObject* objectp = selection->getFirstRootObject(TRUE); + if (!objectp) + { + return; + } + + LLSD params; + params["object_id"] = objectp->getID(); + LLFloaterReg::showInstance("inspect_object", params); +} + +void handle_avatar_show_inspector() +{ + LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); + if(avatar) + { + LLSD params; + params["avatar_id"] = avatar->getID(); + LLFloaterReg::showInstance("inspect_avatar", params); + } +} + + + +bool confirm_take(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if(enable_take() && (option == 0)) + { + derez_objects(DRD_TAKE_INTO_AGENT_INVENTORY, notification["payload"]["folder_id"].asUUID()); + } + return false; +} + +// You can take an item when it is public and transferrable, or when +// you own it. We err on the side of enabling the item when at least +// one item selected can be copied to inventory. +BOOL enable_take() +{ + if (sitting_on_selection()) + { + return FALSE; + } + + for (LLObjectSelection::valid_root_iterator iter = LLSelectMgr::getInstance()->getSelection()->valid_root_begin(); + iter != LLSelectMgr::getInstance()->getSelection()->valid_root_end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + if (object->isAvatar()) + { + // ...don't acquire avatars + continue; + } + +#ifdef HACKED_GODLIKE_VIEWER + return TRUE; +#else +# ifdef TOGGLE_HACKED_GODLIKE_VIEWER + if (!LLGridManager::getInstance()->isInProductionGrid() + && gAgent.isGodlike()) + { + return TRUE; + } +# endif + if((node->mPermissions->allowTransferTo(gAgent.getID()) + && object->permModify()) + || (node->mPermissions->getOwner() == gAgent.getID())) + { + return TRUE; + } +#endif + } + return FALSE; +} + + +void handle_buy_or_take() +{ + if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) + { + return; + } + + if (is_selection_buy_not_take()) + { + S32 total_price = selection_price(); + + if (total_price <= gStatusBar->getBalance() || total_price == 0) + { + handle_buy(); + } + else + { + LLStringUtil::format_map_t args; + args["AMOUNT"] = llformat("%d", total_price); + LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString( "BuyingCosts", args ), total_price ); + } + } + else + { + handle_take(); + } +} + +bool visible_buy_object() +{ + return is_selection_buy_not_take() && enable_buy_object(); +} + +bool visible_take_object() +{ + return !is_selection_buy_not_take() && enable_take(); +} + +bool tools_visible_buy_object() +{ + return is_selection_buy_not_take(); +} + +bool tools_visible_take_object() +{ + return !is_selection_buy_not_take(); +} + +class LLToolsEnableBuyOrTake : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool is_buy = is_selection_buy_not_take(); + bool new_value = is_buy ? enable_buy_object() : enable_take(); + return new_value; + } +}; + +// This is a small helper function to determine if we have a buy or a +// take in the selection. This method is to help with the aliasing +// problems of putting buy and take in the same pie menu space. After +// a fair amont of discussion, it was determined to prefer buy over +// take. The reasoning follows from the fact that when users walk up +// to buy something, they will click on one or more items. Thus, if +// anything is for sale, it becomes a buy operation, and the server +// will group all of the buy items, and copyable/modifiable items into +// one package and give the end user as much as the permissions will +// allow. If the user wanted to take something, they will select fewer +// and fewer items until only 'takeable' items are left. The one +// exception is if you own everything in the selection that is for +// sale, in this case, you can't buy stuff from yourself, so you can +// take it. +// return value = TRUE if selection is a 'buy'. +// FALSE if selection is a 'take' +BOOL is_selection_buy_not_take() +{ + for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); + iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* obj = node->getObject(); + if(obj && !(obj->permYouOwner()) && (node->mSaleInfo.isForSale())) + { + // you do not own the object and it is for sale, thus, + // it's a buy + return TRUE; + } + } + return FALSE; +} + +S32 selection_price() +{ + S32 total_price = 0; + for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); + iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* obj = node->getObject(); + if(obj && !(obj->permYouOwner()) && (node->mSaleInfo.isForSale())) + { + // you do not own the object and it is for sale. + // Add its price. + total_price += node->mSaleInfo.getSalePrice(); + } + } + + return total_price; +} +/* +bool callback_show_buy_currency(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 == option) + { + llinfos << "Loading page " << LLNotifications::instance().getGlobalString("BUY_CURRENCY_URL") << llendl; + LLWeb::loadURL(LLNotifications::instance().getGlobalString("BUY_CURRENCY_URL")); + } + return false; +} +*/ + +void show_buy_currency(const char* extra) +{ + // Don't show currency web page for branded clients. +/* + std::ostringstream mesg; + if (extra != NULL) + { + mesg << extra << "\n \n"; + } + mesg << "Go to " << LLNotifications::instance().getGlobalString("BUY_CURRENCY_URL")<< "\nfor information on purchasing currency?"; +*/ + LLSD args; + if (extra != NULL) + { + args["EXTRA"] = extra; + } + LLNotificationsUtil::add("PromptGoToCurrencyPage", args);//, LLSD(), callback_show_buy_currency); +} + +void handle_buy() +{ + if (LLSelectMgr::getInstance()->getSelection()->isEmpty()) return; + + LLSaleInfo sale_info; + BOOL valid = LLSelectMgr::getInstance()->selectGetSaleInfo(sale_info); + if (!valid) return; + + S32 price = sale_info.getSalePrice(); + + if (price > 0 && price > gStatusBar->getBalance()) + { + LLStringUtil::format_map_t args; + args["AMOUNT"] = llformat("%d", price); + LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("this_object_costs", args), price ); + return; + } + + if (sale_info.getSaleType() == LLSaleInfo::FS_CONTENTS) + { + handle_buy_contents(sale_info); + } + else + { + handle_buy_object(sale_info); + } +} + +bool anyone_copy_selection(LLSelectNode* nodep) +{ + bool perm_copy = (bool)(nodep->getObject()->permCopy()); + bool all_copy = (bool)(nodep->mPermissions->getMaskEveryone() & PERM_COPY); + return perm_copy && all_copy; +} + +bool for_sale_selection(LLSelectNode* nodep) +{ + return nodep->mSaleInfo.isForSale() + && nodep->mPermissions->getMaskOwner() & PERM_TRANSFER + && (nodep->mPermissions->getMaskOwner() & PERM_COPY + || nodep->mSaleInfo.getSaleType() != LLSaleInfo::FS_COPY); +} + +BOOL sitting_on_selection() +{ + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); + if (!node) + { + return FALSE; + } + + if (!node->mValid) + { + return FALSE; + } + + LLViewerObject* root_object = node->getObject(); + if (!root_object) + { + return FALSE; + } + + // Need to determine if avatar is sitting on this object + if (!isAgentAvatarValid()) return FALSE; + + return (gAgentAvatarp->isSitting() && gAgentAvatarp->getRoot() == root_object); +} + +class LLToolsSaveToInventory : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if(enable_save_into_inventory(NULL)) + { + derez_objects(DRD_SAVE_INTO_AGENT_INVENTORY, LLUUID::null); + } + return true; + } +}; + +class LLToolsSaveToObjectInventory : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); + if(node && (node->mValid) && (!node->mFromTaskID.isNull())) + { + // *TODO: check to see if the fromtaskid object exists. + derez_objects(DRD_SAVE_INTO_TASK_INVENTORY, node->mFromTaskID); + } + return true; + } +}; + +// Round the position of all root objects to the grid +class LLToolsSnapObjectXY : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + F64 snap_size = (F64)gSavedSettings.getF32("GridResolution"); + + for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); + iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* obj = node->getObject(); + if (obj->permModify()) + { + LLVector3d pos_global = obj->getPositionGlobal(); + F64 round_x = fmod(pos_global.mdV[VX], snap_size); + if (round_x < snap_size * 0.5) + { + // closer to round down + pos_global.mdV[VX] -= round_x; + } + else + { + // closer to round up + pos_global.mdV[VX] -= round_x; + pos_global.mdV[VX] += snap_size; + } + + F64 round_y = fmod(pos_global.mdV[VY], snap_size); + if (round_y < snap_size * 0.5) + { + pos_global.mdV[VY] -= round_y; + } + else + { + pos_global.mdV[VY] -= round_y; + pos_global.mdV[VY] += snap_size; + } + + obj->setPositionGlobal(pos_global, FALSE); + } + } + LLSelectMgr::getInstance()->sendMultipleUpdate(UPD_POSITION); + return true; + } +}; + +// Determine if the option to cycle between linked prims is shown +class LLToolsEnableSelectNextPart : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = (gSavedSettings.getBOOL("EditLinkedParts") && + !LLSelectMgr::getInstance()->getSelection()->isEmpty()); + return new_value; + } +}; + +// Cycle selection through linked children in selected object. +// FIXME: Order of children list is not always the same as sim's idea of link order. This may confuse +// resis. Need link position added to sim messages to address this. +class LLToolsSelectNextPart : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + S32 object_count = LLSelectMgr::getInstance()->getSelection()->getObjectCount(); + if (gSavedSettings.getBOOL("EditLinkedParts") && object_count) + { + LLViewerObject* selected = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); + if (selected && selected->getRootEdit()) + { + bool fwd = (userdata.asString() == "next"); + bool prev = (userdata.asString() == "previous"); + bool ifwd = (userdata.asString() == "includenext"); + bool iprev = (userdata.asString() == "includeprevious"); + LLViewerObject* to_select = NULL; + LLViewerObject::child_list_t children = selected->getRootEdit()->getChildren(); + children.push_front(selected->getRootEdit()); // need root in the list too + + for (LLViewerObject::child_list_t::iterator iter = children.begin(); iter != children.end(); ++iter) + { + if ((*iter)->isSelected()) + { + if (object_count > 1 && (fwd || prev)) // multiple selection, find first or last selected if not include + { + to_select = *iter; + if (fwd) + { + // stop searching if going forward; repeat to get last hit if backward + break; + } + } + else if ((object_count == 1) || (ifwd || iprev)) // single selection or include + { + if (fwd || ifwd) + { + ++iter; + while (iter != children.end() && ((*iter)->isAvatar() || (ifwd && (*iter)->isSelected()))) + { + ++iter; // skip sitting avatars and selected if include + } + } + else // backward + { + iter = (iter == children.begin() ? children.end() : iter); + --iter; + while (iter != children.begin() && ((*iter)->isAvatar() || (iprev && (*iter)->isSelected()))) + { + --iter; // skip sitting avatars and selected if include + } + } + iter = (iter == children.end() ? children.begin() : iter); + to_select = *iter; + break; + } + } + } + + if (to_select) + { + if (gFocusMgr.childHasKeyboardFocus(gFloaterTools)) + { + gFocusMgr.setKeyboardFocus(NULL); // force edit toolbox to commit any changes + } + if (fwd || prev) + { + LLSelectMgr::getInstance()->deselectAll(); + } + LLSelectMgr::getInstance()->selectObjectOnly(to_select); + return true; + } + } + } + return true; + } +}; + +class LLToolsStopAllAnimations : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gAgent.stopCurrentAnimations(); + return true; + } +}; + +class LLToolsReleaseKeys : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gAgent.forceReleaseControls(); + + return true; + } +}; + +class LLToolsEnableReleaseKeys : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + return gAgent.anyControlGrabbed(); + } +}; + + +class LLEditEnableCut : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canCut(); + return new_value; + } +}; + +class LLEditCut : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if( LLEditMenuHandler::gEditMenuHandler ) + { + LLEditMenuHandler::gEditMenuHandler->cut(); + } + return true; + } +}; + +class LLEditEnableCopy : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canCopy(); + return new_value; + } +}; + +class LLEditCopy : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if( LLEditMenuHandler::gEditMenuHandler ) + { + LLEditMenuHandler::gEditMenuHandler->copy(); + } + return true; + } +}; + +class LLEditEnablePaste : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canPaste(); + return new_value; + } +}; + +class LLEditPaste : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if( LLEditMenuHandler::gEditMenuHandler ) + { + LLEditMenuHandler::gEditMenuHandler->paste(); + } + return true; + } +}; + +class LLEditEnableDelete : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canDoDelete(); + return new_value; + } +}; + +class LLEditDelete : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + // If a text field can do a deletion, it gets precedence over deleting + // an object in the world. + if( LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canDoDelete()) + { + LLEditMenuHandler::gEditMenuHandler->doDelete(); + } + + // and close any pie/context menus when done + gMenuHolder->hideMenus(); + + // When deleting an object we may not actually be done + // Keep selection so we know what to delete when confirmation is needed about the delete + gMenuObject->hide(); + return true; + } +}; + +bool enable_object_delete() +{ + bool new_value = +#ifdef HACKED_GODLIKE_VIEWER + TRUE; +#else +# ifdef TOGGLE_HACKED_GODLIKE_VIEWER + (!LLGridManager::getInstance()->isInProductionGrid() + && gAgent.isGodlike()) || +# endif + LLSelectMgr::getInstance()->canDoDelete(); +#endif + return new_value; +} + +void handle_object_delete() +{ + + if (LLSelectMgr::getInstance()) + { + LLSelectMgr::getInstance()->doDelete(); + } + + // and close any pie/context menus when done + gMenuHolder->hideMenus(); + + // When deleting an object we may not actually be done + // Keep selection so we know what to delete when confirmation is needed about the delete + gMenuObject->hide(); + return; +} + +void handle_force_delete(void*) +{ + LLSelectMgr::getInstance()->selectForceDelete(); +} + +class LLViewEnableJoystickFlycam : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = (gSavedSettings.getBOOL("JoystickEnabled") && gSavedSettings.getBOOL("JoystickFlycamEnabled")); + return new_value; + } +}; + +class LLViewEnableLastChatter : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + // *TODO: add check that last chatter is in range + bool new_value = (gAgentCamera.cameraThirdPerson() && gAgent.getLastChatter().notNull()); + return new_value; + } +}; + +class LLEditEnableDeselect : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canDeselect(); + return new_value; + } +}; + +class LLEditDeselect : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if( LLEditMenuHandler::gEditMenuHandler ) + { + LLEditMenuHandler::gEditMenuHandler->deselect(); + } + return true; + } +}; + +class LLEditEnableSelectAll : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canSelectAll(); + return new_value; + } +}; + + +class LLEditSelectAll : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if( LLEditMenuHandler::gEditMenuHandler ) + { + LLEditMenuHandler::gEditMenuHandler->selectAll(); + } + return true; + } +}; + + +class LLEditEnableUndo : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canUndo(); + return new_value; + } +}; + +class LLEditUndo : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if( LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canUndo() ) + { + LLEditMenuHandler::gEditMenuHandler->undo(); + } + return true; + } +}; + +class LLEditEnableRedo : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canRedo(); + return new_value; + } +}; + +class LLEditRedo : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if( LLEditMenuHandler::gEditMenuHandler && LLEditMenuHandler::gEditMenuHandler->canRedo() ) + { + LLEditMenuHandler::gEditMenuHandler->redo(); + } + return true; + } +}; + + + +void print_object_info(void*) +{ + LLSelectMgr::getInstance()->selectionDump(); +} + +void print_agent_nvpairs(void*) +{ + LLViewerObject *objectp; + + llinfos << "Agent Name Value Pairs" << llendl; + + objectp = gObjectList.findObject(gAgentID); + if (objectp) + { + objectp->printNameValuePairs(); + } + else + { + llinfos << "Can't find agent object" << llendl; + } + + llinfos << "Camera at " << gAgentCamera.getCameraPositionGlobal() << llendl; +} + +void show_debug_menus() +{ + // this might get called at login screen where there is no menu so only toggle it if one exists + if ( gMenuBarView ) + { + BOOL debug = gSavedSettings.getBOOL("UseDebugMenus"); + BOOL qamode = gSavedSettings.getBOOL("QAMode"); + + gMenuBarView->setItemVisible("Advanced", debug); +// gMenuBarView->setItemEnabled("Advanced", debug); // Don't disable Advanced keyboard shortcuts when hidden + + gMenuBarView->setItemVisible("Debug", qamode); + gMenuBarView->setItemEnabled("Debug", qamode); + + gMenuBarView->setItemVisible("Develop", qamode); + gMenuBarView->setItemEnabled("Develop", qamode); + + // Server ('Admin') menu hidden when not in godmode. + const bool show_server_menu = (gAgent.getGodLevel() > GOD_NOT || (debug && gAgent.getAdminOverride())); + gMenuBarView->setItemVisible("Admin", show_server_menu); + gMenuBarView->setItemEnabled("Admin", show_server_menu); + } + if (gLoginMenuBarView) + { + BOOL debug = gSavedSettings.getBOOL("UseDebugMenus"); + gLoginMenuBarView->setItemVisible("Debug", debug); + gLoginMenuBarView->setItemEnabled("Debug", debug); + } +} + +void toggle_debug_menus(void*) +{ + BOOL visible = ! gSavedSettings.getBOOL("UseDebugMenus"); + gSavedSettings.setBOOL("UseDebugMenus", visible); + show_debug_menus(); +} + + +// LLUUID gExporterRequestID; +// std::string gExportDirectory; + +// LLUploadDialog *gExportDialog = NULL; + +// void handle_export_selected( void * ) +// { +// LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); +// if (selection->isEmpty()) +// { +// return; +// } +// llinfos << "Exporting selected objects:" << llendl; + +// gExporterRequestID.generate(); +// gExportDirectory = ""; + +// LLMessageSystem* msg = gMessageSystem; +// msg->newMessageFast(_PREHASH_ObjectExportSelected); +// msg->nextBlockFast(_PREHASH_AgentData); +// msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); +// msg->addUUIDFast(_PREHASH_RequestID, gExporterRequestID); +// msg->addS16Fast(_PREHASH_VolumeDetail, 4); + +// for (LLObjectSelection::root_iterator iter = selection->root_begin(); +// iter != selection->root_end(); iter++) +// { +// LLSelectNode* node = *iter; +// LLViewerObject* object = node->getObject(); +// msg->nextBlockFast(_PREHASH_ObjectData); +// msg->addUUIDFast(_PREHASH_ObjectID, object->getID()); +// llinfos << "Object: " << object->getID() << llendl; +// } +// msg->sendReliable(gAgent.getRegion()->getHost()); + +// gExportDialog = LLUploadDialog::modalUploadDialog("Exporting selected objects..."); +// } +// + + +class LLWorldSetHomeLocation : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + // we just send the message and let the server check for failure cases + // server will echo back a "Home position set." alert if it succeeds + // and the home location screencapture happens when that alert is recieved + gAgent.setStartPosition(START_LOCATION_ID_HOME); + return true; + } +}; + +class LLWorldTeleportHome : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gAgent.teleportHome(); + return true; + } +}; + +class LLWorldAlwaysRun : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + // as well as altering the default walk-vs-run state, + // we also change the *current* walk-vs-run state. + if (gAgent.getAlwaysRun()) + { + gAgent.clearAlwaysRun(); + gAgent.clearRunning(); + } + else + { + gAgent.setAlwaysRun(); + gAgent.setRunning(); + } + + // tell the simulator. + gAgent.sendWalkRun(gAgent.getAlwaysRun()); + + // Update Movement Controls according to AlwaysRun mode + LLFloaterMove::setAlwaysRunMode(gAgent.getAlwaysRun()); + + return true; + } +}; + +class LLWorldCheckAlwaysRun : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = gAgent.getAlwaysRun(); + return new_value; + } +}; + +class LLWorldSetAway : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if (gAgent.getAFK()) + { + gAgent.clearAFK(); + } + else + { + gAgent.setAFK(); + } + return true; + } +}; + +class LLWorldSetBusy : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if (gAgent.getBusy()) + { + gAgent.clearBusy(); + } + else + { + gAgent.setBusy(); + LLNotificationsUtil::add("BusyModeSet"); + } + return true; + } +}; + +class LLWorldCreateLandmark : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLSideTray::getInstance()->showPanel("panel_places", LLSD().with("type", "create_landmark")); + + return true; + } +}; + +class LLWorldPlaceProfile : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLSideTray::getInstance()->showPanel("panel_places", LLSD().with("type", "agent")); + + return true; + } +}; + +void handle_look_at_selection(const LLSD& param) +{ + const F32 PADDING_FACTOR = 1.75f; + BOOL zoom = (param.asString() == "zoom"); + if (!LLSelectMgr::getInstance()->getSelection()->isEmpty()) + { + gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); + + LLBBox selection_bbox = LLSelectMgr::getInstance()->getBBoxOfSelection(); + F32 angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getAspect() > 1.f ? LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect() : LLViewerCamera::getInstance()->getView()); + F32 distance = selection_bbox.getExtentLocal().magVec() * PADDING_FACTOR / atan(angle_of_view); + + LLVector3 obj_to_cam = LLViewerCamera::getInstance()->getOrigin() - selection_bbox.getCenterAgent(); + obj_to_cam.normVec(); + + LLUUID object_id; + if (LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()) + { + object_id = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()->mID; + } + if (zoom) + { + // Make sure we are not increasing the distance between the camera and object + LLVector3d orig_distance = gAgentCamera.getCameraPositionGlobal() - LLSelectMgr::getInstance()->getSelectionCenterGlobal(); + distance = llmin(distance, (F32) orig_distance.length()); + + gAgentCamera.setCameraPosAndFocusGlobal(LLSelectMgr::getInstance()->getSelectionCenterGlobal() + LLVector3d(obj_to_cam * distance), + LLSelectMgr::getInstance()->getSelectionCenterGlobal(), + object_id ); + + } + else + { + gAgentCamera.setFocusGlobal( LLSelectMgr::getInstance()->getSelectionCenterGlobal(), object_id ); + } + } +} + +void handle_zoom_to_object(LLUUID object_id) +{ + const F32 PADDING_FACTOR = 2.f; + + LLViewerObject* object = gObjectList.findObject(object_id); + + if (object) + { + gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); + + LLBBox bbox = object->getBoundingBoxAgent() ; + F32 angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getAspect() > 1.f ? LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect() : LLViewerCamera::getInstance()->getView()); + F32 distance = bbox.getExtentLocal().magVec() * PADDING_FACTOR / atan(angle_of_view); + + LLVector3 obj_to_cam = LLViewerCamera::getInstance()->getOrigin() - bbox.getCenterAgent(); + obj_to_cam.normVec(); + + + LLVector3d object_center_global = gAgent.getPosGlobalFromAgent(bbox.getCenterAgent()); + + gAgentCamera.setCameraPosAndFocusGlobal(object_center_global + LLVector3d(obj_to_cam * distance), + object_center_global, + object_id ); + } +} + +class LLAvatarInviteToGroup : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); + if(avatar) + { + LLAvatarActions::inviteToGroup(avatar->getID()); + } + return true; + } +}; + +class LLAvatarAddFriend : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); + if(avatar && !LLAvatarActions::isFriend(avatar->getID())) + { + request_friendship(avatar->getID()); + } + return true; + } +}; + +class LLAvatarAddContact : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); + if(avatar) + { + create_inventory_callingcard(avatar->getID()); + } + return true; + } +}; + +bool complete_give_money(const LLSD& notification, const LLSD& response, LLObjectSelectionHandle selection) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) + { + gAgent.clearBusy(); + } + + LLViewerObject* objectp = selection->getPrimaryObject(); + + // Show avatar's name if paying attachment + if (objectp && objectp->isAttachment()) + { + while (objectp && !objectp->isAvatar()) + { + objectp = (LLViewerObject*)objectp->getParent(); + } + } + + if (objectp) + { + if (objectp->isAvatar()) + { + const bool is_group = false; + LLFloaterPayUtil::payDirectly(&give_money, + objectp->getID(), + is_group); + } + else + { + LLFloaterPayUtil::payViaObject(&give_money, selection); + } + } + return false; +} + +void handle_give_money_dialog() +{ + LLNotification::Params params("BusyModePay"); + params.functor.function(boost::bind(complete_give_money, _1, _2, LLSelectMgr::getInstance()->getSelection())); + + if (gAgent.getBusy()) + { + // warn users of being in busy mode during a transaction + LLNotifications::instance().add(params); + } + else + { + LLNotifications::instance().forceResponse(params, 1); + } +} + +bool enable_pay_avatar() +{ + LLViewerObject* obj = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + LLVOAvatar* avatar = find_avatar_from_object(obj); + return (avatar != NULL); +} + +bool enable_pay_object() +{ + LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if( object ) + { + LLViewerObject *parent = (LLViewerObject *)object->getParent(); + if((object->flagTakesMoney()) || (parent && parent->flagTakesMoney())) + { + return true; + } + } + return false; +} + +bool enable_object_stand_up() +{ + // 'Object Stand Up' menu item is enabled when agent is sitting on selection + return sitting_on_selection(); +} + +bool enable_object_sit(LLUICtrl* ctrl) +{ + // 'Object Sit' menu item is enabled when agent is not sitting on selection + bool sitting_on_sel = sitting_on_selection(); + if (!sitting_on_sel) + { + std::string item_name = ctrl->getName(); + + // init default labels + init_default_item_label(item_name); + + // Update label + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); + if (node && node->mValid && !node->mSitName.empty()) + { + gMenuHolder->childSetText(item_name, node->mSitName); + } + else + { + gMenuHolder->childSetText(item_name, get_default_item_label(item_name)); + } + } + return !sitting_on_sel && is_object_sittable(); +} + +void dump_select_mgr(void*) +{ + LLSelectMgr::getInstance()->dump(); +} + +void dump_inventory(void*) +{ + gInventory.dumpInventory(); +} + + +void handle_dump_followcam(void*) +{ + LLFollowCamMgr::dump(); +} + +void handle_viewer_enable_message_log(void*) +{ + gMessageSystem->startLogging(); +} + +void handle_viewer_disable_message_log(void*) +{ + gMessageSystem->stopLogging(); +} + +void handle_customize_avatar() +{ + LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "my_outfits")); +} + +void handle_edit_outfit() +{ + LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "edit_outfit")); +} + +void handle_edit_shape() +{ + LLSideTray::getInstance()->showPanel("sidepanel_appearance", LLSD().with("type", "edit_shape")); +} + +void handle_report_abuse() +{ + // Prevent menu from appearing in screen shot. + gMenuHolder->hideMenus(); + LLFloaterReporter::showFromMenu(COMPLAINT_REPORT); +} + +void handle_buy_currency() +{ + LLBuyCurrencyHTML::openCurrencyFloater(); +} + +class LLFloaterVisible : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string floater_name = userdata.asString(); + bool new_value = false; + { + new_value = LLFloaterReg::instanceVisible(floater_name); + } + return new_value; + } +}; + +class LLShowHelp : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string help_topic = userdata.asString(); + LLViewerHelp* vhelp = LLViewerHelp::getInstance(); + vhelp->showTopic(help_topic); + return true; + } +}; + +class LLToggleHelp : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLFloater* help_browser = (LLFloaterReg::findInstance("help_browser")); + if (help_browser && help_browser->isInVisibleChain()) + { + help_browser->closeFloater(); + } + else + { + std::string help_topic = userdata.asString(); + LLViewerHelp* vhelp = LLViewerHelp::getInstance(); + vhelp->showTopic(help_topic); + } + return true; + } +}; + +class LLShowSidetrayPanel : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string panel_name = userdata.asString(); + + LLPanel* panel = LLSideTray::getInstance()->getPanel(panel_name); + if (panel) + { + if (panel->isInVisibleChain()) + { + LLSideTray::getInstance()->hidePanel(panel_name); + } + else + { + LLSideTray::getInstance()->showPanel(panel_name); + } + } + return true; + } +}; + +class LLSidetrayPanelVisible : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string panel_name = userdata.asString(); + // Toggle the panel + if (LLSideTray::getInstance()->isPanelActive(panel_name)) + { + return true; + } + else + { + return false; + } + + } +}; + + +bool callback_show_url(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 == option) + { + LLWeb::loadURL(notification["payload"]["url"].asString()); + } + return false; +} + +class LLPromptShowURL : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string param = userdata.asString(); + std::string::size_type offset = param.find(","); + if (offset != param.npos) + { + std::string alert = param.substr(0, offset); + std::string url = param.substr(offset+1); + + if(gSavedSettings.getBOOL("UseExternalBrowser")) + { + LLSD payload; + payload["url"] = url; + LLNotificationsUtil::add(alert, LLSD(), payload, callback_show_url); + } + else + { + LLWeb::loadURL(url); + } + } + else + { + llinfos << "PromptShowURL invalid parameters! Expecting \"ALERT,URL\"." << llendl; + } + return true; + } +}; + +bool callback_show_file(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 == option) + { + LLWeb::loadURL(notification["payload"]["url"]); + } + return false; +} + +class LLPromptShowFile : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string param = userdata.asString(); + std::string::size_type offset = param.find(","); + if (offset != param.npos) + { + std::string alert = param.substr(0, offset); + std::string file = param.substr(offset+1); + + LLSD payload; + payload["url"] = file; + LLNotificationsUtil::add(alert, LLSD(), payload, callback_show_file); + } + else + { + llinfos << "PromptShowFile invalid parameters! Expecting \"ALERT,FILE\"." << llendl; + } + return true; + } +}; + +class LLShowAgentProfile : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLUUID agent_id; + if (userdata.asString() == "agent") + { + agent_id = gAgent.getID(); + } + else if (userdata.asString() == "hit object") + { + LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if (objectp) + { + agent_id = objectp->getID(); + } + } + else + { + agent_id = userdata.asUUID(); + } + + LLVOAvatar* avatar = find_avatar_from_object(agent_id); + if (avatar) + { + LLAvatarActions::showProfile(avatar->getID()); + } + return true; + } +}; + +class LLToggleAgentProfile : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLUUID agent_id; + if (userdata.asString() == "agent") + { + agent_id = gAgent.getID(); + } + else if (userdata.asString() == "hit object") + { + LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if (objectp) + { + agent_id = objectp->getID(); + } + } + else + { + agent_id = userdata.asUUID(); + } + + LLVOAvatar* avatar = find_avatar_from_object(agent_id); + if (avatar) + { + if (!LLAvatarActions::profileVisible(avatar->getID())) + { + LLAvatarActions::showProfile(avatar->getID()); + } + else + { + LLAvatarActions::hideProfile(avatar->getID()); + } + } + return true; + } +}; + +class LLLandEdit : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + if (gAgentCamera.getFocusOnAvatar() && gSavedSettings.getBOOL("EditCameraMovement") ) + { + // zoom in if we're looking at the avatar + gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); + gAgentCamera.setFocusGlobal(LLToolPie::getInstance()->getPick()); + + gAgentCamera.cameraOrbitOver( F_PI * 0.25f ); + gViewerWindow->moveCursorToCenter(); + } + else if ( gSavedSettings.getBOOL("EditCameraMovement") ) + { + gAgentCamera.setFocusGlobal(LLToolPie::getInstance()->getPick()); + gViewerWindow->moveCursorToCenter(); + } + + + LLViewerParcelMgr::getInstance()->selectParcelAt( LLToolPie::getInstance()->getPick().mPosGlobal ); + + LLFloaterReg::showInstance("build"); + + // Switch to land edit toolset + LLToolMgr::getInstance()->getCurrentToolset()->selectTool( LLToolSelectLand::getInstance() ); + return true; + } +}; + +class LLWorldEnableBuyLand : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLViewerParcelMgr::getInstance()->canAgentBuyParcel( + LLViewerParcelMgr::getInstance()->selectionEmpty() + ? LLViewerParcelMgr::getInstance()->getAgentParcel() + : LLViewerParcelMgr::getInstance()->getParcelSelection()->getParcel(), + false); + return new_value; + } +}; + +BOOL enable_buy_land(void*) +{ + return LLViewerParcelMgr::getInstance()->canAgentBuyParcel( + LLViewerParcelMgr::getInstance()->getParcelSelection()->getParcel(), false); +} + +void handle_buy_land() +{ + LLViewerParcelMgr* vpm = LLViewerParcelMgr::getInstance(); + if (vpm->selectionEmpty()) + { + vpm->selectParcelAt(gAgent.getPositionGlobal()); + } + vpm->startBuyLand(); +} + +class LLObjectAttachToAvatar : public view_listener_t +{ +public: + LLObjectAttachToAvatar(bool replace) : mReplace(replace) {} + static void setObjectSelection(LLObjectSelectionHandle selection) { sObjectSelection = selection; } + +private: + bool handleEvent(const LLSD& userdata) + { + setObjectSelection(LLSelectMgr::getInstance()->getSelection()); + LLViewerObject* selectedObject = sObjectSelection->getFirstRootObject(); + if (selectedObject) + { + S32 index = userdata.asInteger(); + LLViewerJointAttachment* attachment_point = NULL; + if (index > 0) + attachment_point = get_if_there(gAgentAvatarp->mAttachmentPoints, index, (LLViewerJointAttachment*)NULL); + confirmReplaceAttachment(0, attachment_point); + } + return true; + } + + static void onNearAttachObject(BOOL success, void *user_data); + void confirmReplaceAttachment(S32 option, LLViewerJointAttachment* attachment_point); + + struct CallbackData + { + CallbackData(LLViewerJointAttachment* point, bool replace) : mAttachmentPoint(point), mReplace(replace) {} + + LLViewerJointAttachment* mAttachmentPoint; + bool mReplace; + }; + +protected: + static LLObjectSelectionHandle sObjectSelection; + bool mReplace; +}; + +LLObjectSelectionHandle LLObjectAttachToAvatar::sObjectSelection; + +// static +void LLObjectAttachToAvatar::onNearAttachObject(BOOL success, void *user_data) +{ + if (!user_data) return; + CallbackData* cb_data = static_cast(user_data); + + if (success) + { + const LLViewerJointAttachment *attachment = cb_data->mAttachmentPoint; + + U8 attachment_id = 0; + if (attachment) + { + for (LLVOAvatar::attachment_map_t::const_iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); + iter != gAgentAvatarp->mAttachmentPoints.end(); ++iter) + { + if (iter->second == attachment) + { + attachment_id = iter->first; + break; + } + } + } + else + { + // interpret 0 as "default location" + attachment_id = 0; + } + LLSelectMgr::getInstance()->sendAttach(attachment_id, cb_data->mReplace); + } + LLObjectAttachToAvatar::setObjectSelection(NULL); + + delete cb_data; +} + +// static +void LLObjectAttachToAvatar::confirmReplaceAttachment(S32 option, LLViewerJointAttachment* attachment_point) +{ + if (option == 0/*YES*/) + { + LLViewerObject* selectedObject = LLSelectMgr::getInstance()->getSelection()->getFirstRootObject(); + if (selectedObject) + { + const F32 MIN_STOP_DISTANCE = 1.f; // meters + const F32 ARM_LENGTH = 0.5f; // meters + const F32 SCALE_FUDGE = 1.5f; + + F32 stop_distance = SCALE_FUDGE * selectedObject->getMaxScale() + ARM_LENGTH; + if (stop_distance < MIN_STOP_DISTANCE) + { + stop_distance = MIN_STOP_DISTANCE; + } + + LLVector3 walkToSpot = selectedObject->getPositionAgent(); + + // make sure we stop in front of the object + LLVector3 delta = walkToSpot - gAgent.getPositionAgent(); + delta.normVec(); + delta = delta * 0.5f; + walkToSpot -= delta; + + // The callback will be called even if avatar fails to get close enough to the object, so we won't get a memory leak. + CallbackData* user_data = new CallbackData(attachment_point, mReplace); + gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(walkToSpot), "Attach", NULL, onNearAttachObject, user_data, stop_distance); + gAgentCamera.clearFocusObject(); + } + } +} + +void callback_attachment_drop(const LLSD& notification, const LLSD& response) +{ + // Ensure user confirmed the drop + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) return; + + // Called when the user clicked on an object attached to them + // and selected "Drop". + LLUUID object_id = notification["payload"]["object_id"].asUUID(); + LLViewerObject *object = gObjectList.findObject(object_id); + + if (!object) + { + llwarns << "handle_drop_attachment() - no object to drop" << llendl; + return; + } + + LLViewerObject *parent = (LLViewerObject*)object->getParent(); + while (parent) + { + if(parent->isAvatar()) + { + break; + } + object = parent; + parent = (LLViewerObject*)parent->getParent(); + } + + if (!object) + { + llwarns << "handle_detach() - no object to detach" << llendl; + return; + } + + if (object->isAvatar()) + { + llwarns << "Trying to detach avatar from avatar." << llendl; + return; + } + + // reselect the object + LLSelectMgr::getInstance()->selectObjectAndFamily(object); + + LLSelectMgr::getInstance()->sendDropAttachment(); + + return; +} + +class LLAttachmentDrop : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLSD payload; + LLViewerObject *object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + + if (object) + { + payload["object_id"] = object->getID(); + } + else + { + llwarns << "Drop object not found" << llendl; + return true; + } + + LLNotificationsUtil::add("AttachmentDrop", LLSD(), payload, &callback_attachment_drop); + return true; + } +}; + +// called from avatar pie menu +class LLAttachmentDetachFromPoint : public view_listener_t +{ + bool handleEvent(const LLSD& user_data) + { + const LLViewerJointAttachment *attachment = get_if_there(gAgentAvatarp->mAttachmentPoints, user_data.asInteger(), (LLViewerJointAttachment*)NULL); + if (attachment->getNumObjects() > 0) + { + gMessageSystem->newMessage("ObjectDetach"); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() ); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + + for (LLViewerJointAttachment::attachedobjs_vec_t::const_iterator iter = attachment->mAttachedObjects.begin(); + iter != attachment->mAttachedObjects.end(); + iter++) + { + LLViewerObject *attached_object = (*iter); + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, attached_object->getLocalID()); + } + gMessageSystem->sendReliable( gAgent.getRegionHost() ); + } + return true; + } +}; + +static bool onEnableAttachmentLabel(LLUICtrl* ctrl, const LLSD& data) +{ + std::string label; + LLMenuItemGL* menu = dynamic_cast(ctrl); + if (menu) + { + const LLViewerJointAttachment *attachment = get_if_there(gAgentAvatarp->mAttachmentPoints, data["index"].asInteger(), (LLViewerJointAttachment*)NULL); + if (attachment) + { + label = data["label"].asString(); + for (LLViewerJointAttachment::attachedobjs_vec_t::const_iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) + { + const LLViewerObject* attached_object = (*attachment_iter); + if (attached_object) + { + LLViewerInventoryItem* itemp = gInventory.getItem(attached_object->getAttachmentItemID()); + if (itemp) + { + label += std::string(" (") + itemp->getName() + std::string(")"); + break; + } + } + } + } + menu->setLabel(label); + } + return true; +} + +class LLAttachmentDetach : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + // Called when the user clicked on an object attached to them + // and selected "Detach". + LLViewerObject *object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if (!object) + { + llwarns << "handle_detach() - no object to detach" << llendl; + return true; + } + + LLViewerObject *parent = (LLViewerObject*)object->getParent(); + while (parent) + { + if(parent->isAvatar()) + { + break; + } + object = parent; + parent = (LLViewerObject*)parent->getParent(); + } + + if (!object) + { + llwarns << "handle_detach() - no object to detach" << llendl; + return true; + } + + if (object->isAvatar()) + { + llwarns << "Trying to detach avatar from avatar." << llendl; + return true; + } + + // The sendDetach() method works on the list of selected + // objects. Thus we need to clear the list, make sure it only + // contains the object the user clicked, send the message, + // then clear the list. + // We use deselectAll to update the simulator's notion of what's + // selected, and removeAll just to change things locally. + //RN: I thought it was more useful to detach everything that was selected + if (LLSelectMgr::getInstance()->getSelection()->isAttachment()) + { + LLSelectMgr::getInstance()->sendDetach(); + } + return true; + } +}; + +//Adding an observer for a Jira 2422 and needs to be a fetch observer +//for Jira 3119 +class LLWornItemFetchedObserver : public LLInventoryFetchItemsObserver +{ +public: + LLWornItemFetchedObserver(const LLUUID& worn_item_id) : + LLInventoryFetchItemsObserver(worn_item_id) + {} + virtual ~LLWornItemFetchedObserver() {} + +protected: + virtual void done() + { + gMenuAttachmentSelf->buildDrawLabels(); + gInventory.removeObserver(this); + delete this; + } +}; + +// You can only drop items on parcels where you can build. +class LLAttachmentEnableDrop : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + BOOL can_build = gAgent.isGodlike() || (LLViewerParcelMgr::getInstance()->allowAgentBuild()); + + //Add an inventory observer to only allow dropping the newly attached item + //once it exists in your inventory. Look at Jira 2422. + //-jwolk + + // A bug occurs when you wear/drop an item before it actively is added to your inventory + // if this is the case (you're on a slow sim, etc.) a copy of the object, + // well, a newly created object with the same properties, is placed + // in your inventory. Therefore, we disable the drop option until the + // item is in your inventory + + LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + LLViewerJointAttachment* attachment = NULL; + LLInventoryItem* item = NULL; + + // Do not enable drop if all faces of object are not enabled + if (object && LLSelectMgr::getInstance()->getSelection()->contains(object,SELECT_ALL_TES )) + { + S32 attachmentID = ATTACHMENT_ID_FROM_STATE(object->getState()); + attachment = get_if_there(gAgentAvatarp->mAttachmentPoints, attachmentID, (LLViewerJointAttachment*)NULL); + + if (attachment) + { + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) + { + // make sure item is in your inventory (it could be a delayed attach message being sent from the sim) + // so check to see if the item is in the inventory already + item = gInventory.getItem((*attachment_iter)->getAttachmentItemID()); + if (!item) + { + // Item does not exist, make an observer to enable the pie menu + // when the item finishes fetching worst case scenario + // if a fetch is already out there (being sent from a slow sim) + // we refetch and there are 2 fetches + LLWornItemFetchedObserver* worn_item_fetched = new LLWornItemFetchedObserver((*attachment_iter)->getAttachmentItemID()); + worn_item_fetched->startFetch(); + gInventory.addObserver(worn_item_fetched); + } + } + } + } + + //now check to make sure that the item is actually in the inventory before we enable dropping it + bool new_value = enable_detach() && can_build && item; + + return new_value; + } +}; + +BOOL enable_detach(const LLSD&) +{ + LLViewerObject* object = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + + // Only enable detach if all faces of object are selected + if (!object || + !object->isAttachment() || + !LLSelectMgr::getInstance()->getSelection()->contains(object,SELECT_ALL_TES )) + { + return FALSE; + } + + // Find the avatar who owns this attachment + LLViewerObject* avatar = object; + while (avatar) + { + // ...if it's you, good to detach + if (avatar->getID() == gAgent.getID()) + { + return TRUE; + } + + avatar = (LLViewerObject*)avatar->getParent(); + } + + return FALSE; +} + +class LLAttachmentEnableDetach : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = enable_detach(); + return new_value; + } +}; + +// Used to tell if the selected object can be attached to your avatar. +BOOL object_selected_and_point_valid() +{ + LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); + for (LLObjectSelection::root_iterator iter = selection->root_begin(); + iter != selection->root_end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + LLViewerObject::const_child_list_t& child_list = object->getChildren(); + for (LLViewerObject::child_list_t::const_iterator iter = child_list.begin(); + iter != child_list.end(); iter++) + { + LLViewerObject* child = *iter; + if (child->isAvatar()) + { + return FALSE; + } + } + } + + return (selection->getRootObjectCount() == 1) && + (selection->getFirstRootObject()->getPCode() == LL_PCODE_VOLUME) && + selection->getFirstRootObject()->permYouOwner() && + selection->getFirstRootObject()->flagObjectMove() && + !((LLViewerObject*)selection->getFirstRootObject()->getRoot())->isAvatar() && + (selection->getFirstRootObject()->getNVPair("AssetContainer") == NULL); +} + + +BOOL object_is_wearable() +{ + if (!object_selected_and_point_valid()) + { + return FALSE; + } + if (sitting_on_selection()) + { + return FALSE; + } + LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); + for (LLObjectSelection::valid_root_iterator iter = LLSelectMgr::getInstance()->getSelection()->valid_root_begin(); + iter != LLSelectMgr::getInstance()->getSelection()->valid_root_end(); iter++) + { + LLSelectNode* node = *iter; + if (node->mPermissions->getOwner() == gAgent.getID()) + { + return TRUE; + } + } + return FALSE; +} + + +class LLAttachmentPointFilled : public view_listener_t +{ + bool handleEvent(const LLSD& user_data) + { + bool enable = false; + LLVOAvatar::attachment_map_t::iterator found_it = gAgentAvatarp->mAttachmentPoints.find(user_data.asInteger()); + if (found_it != gAgentAvatarp->mAttachmentPoints.end()) + { + enable = found_it->second->getNumObjects() > 0; + } + return enable; + } +}; + +class LLAvatarSendIM : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); + if(avatar) + { + LLAvatarActions::startIM(avatar->getID()); + } + return true; + } +}; + +class LLAvatarCall : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); + if(avatar) + { + LLAvatarActions::startCall(avatar->getID()); + } + return true; + } +}; + +namespace +{ + struct QueueObjects : public LLSelectedObjectFunctor + { + BOOL scripted; + BOOL modifiable; + LLFloaterScriptQueue* mQueue; + QueueObjects(LLFloaterScriptQueue* q) : mQueue(q), scripted(FALSE), modifiable(FALSE) {} + virtual bool apply(LLViewerObject* obj) + { + scripted = obj->flagScripted(); + modifiable = obj->permModify(); + + if( scripted && modifiable ) + { + mQueue->addObject(obj->getID()); + return false; + } + else + { + return true; // fail: stop applying + } + } + }; +} + +void queue_actions(LLFloaterScriptQueue* q, const std::string& msg) +{ + QueueObjects func(q); + LLSelectMgr *mgr = LLSelectMgr::getInstance(); + LLObjectSelectionHandle selectHandle = mgr->getSelection(); + bool fail = selectHandle->applyToObjects(&func); + if(fail) + { + if ( !func.scripted ) + { + std::string noscriptmsg = std::string("Cannot") + msg + "SelectObjectsNoScripts"; + LLNotificationsUtil::add(noscriptmsg); + } + else if ( !func.modifiable ) + { + std::string nomodmsg = std::string("Cannot") + msg + "SelectObjectsNoPermission"; + LLNotificationsUtil::add(nomodmsg); + } + else + { + llerrs << "Bad logic." << llendl; + } + } + else + { + if (!q->start()) + { + llwarns << "Unexpected script compile failure." << llendl; + } + } +} + +class LLToolsSelectedScriptAction : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string action = userdata.asString(); + bool mono = false; + std::string msg, name; + if (action == "compile mono") + { + name = "compile_queue"; + mono = true; + msg = "Recompile"; + } + if (action == "compile lsl") + { + name = "compile_queue"; + msg = "Recompile"; + } + else if (action == "reset") + { + name = "reset_queue"; + msg = "Reset"; + } + else if (action == "start") + { + name = "start_queue"; + msg = "Running"; + } + else if (action == "stop") + { + name = "stop_queue"; + msg = "RunningNot"; + } + LLUUID id; id.generate(); + + LLFloaterScriptQueue* queue =LLFloaterReg::getTypedInstance(name, LLSD(id)); + if (queue) + { + queue->setMono(mono); + queue_actions(queue, msg); + } + else + { + llwarns << "Failed to generate LLFloaterScriptQueue with action: " << action << llendl; + } + return true; + } +}; + +void handle_selected_texture_info(void*) +{ + for (LLObjectSelection::valid_iterator iter = LLSelectMgr::getInstance()->getSelection()->valid_begin(); + iter != LLSelectMgr::getInstance()->getSelection()->valid_end(); iter++) + { + LLSelectNode* node = *iter; + + std::string msg; + msg.assign("Texture info for: "); + msg.append(node->mName); + + LLSD args; + args["MESSAGE"] = msg; + LLNotificationsUtil::add("SystemMessage", args); + + U8 te_count = node->getObject()->getNumTEs(); + // map from texture ID to list of faces using it + typedef std::map< LLUUID, std::vector > map_t; + map_t faces_per_texture; + for (U8 i = 0; i < te_count; i++) + { + if (!node->isTESelected(i)) continue; + + LLViewerTexture* img = node->getObject()->getTEImage(i); + LLUUID image_id = img->getID(); + faces_per_texture[image_id].push_back(i); + } + // Per-texture, dump which faces are using it. + map_t::iterator it; + for (it = faces_per_texture.begin(); it != faces_per_texture.end(); ++it) + { + LLUUID image_id = it->first; + U8 te = it->second[0]; + LLViewerTexture* img = node->getObject()->getTEImage(te); + S32 height = img->getHeight(); + S32 width = img->getWidth(); + S32 components = img->getComponents(); + msg = llformat("%dx%d %s on face ", + width, + height, + (components == 4 ? "alpha" : "opaque")); + for (U8 i = 0; i < it->second.size(); ++i) + { + msg.append( llformat("%d ", (S32)(it->second[i]))); + } + + LLSD args; + args["MESSAGE"] = msg; + LLNotificationsUtil::add("SystemMessage", args); + } + } +} + +void handle_test_male(void*) +{ + LLAppearanceMgr::instance().wearOutfitByName("Male Shape & Outfit"); + //gGestureList.requestResetFromServer( TRUE ); +} + +void handle_test_female(void*) +{ + LLAppearanceMgr::instance().wearOutfitByName("Female Shape & Outfit"); + //gGestureList.requestResetFromServer( FALSE ); +} + +void handle_toggle_pg(void*) +{ + gAgent.setTeen( !gAgent.isTeen() ); + + LLFloaterWorldMap::reloadIcons(NULL); + + llinfos << "PG status set to " << (S32)gAgent.isTeen() << llendl; +} + +void handle_dump_attachments(void*) +{ + if(!isAgentAvatarValid()) return; + + for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); + iter != gAgentAvatarp->mAttachmentPoints.end(); ) + { + LLVOAvatar::attachment_map_t::iterator curiter = iter++; + LLViewerJointAttachment* attachment = curiter->second; + S32 key = curiter->first; + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) + { + LLViewerObject *attached_object = (*attachment_iter); + BOOL visible = (attached_object != NULL && + attached_object->mDrawable.notNull() && + !attached_object->mDrawable->isRenderType(0)); + LLVector3 pos; + if (visible) pos = attached_object->mDrawable->getPosition(); + llinfos << "ATTACHMENT " << key << ": item_id=" << attached_object->getAttachmentItemID() + << (attached_object ? " present " : " absent ") + << (visible ? "visible " : "invisible ") + << " at " << pos + << " and " << (visible ? attached_object->getPosition() : LLVector3::zero) + << llendl; + } + } +} + + +// these are used in the gl menus to set control values, generically. +class LLToggleControl : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string control_name = userdata.asString(); + BOOL checked = gSavedSettings.getBOOL( control_name ); + gSavedSettings.setBOOL( control_name, !checked ); + return true; + } +}; + +class LLCheckControl : public view_listener_t +{ + bool handleEvent( const LLSD& userdata) + { + std::string callback_data = userdata.asString(); + bool new_value = gSavedSettings.getBOOL(callback_data); + return new_value; + } +}; + +// not so generic + +class LLAdvancedCheckRenderShadowOption: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string control_name = userdata.asString(); + S32 current_shadow_level = gSavedSettings.getS32(control_name); + if (current_shadow_level == 0) // is off + { + return false; + } + else // is on + { + return true; + } + } +}; + +class LLAdvancedClickRenderShadowOption: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string control_name = userdata.asString(); + S32 current_shadow_level = gSavedSettings.getS32(control_name); + if (current_shadow_level == 0) // upgrade to level 2 + { + gSavedSettings.setS32(control_name, 2); + } + else // downgrade to level 0 + { + gSavedSettings.setS32(control_name, 0); + } + return true; + } +}; + +void menu_toggle_attached_lights(void* user_data) +{ + LLPipeline::sRenderAttachedLights = gSavedSettings.getBOOL("RenderAttachedLights"); +} + +void menu_toggle_attached_particles(void* user_data) +{ + LLPipeline::sRenderAttachedParticles = gSavedSettings.getBOOL("RenderAttachedParticles"); +} + +class LLAdvancedHandleAttachedLightParticles: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string control_name = userdata.asString(); + + // toggle the control + gSavedSettings.setBOOL(control_name, + !gSavedSettings.getBOOL(control_name)); + + // update internal flags + if (control_name == "RenderAttachedLights") + { + menu_toggle_attached_lights(NULL); + } + else if (control_name == "RenderAttachedParticles") + { + menu_toggle_attached_particles(NULL); + } + return true; + } +}; + +class LLSomethingSelected : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = !(LLSelectMgr::getInstance()->getSelection()->isEmpty()); + return new_value; + } +}; + +class LLSomethingSelectedNoHUD : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); + bool new_value = !(selection->isEmpty()) && !(selection->getSelectType() == SELECT_TYPE_HUD); + return new_value; + } +}; + +static bool is_editable_selected() +{ + return (LLSelectMgr::getInstance()->getSelection()->getFirstEditableObject() != NULL); +} + +class LLEditableSelected : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + return is_editable_selected(); + } +}; + +class LLEditableSelectedMono : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = false; + LLViewerRegion* region = gAgent.getRegion(); + if(region && gMenuHolder) + { + bool have_cap = (! region->getCapability("UpdateScriptTask").empty()); + new_value = is_editable_selected() && have_cap; + } + return new_value; + } +}; + +bool enable_object_take_copy() +{ + bool all_valid = false; + if (LLSelectMgr::getInstance()) + { + if (!LLSelectMgr::getInstance()->getSelection()->isEmpty()) + { + all_valid = true; +#ifndef HACKED_GODLIKE_VIEWER +# ifdef TOGGLE_HACKED_GODLIKE_VIEWER + if (LLGridManager::getInstance()->isInProductionGrid() + || !gAgent.isGodlike()) +# endif + { + struct f : public LLSelectedObjectFunctor + { + virtual bool apply(LLViewerObject* obj) + { + return (!obj->permCopy() || obj->isAttachment()); + } + } func; + const bool firstonly = true; + bool any_invalid = LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, firstonly); + all_valid = !any_invalid; + } +#endif // HACKED_GODLIKE_VIEWER + } + } + + return all_valid; +} + + +class LLHasAsset : public LLInventoryCollectFunctor +{ +public: + LLHasAsset(const LLUUID& id) : mAssetID(id), mHasAsset(FALSE) {} + virtual ~LLHasAsset() {} + virtual bool operator()(LLInventoryCategory* cat, + LLInventoryItem* item); + BOOL hasAsset() const { return mHasAsset; } + +protected: + LLUUID mAssetID; + BOOL mHasAsset; +}; + +bool LLHasAsset::operator()(LLInventoryCategory* cat, + LLInventoryItem* item) +{ + if(item && item->getAssetUUID() == mAssetID) + { + mHasAsset = TRUE; + } + return FALSE; +} + +BOOL enable_save_into_inventory(void*) +{ + // *TODO: clean this up + // find the last root + LLSelectNode* last_node = NULL; + for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); + iter != LLSelectMgr::getInstance()->getSelection()->root_end(); iter++) + { + last_node = *iter; + } + +#ifdef HACKED_GODLIKE_VIEWER + return TRUE; +#else +# ifdef TOGGLE_HACKED_GODLIKE_VIEWER + if (!LLGridManager::getInstance()->isInProductionGrid() + && gAgent.isGodlike()) + { + return TRUE; + } +# endif + // check all pre-req's for save into inventory. + if(last_node && last_node->mValid && !last_node->mItemID.isNull() + && (last_node->mPermissions->getOwner() == gAgent.getID()) + && (gInventory.getItem(last_node->mItemID) != NULL)) + { + LLViewerObject* obj = last_node->getObject(); + if( obj && !obj->isAttachment() ) + { + return TRUE; + } + } +#endif + return FALSE; +} + +class LLToolsEnableSaveToInventory : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = enable_save_into_inventory(NULL); + return new_value; + } +}; + +BOOL enable_save_into_task_inventory(void*) +{ + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstRootNode(); + if(node && (node->mValid) && (!node->mFromTaskID.isNull())) + { + // *TODO: check to see if the fromtaskid object exists. + LLViewerObject* obj = node->getObject(); + if( obj && !obj->isAttachment() ) + { + return TRUE; + } + } + return FALSE; +} + +class LLToolsEnableSaveToObjectInventory : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = enable_save_into_task_inventory(NULL); + return new_value; + } +}; + + +class LLViewEnableMouselook : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + // You can't go directly from customize avatar to mouselook. + // TODO: write code with appropriate dialogs to handle this transition. + bool new_value = (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgentCamera.getCameraMode() && !gSavedSettings.getBOOL("FreezeTime")); + return new_value; + } +}; + +class LLToolsEnableToolNotPie : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = ( LLToolMgr::getInstance()->getBaseTool() != LLToolPie::getInstance() ); + return new_value; + } +}; + +class LLWorldEnableCreateLandmark : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + return !LLLandmarkActions::landmarkAlreadyExists(); + } +}; + +class LLWorldEnableSetHomeLocation : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = gAgent.isGodlike() || + (gAgent.getRegion() && gAgent.getRegion()->getAllowSetHome()); + return new_value; + } +}; + +class LLWorldEnableTeleportHome : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLViewerRegion* regionp = gAgent.getRegion(); + bool agent_on_prelude = (regionp && regionp->isPrelude()); + bool enable_teleport_home = gAgent.isGodlike() || !agent_on_prelude; + return enable_teleport_home; + } +}; + +BOOL enable_god_full(void*) +{ + return gAgent.getGodLevel() >= GOD_FULL; +} + +BOOL enable_god_liaison(void*) +{ + return gAgent.getGodLevel() >= GOD_LIAISON; +} + +bool is_god_customer_service() +{ + return gAgent.getGodLevel() >= GOD_CUSTOMER_SERVICE; +} + +BOOL enable_god_basic(void*) +{ + return gAgent.getGodLevel() > GOD_NOT; +} + + +void toggle_show_xui_names(void *) +{ + gSavedSettings.setBOOL("DebugShowXUINames", !gSavedSettings.getBOOL("DebugShowXUINames")); +} + +BOOL check_show_xui_names(void *) +{ + return gSavedSettings.getBOOL("DebugShowXUINames"); +} + +class LLToolsSelectOnlyMyObjects : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + BOOL cur_val = gSavedSettings.getBOOL("SelectOwnedOnly"); + + gSavedSettings.setBOOL("SelectOwnedOnly", ! cur_val ); + + return true; + } +}; + +class LLToolsSelectOnlyMovableObjects : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + BOOL cur_val = gSavedSettings.getBOOL("SelectMovableOnly"); + + gSavedSettings.setBOOL("SelectMovableOnly", ! cur_val ); + + return true; + } +}; + +class LLToolsSelectBySurrounding : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLSelectMgr::sRectSelectInclusive = !LLSelectMgr::sRectSelectInclusive; + + gSavedSettings.setBOOL("RectangleSelectInclusive", LLSelectMgr::sRectSelectInclusive); + return true; + } +}; + +class LLToolsShowHiddenSelection : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + // TomY TODO Merge these + LLSelectMgr::sRenderHiddenSelections = !LLSelectMgr::sRenderHiddenSelections; + + gSavedSettings.setBOOL("RenderHiddenSelections", LLSelectMgr::sRenderHiddenSelections); + return true; + } +}; + +class LLToolsShowSelectionLightRadius : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + // TomY TODO merge these + LLSelectMgr::sRenderLightRadius = !LLSelectMgr::sRenderLightRadius; + + gSavedSettings.setBOOL("RenderLightRadius", LLSelectMgr::sRenderLightRadius); + return true; + } +}; + +class LLToolsEditLinkedParts : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + BOOL select_individuals = !gSavedSettings.getBOOL("EditLinkedParts"); + gSavedSettings.setBOOL( "EditLinkedParts", select_individuals ); + if (select_individuals) + { + LLSelectMgr::getInstance()->demoteSelectionToIndividuals(); + } + else + { + LLSelectMgr::getInstance()->promoteSelectionToRoot(); + } + return true; + } +}; + +void reload_vertex_shader(void *) +{ + //THIS WOULD BE AN AWESOME PLACE TO RELOAD SHADERS... just a thought - DaveP +} + +void handle_dump_avatar_local_textures(void*) +{ + gAgentAvatarp->dumpLocalTextures(); +} + +void handle_dump_timers() +{ + LLFastTimer::dumpCurTimes(); +} + +void handle_debug_avatar_textures(void*) +{ + LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getPrimaryObject(); + if (objectp) + { + LLFloaterReg::showInstance( "avatar_textures", LLSD(objectp->getID()) ); + } +} + +void handle_grab_baked_texture(void* data) +{ + EBakedTextureIndex baked_tex_index = (EBakedTextureIndex)((intptr_t)data); + if (!isAgentAvatarValid()) return; + + const LLUUID& asset_id = gAgentAvatarp->grabBakedTexture(baked_tex_index); + LL_INFOS("texture") << "Adding baked texture " << asset_id << " to inventory." << llendl; + LLAssetType::EType asset_type = LLAssetType::AT_TEXTURE; + LLInventoryType::EType inv_type = LLInventoryType::IT_TEXTURE; + const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(asset_type)); + if(folder_id.notNull()) + { + std::string name; + name = "Baked " + LLVOAvatarDictionary::getInstance()->getBakedTexture(baked_tex_index)->mNameCapitalized + " Texture"; + + LLUUID item_id; + item_id.generate(); + LLPermissions perm; + perm.init(gAgentID, + gAgentID, + LLUUID::null, + LLUUID::null); + U32 next_owner_perm = PERM_MOVE | PERM_TRANSFER; + perm.initMasks(PERM_ALL, + PERM_ALL, + PERM_NONE, + PERM_NONE, + next_owner_perm); + time_t creation_date_now = time_corrected(); + LLPointer item + = new LLViewerInventoryItem(item_id, + folder_id, + perm, + asset_id, + asset_type, + inv_type, + name, + LLStringUtil::null, + LLSaleInfo::DEFAULT, + LLInventoryItemFlags::II_FLAGS_NONE, + creation_date_now); + + item->updateServer(TRUE); + gInventory.updateItem(item); + gInventory.notifyObservers(); + + // Show the preview panel for textures to let + // user know that the image is now in inventory. + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); + if(active_panel) + { + LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus(); + + active_panel->setSelection(item_id, TAKE_FOCUS_NO); + active_panel->openSelected(); + //LLFloaterInventory::dumpSelectionInformation((void*)view); + // restore keyboard focus + gFocusMgr.setKeyboardFocus(focus_ctrl); + } + } + else + { + llwarns << "Can't find a folder to put it in" << llendl; + } +} + +BOOL enable_grab_baked_texture(void* data) +{ + EBakedTextureIndex index = (EBakedTextureIndex)((intptr_t)data); + if (isAgentAvatarValid()) + { + return gAgentAvatarp->canGrabBakedTexture(index); + } + return FALSE; +} + +// Returns a pointer to the avatar give the UUID of the avatar OR of an attachment the avatar is wearing. +// Returns NULL on failure. +LLVOAvatar* find_avatar_from_object( LLViewerObject* object ) +{ + if (object) + { + if( object->isAttachment() ) + { + do + { + object = (LLViewerObject*) object->getParent(); + } + while( object && !object->isAvatar() ); + } + else if( !object->isAvatar() ) + { + object = NULL; + } + } + + return (LLVOAvatar*) object; +} + + +// Returns a pointer to the avatar give the UUID of the avatar OR of an attachment the avatar is wearing. +// Returns NULL on failure. +LLVOAvatar* find_avatar_from_object( const LLUUID& object_id ) +{ + return find_avatar_from_object( gObjectList.findObject(object_id) ); +} + + +void handle_disconnect_viewer(void *) +{ + LLAppViewer::instance()->forceDisconnect(LLTrans::getString("TestingDisconnect")); +} + +void force_error_breakpoint(void *) +{ + LLAppViewer::instance()->forceErrorBreakpoint(); +} + +void force_error_llerror(void *) +{ + LLAppViewer::instance()->forceErrorLLError(); +} + +void force_error_bad_memory_access(void *) +{ + LLAppViewer::instance()->forceErrorBadMemoryAccess(); +} + +void force_error_infinite_loop(void *) +{ + LLAppViewer::instance()->forceErrorInfiniteLoop(); +} + +void force_error_software_exception(void *) +{ + LLAppViewer::instance()->forceErrorSoftwareException(); +} + +void force_error_driver_crash(void *) +{ + LLAppViewer::instance()->forceErrorDriverCrash(); +} + +class LLToolsUseSelectionForGrid : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLSelectMgr::getInstance()->clearGridObjects(); + struct f : public LLSelectedObjectFunctor + { + virtual bool apply(LLViewerObject* objectp) + { + LLSelectMgr::getInstance()->addGridObject(objectp); + return true; + } + } func; + LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func); + LLSelectMgr::getInstance()->setGridMode(GRID_MODE_REF_OBJECT); + if (gFloaterTools) + { + gFloaterTools->mComboGridMode->setCurrentByIndex((S32)GRID_MODE_REF_OBJECT); + } + return true; + } +}; + +void handle_test_load_url(void*) +{ + LLWeb::loadURL(""); + LLWeb::loadURL("hacker://www.google.com/"); + LLWeb::loadURL("http"); + LLWeb::loadURL("http://www.google.com/"); +} + +// +// LLViewerMenuHolderGL +// +static LLDefaultChildRegistry::Register r("menu_holder"); + +LLViewerMenuHolderGL::LLViewerMenuHolderGL(const LLViewerMenuHolderGL::Params& p) +: LLMenuHolderGL(p) +{} + +BOOL LLViewerMenuHolderGL::hideMenus() +{ + BOOL handled = LLMenuHolderGL::hideMenus(); + + // drop pie menu selection + mParcelSelection = NULL; + mObjectSelection = NULL; + + if (gMenuBarView) + { + gMenuBarView->clearHoverItem(); + gMenuBarView->resetMenuTrigger(); + } + + return handled; +} + +void LLViewerMenuHolderGL::setParcelSelection(LLSafeHandle selection) +{ + mParcelSelection = selection; +} + +void LLViewerMenuHolderGL::setObjectSelection(LLSafeHandle selection) +{ + mObjectSelection = selection; +} + + +const LLRect LLViewerMenuHolderGL::getMenuRect() const +{ + return LLRect(0, getRect().getHeight() - MENU_BAR_HEIGHT, getRect().getWidth(), STATUS_BAR_HEIGHT); +} + +void handle_web_browser_test(const LLSD& param) +{ + std::string url = param.asString(); + if (url.empty()) + { + url = "about:blank"; + } + LLWeb::loadURLInternal(url); +} + +void handle_web_content_test(const LLSD& param) +{ + std::string url = param.asString(); + LLWeb::loadWebURLInternal(url); +} + +void handle_buy_currency_test(void*) +{ + std::string url = + "http://sarahd-sl-13041.webdev.lindenlab.com/app/lindex/index.php?agent_id=[AGENT_ID]&secure_session_id=[SESSION_ID]&lang=[LANGUAGE]"; + + LLStringUtil::format_map_t replace; + replace["[AGENT_ID]"] = gAgent.getID().asString(); + replace["[SESSION_ID]"] = gAgent.getSecureSessionID().asString(); + replace["[LANGUAGE]"] = LLUI::getLanguage(); + LLStringUtil::format(url, replace); + + llinfos << "buy currency url " << url << llendl; + + LLFloaterReg::showInstance("buy_currency_html", LLSD(url)); +} + +void handle_rebake_textures(void*) +{ + if (!isAgentAvatarValid()) return; + + // Slam pending upload count to "unstick" things + bool slam_for_debug = true; + gAgentAvatarp->forceBakeAllTextures(slam_for_debug); +} + +void toggle_visibility(void* user_data) +{ + LLView* viewp = (LLView*)user_data; + viewp->setVisible(!viewp->getVisible()); +} + +BOOL get_visibility(void* user_data) +{ + LLView* viewp = (LLView*)user_data; + return viewp->getVisible(); +} + +// TomY TODO: Get rid of these? +class LLViewShowHoverTips : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + gSavedSettings.setBOOL("ShowHoverTips", !gSavedSettings.getBOOL("ShowHoverTips")); + return true; + } +}; + +class LLViewCheckShowHoverTips : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = gSavedSettings.getBOOL("ShowHoverTips"); + return new_value; + } +}; + +// TomY TODO: Get rid of these? +class LLViewHighlightTransparent : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLDrawPoolAlpha::sShowDebugAlpha = !LLDrawPoolAlpha::sShowDebugAlpha; + return true; + } +}; + +class LLViewCheckHighlightTransparent : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLDrawPoolAlpha::sShowDebugAlpha; + return new_value; + } +}; + +class LLViewBeaconWidth : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string width = userdata.asString(); + if(width == "1") + { + gSavedSettings.setS32("DebugBeaconLineWidth", 1); + } + else if(width == "4") + { + gSavedSettings.setS32("DebugBeaconLineWidth", 4); + } + else if(width == "16") + { + gSavedSettings.setS32("DebugBeaconLineWidth", 16); + } + else if(width == "32") + { + gSavedSettings.setS32("DebugBeaconLineWidth", 32); + } + + return true; + } +}; + + +class LLViewToggleBeacon : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string beacon = userdata.asString(); + if (beacon == "scriptsbeacon") + { + LLPipeline::toggleRenderScriptedBeacons(NULL); + gSavedSettings.setBOOL( "scriptsbeacon", LLPipeline::getRenderScriptedBeacons(NULL) ); + // toggle the other one off if it's on + if (LLPipeline::getRenderScriptedBeacons(NULL) && LLPipeline::getRenderScriptedTouchBeacons(NULL)) + { + LLPipeline::toggleRenderScriptedTouchBeacons(NULL); + gSavedSettings.setBOOL( "scripttouchbeacon", LLPipeline::getRenderScriptedTouchBeacons(NULL) ); + } + } + else if (beacon == "physicalbeacon") + { + LLPipeline::toggleRenderPhysicalBeacons(NULL); + gSavedSettings.setBOOL( "physicalbeacon", LLPipeline::getRenderPhysicalBeacons(NULL) ); + } + else if (beacon == "soundsbeacon") + { + LLPipeline::toggleRenderSoundBeacons(NULL); + gSavedSettings.setBOOL( "soundsbeacon", LLPipeline::getRenderSoundBeacons(NULL) ); + } + else if (beacon == "particlesbeacon") + { + LLPipeline::toggleRenderParticleBeacons(NULL); + gSavedSettings.setBOOL( "particlesbeacon", LLPipeline::getRenderParticleBeacons(NULL) ); + } + else if (beacon == "scripttouchbeacon") + { + LLPipeline::toggleRenderScriptedTouchBeacons(NULL); + gSavedSettings.setBOOL( "scripttouchbeacon", LLPipeline::getRenderScriptedTouchBeacons(NULL) ); + // toggle the other one off if it's on + if (LLPipeline::getRenderScriptedBeacons(NULL) && LLPipeline::getRenderScriptedTouchBeacons(NULL)) + { + LLPipeline::toggleRenderScriptedBeacons(NULL); + gSavedSettings.setBOOL( "scriptsbeacon", LLPipeline::getRenderScriptedBeacons(NULL) ); + } + } + else if (beacon == "renderbeacons") + { + LLPipeline::toggleRenderBeacons(NULL); + gSavedSettings.setBOOL( "renderbeacons", LLPipeline::getRenderBeacons(NULL) ); + // toggle the other one on if it's not + if (!LLPipeline::getRenderBeacons(NULL) && !LLPipeline::getRenderHighlights(NULL)) + { + LLPipeline::toggleRenderHighlights(NULL); + gSavedSettings.setBOOL( "renderhighlights", LLPipeline::getRenderHighlights(NULL) ); + } + } + else if (beacon == "renderhighlights") + { + LLPipeline::toggleRenderHighlights(NULL); + gSavedSettings.setBOOL( "renderhighlights", LLPipeline::getRenderHighlights(NULL) ); + // toggle the other one on if it's not + if (!LLPipeline::getRenderBeacons(NULL) && !LLPipeline::getRenderHighlights(NULL)) + { + LLPipeline::toggleRenderBeacons(NULL); + gSavedSettings.setBOOL( "renderbeacons", LLPipeline::getRenderBeacons(NULL) ); + } + } + + return true; + } +}; + +class LLViewCheckBeaconEnabled : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string beacon = userdata.asString(); + bool new_value = false; + if (beacon == "scriptsbeacon") + { + new_value = gSavedSettings.getBOOL( "scriptsbeacon"); + LLPipeline::setRenderScriptedBeacons(new_value); + } + else if (beacon == "physicalbeacon") + { + new_value = gSavedSettings.getBOOL( "physicalbeacon"); + LLPipeline::setRenderPhysicalBeacons(new_value); + } + else if (beacon == "soundsbeacon") + { + new_value = gSavedSettings.getBOOL( "soundsbeacon"); + LLPipeline::setRenderSoundBeacons(new_value); + } + else if (beacon == "particlesbeacon") + { + new_value = gSavedSettings.getBOOL( "particlesbeacon"); + LLPipeline::setRenderParticleBeacons(new_value); + } + else if (beacon == "scripttouchbeacon") + { + new_value = gSavedSettings.getBOOL( "scripttouchbeacon"); + LLPipeline::setRenderScriptedTouchBeacons(new_value); + } + else if (beacon == "renderbeacons") + { + new_value = gSavedSettings.getBOOL( "renderbeacons"); + LLPipeline::setRenderBeacons(new_value); + } + else if (beacon == "renderhighlights") + { + new_value = gSavedSettings.getBOOL( "renderhighlights"); + LLPipeline::setRenderHighlights(new_value); + } + return new_value; + } +}; + +class LLViewToggleRenderType : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string type = userdata.asString(); + if (type == "hideparticles") + { + LLPipeline::toggleRenderType(LLPipeline::RENDER_TYPE_PARTICLES); + } + return true; + } +}; + +class LLViewCheckRenderType : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string type = userdata.asString(); + bool new_value = false; + if (type == "hideparticles") + { + new_value = LLPipeline::toggleRenderTypeControlNegated((void *)LLPipeline::RENDER_TYPE_PARTICLES); + } + return new_value; + } +}; + +class LLViewShowHUDAttachments : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLPipeline::sShowHUDAttachments = !LLPipeline::sShowHUDAttachments; + return true; + } +}; + +class LLViewCheckHUDAttachments : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool new_value = LLPipeline::sShowHUDAttachments; + return new_value; + } +}; + +class LLEditEnableTakeOff : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string clothing = userdata.asString(); + LLWearableType::EType type = LLWearableType::typeNameToType(clothing); + if (type >= LLWearableType::WT_SHAPE && type < LLWearableType::WT_COUNT) + return LLAgentWearables::selfHasWearable(type); + return false; + } +}; + +class LLEditTakeOff : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string clothing = userdata.asString(); + if (clothing == "all") + LLWearableBridge::removeAllClothesFromAvatar(); + else + { + LLWearableType::EType type = LLWearableType::typeNameToType(clothing); + if (type >= LLWearableType::WT_SHAPE + && type < LLWearableType::WT_COUNT + && (gAgentWearables.getWearableCount(type) > 0)) + { + // MULTI-WEARABLES: assuming user wanted to remove top shirt. + U32 wearable_index = gAgentWearables.getWearableCount(type) - 1; + LLViewerInventoryItem *item = dynamic_cast(gAgentWearables.getWearableInventoryItem(type,wearable_index)); + LLWearableBridge::removeItemFromAvatar(item); + } + + } + return true; + } +}; + +class LLToolsSelectTool : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string tool_name = userdata.asString(); + if (tool_name == "focus") + { + LLToolMgr::getInstance()->getCurrentToolset()->selectToolByIndex(1); + } + else if (tool_name == "move") + { + LLToolMgr::getInstance()->getCurrentToolset()->selectToolByIndex(2); + } + else if (tool_name == "edit") + { + LLToolMgr::getInstance()->getCurrentToolset()->selectToolByIndex(3); + } + else if (tool_name == "create") + { + LLToolMgr::getInstance()->getCurrentToolset()->selectToolByIndex(4); + } + else if (tool_name == "land") + { + LLToolMgr::getInstance()->getCurrentToolset()->selectToolByIndex(5); + } + return true; + } +}; + +/// WINDLIGHT callbacks +class LLWorldEnvSettings : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + std::string tod = userdata.asString(); + LLVector3 sun_direction; + + if (tod == "editor") + { + // if not there or is hidden, show it + LLFloaterReg::toggleInstance("env_settings"); + return true; + } + + if (tod == "sunrise") + { + // set the value, turn off animation + LLWLParamManager::instance()->mAnimator.setDayTime(0.25); + LLWLParamManager::instance()->mAnimator.mIsRunning = false; + LLWLParamManager::instance()->mAnimator.mUseLindenTime = false; + + // then call update once + LLWLParamManager::instance()->mAnimator.update( + LLWLParamManager::instance()->mCurParams); + } + else if (tod == "noon") + { + // set the value, turn off animation + LLWLParamManager::instance()->mAnimator.setDayTime(0.567); + LLWLParamManager::instance()->mAnimator.mIsRunning = false; + LLWLParamManager::instance()->mAnimator.mUseLindenTime = false; + + // then call update once + LLWLParamManager::instance()->mAnimator.update( + LLWLParamManager::instance()->mCurParams); + } + else if (tod == "sunset") + { + // set the value, turn off animation + LLWLParamManager::instance()->mAnimator.setDayTime(0.75); + LLWLParamManager::instance()->mAnimator.mIsRunning = false; + LLWLParamManager::instance()->mAnimator.mUseLindenTime = false; + + // then call update once + LLWLParamManager::instance()->mAnimator.update( + LLWLParamManager::instance()->mCurParams); + } + else if (tod == "midnight") + { + // set the value, turn off animation + LLWLParamManager::instance()->mAnimator.setDayTime(0.0); + LLWLParamManager::instance()->mAnimator.mIsRunning = false; + LLWLParamManager::instance()->mAnimator.mUseLindenTime = false; + + // then call update once + LLWLParamManager::instance()->mAnimator.update( + LLWLParamManager::instance()->mCurParams); + } + else + { + LLWLParamManager::instance()->mAnimator.mIsRunning = true; + LLWLParamManager::instance()->mAnimator.mUseLindenTime = true; + } + return true; + } +}; + +/// Water Menu callbacks +class LLWorldWaterSettings : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLFloaterReg::toggleInstance("env_water"); + return true; + } +}; + +/// Post-Process callbacks +class LLWorldPostProcess : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLFloaterReg::showInstance("env_post_process"); + return true; + } +}; + +/// Day Cycle callbacks +class LLWorldDayCycle : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLFloaterReg::showInstance("env_day_cycle"); + return true; + } +}; + +class LLWorldToggleMovementControls : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLBottomTray::getInstance()->toggleMovementControls(); + return true; + } +}; + +class LLWorldToggleCameraControls : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLBottomTray::getInstance()->toggleCameraControls(); + return true; + } +}; + +void handle_flush_name_caches() +{ + // Toggle display names on and off to flush + bool use_display_names = LLAvatarNameCache::useDisplayNames(); + LLAvatarNameCache::setUseDisplayNames(!use_display_names); + LLAvatarNameCache::setUseDisplayNames(use_display_names); + + if (gCacheName) gCacheName->clear(); +} + +class LLUploadCostCalculator : public view_listener_t +{ + std::string mCostStr; + + bool handleEvent(const LLSD& userdata) + { + std::string menu_name = userdata.asString(); + gMenuHolder->childSetLabelArg(menu_name, "[COST]", mCostStr); + + return true; + } + + void calculateCost(); + +public: + LLUploadCostCalculator() + { + calculateCost(); + } +}; + +class LLToggleUIHints : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + bool ui_hints_enabled = gSavedSettings.getBOOL("EnableUIHints"); + // toggle + ui_hints_enabled = !ui_hints_enabled; + gSavedSettings.setBOOL("EnableUIHints", ui_hints_enabled); + return true; + } +}; + +void LLUploadCostCalculator::calculateCost() +{ + S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); + + // getPriceUpload() returns -1 if no data available yet. + if(upload_cost >= 0) + { + mCostStr = llformat("%d", upload_cost); + } + else + { + mCostStr = llformat("%d", gSavedSettings.getU32("DefaultUploadCost")); + } +} + +void show_navbar_context_menu(LLView* ctrl, S32 x, S32 y) +{ + static LLMenuGL* show_navbar_context_menu = LLUICtrlFactory::getInstance()->createFromFile("menu_hide_navbar.xml", + gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(gMenuHolder->hasVisibleMenu()) + { + gMenuHolder->hideMenus(); + } + show_navbar_context_menu->buildDrawLabels(); + show_navbar_context_menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(ctrl, show_navbar_context_menu, x, y); +} + +void show_topinfobar_context_menu(LLView* ctrl, S32 x, S32 y) +{ + static LLMenuGL* show_topbarinfo_context_menu = LLUICtrlFactory::getInstance()->createFromFile("menu_topinfobar.xml", + gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + + LLMenuItemGL* landmark_item = show_topbarinfo_context_menu->getChild("Landmark"); + if (!LLLandmarkActions::landmarkAlreadyExists()) + { + landmark_item->setLabel(LLTrans::getString("AddLandmarkNavBarMenu")); + } + else + { + landmark_item->setLabel(LLTrans::getString("EditLandmarkNavBarMenu")); + } + + if(gMenuHolder->hasVisibleMenu()) + { + gMenuHolder->hideMenus(); + } + + show_topbarinfo_context_menu->buildDrawLabels(); + show_topbarinfo_context_menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(ctrl, show_topbarinfo_context_menu, x, y); +} + +void initialize_edit_menu() +{ + view_listener_t::addMenu(new LLEditUndo(), "Edit.Undo"); + view_listener_t::addMenu(new LLEditRedo(), "Edit.Redo"); + view_listener_t::addMenu(new LLEditCut(), "Edit.Cut"); + view_listener_t::addMenu(new LLEditCopy(), "Edit.Copy"); + view_listener_t::addMenu(new LLEditPaste(), "Edit.Paste"); + view_listener_t::addMenu(new LLEditDelete(), "Edit.Delete"); + view_listener_t::addMenu(new LLEditSelectAll(), "Edit.SelectAll"); + view_listener_t::addMenu(new LLEditDeselect(), "Edit.Deselect"); + view_listener_t::addMenu(new LLEditDuplicate(), "Edit.Duplicate"); + view_listener_t::addMenu(new LLEditTakeOff(), "Edit.TakeOff"); + view_listener_t::addMenu(new LLEditEnableUndo(), "Edit.EnableUndo"); + view_listener_t::addMenu(new LLEditEnableRedo(), "Edit.EnableRedo"); + view_listener_t::addMenu(new LLEditEnableCut(), "Edit.EnableCut"); + view_listener_t::addMenu(new LLEditEnableCopy(), "Edit.EnableCopy"); + view_listener_t::addMenu(new LLEditEnablePaste(), "Edit.EnablePaste"); + view_listener_t::addMenu(new LLEditEnableDelete(), "Edit.EnableDelete"); + view_listener_t::addMenu(new LLEditEnableSelectAll(), "Edit.EnableSelectAll"); + view_listener_t::addMenu(new LLEditEnableDeselect(), "Edit.EnableDeselect"); + view_listener_t::addMenu(new LLEditEnableDuplicate(), "Edit.EnableDuplicate"); + +} + +void initialize_menus() +{ + // A parameterized event handler used as ctrl-8/9/0 zoom controls below. + class LLZoomer : public view_listener_t + { + public: + // The "mult" parameter says whether "val" is a multiplier or used to set the value. + LLZoomer(F32 val, bool mult=true) : mVal(val), mMult(mult) {} + bool handleEvent(const LLSD& userdata) + { + F32 new_fov_rad = mMult ? LLViewerCamera::getInstance()->getDefaultFOV() * mVal : mVal; + LLViewerCamera::getInstance()->setDefaultFOV(new_fov_rad); + gSavedSettings.setF32("CameraAngle", LLViewerCamera::getInstance()->getView()); // setView may have clamped it. + return true; + } + private: + F32 mVal; + bool mMult; + }; + + LLUICtrl::EnableCallbackRegistry::Registrar& enable = LLUICtrl::EnableCallbackRegistry::currentRegistrar(); + LLUICtrl::CommitCallbackRegistry::Registrar& commit = LLUICtrl::CommitCallbackRegistry::currentRegistrar(); + + // Generic enable and visible + // Don't prepend MenuName.Foo because these can be used in any menu. + enable.add("IsGodCustomerService", boost::bind(&is_god_customer_service)); + + view_listener_t::addEnable(new LLUploadCostCalculator(), "Upload.CalculateCosts"); + + // Agent + commit.add("Agent.toggleFlying", boost::bind(&LLAgent::toggleFlying)); + enable.add("Agent.enableFlying", boost::bind(&LLAgent::enableFlying)); + + // File menu + init_menu_file(); + + view_listener_t::addMenu(new LLEditEnableTakeOff(), "Edit.EnableTakeOff"); + view_listener_t::addMenu(new LLEditEnableCustomizeAvatar(), "Edit.EnableCustomizeAvatar"); + view_listener_t::addMenu(new LLEnableEditShape(), "Edit.EnableEditShape"); + commit.add("CustomizeAvatar", boost::bind(&handle_customize_avatar)); + commit.add("EditOutfit", boost::bind(&handle_edit_outfit)); + commit.add("EditShape", boost::bind(&handle_edit_shape)); + + // View menu + view_listener_t::addMenu(new LLViewMouselook(), "View.Mouselook"); + view_listener_t::addMenu(new LLViewJoystickFlycam(), "View.JoystickFlycam"); + view_listener_t::addMenu(new LLViewResetView(), "View.ResetView"); + view_listener_t::addMenu(new LLViewLookAtLastChatter(), "View.LookAtLastChatter"); + view_listener_t::addMenu(new LLViewShowHoverTips(), "View.ShowHoverTips"); + view_listener_t::addMenu(new LLViewHighlightTransparent(), "View.HighlightTransparent"); + view_listener_t::addMenu(new LLViewToggleRenderType(), "View.ToggleRenderType"); + view_listener_t::addMenu(new LLViewShowHUDAttachments(), "View.ShowHUDAttachments"); + view_listener_t::addMenu(new LLZoomer(1.2f), "View.ZoomOut"); + view_listener_t::addMenu(new LLZoomer(1/1.2f), "View.ZoomIn"); + view_listener_t::addMenu(new LLZoomer(DEFAULT_FIELD_OF_VIEW, false), "View.ZoomDefault"); + view_listener_t::addMenu(new LLViewDefaultUISize(), "View.DefaultUISize"); + + view_listener_t::addMenu(new LLViewEnableMouselook(), "View.EnableMouselook"); + view_listener_t::addMenu(new LLViewEnableJoystickFlycam(), "View.EnableJoystickFlycam"); + view_listener_t::addMenu(new LLViewEnableLastChatter(), "View.EnableLastChatter"); + + view_listener_t::addMenu(new LLViewCheckJoystickFlycam(), "View.CheckJoystickFlycam"); + view_listener_t::addMenu(new LLViewCheckShowHoverTips(), "View.CheckShowHoverTips"); + view_listener_t::addMenu(new LLViewCheckHighlightTransparent(), "View.CheckHighlightTransparent"); + view_listener_t::addMenu(new LLViewCheckRenderType(), "View.CheckRenderType"); + view_listener_t::addMenu(new LLViewCheckHUDAttachments(), "View.CheckHUDAttachments"); + + // Me > Movement + view_listener_t::addMenu(new LLAdvancedAgentFlyingInfo(), "Agent.getFlying"); + + // World menu + commit.add("World.Chat", boost::bind(&handle_chat, (void*)NULL)); + view_listener_t::addMenu(new LLWorldAlwaysRun(), "World.AlwaysRun"); + view_listener_t::addMenu(new LLWorldCreateLandmark(), "World.CreateLandmark"); + view_listener_t::addMenu(new LLWorldPlaceProfile(), "World.PlaceProfile"); + view_listener_t::addMenu(new LLWorldSetHomeLocation(), "World.SetHomeLocation"); + view_listener_t::addMenu(new LLWorldTeleportHome(), "World.TeleportHome"); + view_listener_t::addMenu(new LLWorldSetAway(), "World.SetAway"); + view_listener_t::addMenu(new LLWorldSetBusy(), "World.SetBusy"); + + view_listener_t::addMenu(new LLWorldEnableCreateLandmark(), "World.EnableCreateLandmark"); + view_listener_t::addMenu(new LLWorldEnableSetHomeLocation(), "World.EnableSetHomeLocation"); + view_listener_t::addMenu(new LLWorldEnableTeleportHome(), "World.EnableTeleportHome"); + view_listener_t::addMenu(new LLWorldEnableBuyLand(), "World.EnableBuyLand"); + + view_listener_t::addMenu(new LLWorldCheckAlwaysRun(), "World.CheckAlwaysRun"); + + view_listener_t::addMenu(new LLWorldEnvSettings(), "World.EnvSettings"); + view_listener_t::addMenu(new LLWorldWaterSettings(), "World.WaterSettings"); + view_listener_t::addMenu(new LLWorldPostProcess(), "World.PostProcess"); + view_listener_t::addMenu(new LLWorldDayCycle(), "World.DayCycle"); + + view_listener_t::addMenu(new LLWorldToggleMovementControls(), "World.Toggle.MovementControls"); + view_listener_t::addMenu(new LLWorldToggleCameraControls(), "World.Toggle.CameraControls"); + + // Tools menu + view_listener_t::addMenu(new LLToolsSelectTool(), "Tools.SelectTool"); + view_listener_t::addMenu(new LLToolsSelectOnlyMyObjects(), "Tools.SelectOnlyMyObjects"); + view_listener_t::addMenu(new LLToolsSelectOnlyMovableObjects(), "Tools.SelectOnlyMovableObjects"); + view_listener_t::addMenu(new LLToolsSelectBySurrounding(), "Tools.SelectBySurrounding"); + view_listener_t::addMenu(new LLToolsShowHiddenSelection(), "Tools.ShowHiddenSelection"); + view_listener_t::addMenu(new LLToolsShowSelectionLightRadius(), "Tools.ShowSelectionLightRadius"); + view_listener_t::addMenu(new LLToolsEditLinkedParts(), "Tools.EditLinkedParts"); + view_listener_t::addMenu(new LLToolsSnapObjectXY(), "Tools.SnapObjectXY"); + view_listener_t::addMenu(new LLToolsUseSelectionForGrid(), "Tools.UseSelectionForGrid"); + view_listener_t::addMenu(new LLToolsSelectNextPart(), "Tools.SelectNextPart"); + commit.add("Tools.Link", boost::bind(&LLSelectMgr::linkObjects, LLSelectMgr::getInstance())); + commit.add("Tools.Unlink", boost::bind(&LLSelectMgr::unlinkObjects, LLSelectMgr::getInstance())); + view_listener_t::addMenu(new LLToolsStopAllAnimations(), "Tools.StopAllAnimations"); + view_listener_t::addMenu(new LLToolsReleaseKeys(), "Tools.ReleaseKeys"); + view_listener_t::addMenu(new LLToolsEnableReleaseKeys(), "Tools.EnableReleaseKeys"); + commit.add("Tools.LookAtSelection", boost::bind(&handle_look_at_selection, _2)); + commit.add("Tools.BuyOrTake", boost::bind(&handle_buy_or_take)); + commit.add("Tools.TakeCopy", boost::bind(&handle_take_copy)); + view_listener_t::addMenu(new LLToolsSaveToInventory(), "Tools.SaveToInventory"); + view_listener_t::addMenu(new LLToolsSaveToObjectInventory(), "Tools.SaveToObjectInventory"); + view_listener_t::addMenu(new LLToolsSelectedScriptAction(), "Tools.SelectedScriptAction"); + + view_listener_t::addMenu(new LLToolsEnableToolNotPie(), "Tools.EnableToolNotPie"); + view_listener_t::addMenu(new LLToolsEnableSelectNextPart(), "Tools.EnableSelectNextPart"); + enable.add("Tools.EnableLink", boost::bind(&LLSelectMgr::enableLinkObjects, LLSelectMgr::getInstance())); + enable.add("Tools.EnableUnlink", boost::bind(&LLSelectMgr::enableUnlinkObjects, LLSelectMgr::getInstance())); + view_listener_t::addMenu(new LLToolsEnableBuyOrTake(), "Tools.EnableBuyOrTake"); + enable.add("Tools.EnableTakeCopy", boost::bind(&enable_object_take_copy)); + enable.add("Tools.VisibleBuyObject", boost::bind(&tools_visible_buy_object)); + enable.add("Tools.VisibleTakeObject", boost::bind(&tools_visible_take_object)); + view_listener_t::addMenu(new LLToolsEnableSaveToInventory(), "Tools.EnableSaveToInventory"); + view_listener_t::addMenu(new LLToolsEnableSaveToObjectInventory(), "Tools.EnableSaveToObjectInventory"); + + // Help menu + // most items use the ShowFloater method + + // Advanced menu + view_listener_t::addMenu(new LLAdvancedToggleConsole(), "Advanced.ToggleConsole"); + view_listener_t::addMenu(new LLAdvancedCheckConsole(), "Advanced.CheckConsole"); + view_listener_t::addMenu(new LLAdvancedDumpInfoToConsole(), "Advanced.DumpInfoToConsole"); + + // Advanced > HUD Info + view_listener_t::addMenu(new LLAdvancedToggleHUDInfo(), "Advanced.ToggleHUDInfo"); + view_listener_t::addMenu(new LLAdvancedCheckHUDInfo(), "Advanced.CheckHUDInfo"); + + // Advanced Other Settings + view_listener_t::addMenu(new LLAdvancedClearGroupCache(), "Advanced.ClearGroupCache"); + + // Advanced > Render > Types + view_listener_t::addMenu(new LLAdvancedToggleRenderType(), "Advanced.ToggleRenderType"); + view_listener_t::addMenu(new LLAdvancedCheckRenderType(), "Advanced.CheckRenderType"); + + //// Advanced > Render > Features + view_listener_t::addMenu(new LLAdvancedToggleFeature(), "Advanced.ToggleFeature"); + view_listener_t::addMenu(new LLAdvancedCheckFeature(), "Advanced.CheckFeature"); + // Advanced > Render > Info Displays + view_listener_t::addMenu(new LLAdvancedToggleInfoDisplay(), "Advanced.ToggleInfoDisplay"); + view_listener_t::addMenu(new LLAdvancedCheckInfoDisplay(), "Advanced.CheckInfoDisplay"); + view_listener_t::addMenu(new LLAdvancedSelectedTextureInfo(), "Advanced.SelectedTextureInfo"); + view_listener_t::addMenu(new LLAdvancedToggleWireframe(), "Advanced.ToggleWireframe"); + view_listener_t::addMenu(new LLAdvancedCheckWireframe(), "Advanced.CheckWireframe"); + // Develop > Render + view_listener_t::addMenu(new LLAdvancedToggleTextureAtlas(), "Advanced.ToggleTextureAtlas"); + view_listener_t::addMenu(new LLAdvancedCheckTextureAtlas(), "Advanced.CheckTextureAtlas"); + view_listener_t::addMenu(new LLAdvancedEnableObjectObjectOcclusion(), "Advanced.EnableObjectObjectOcclusion"); + view_listener_t::addMenu(new LLAdvancedEnableRenderFBO(), "Advanced.EnableRenderFBO"); + view_listener_t::addMenu(new LLAdvancedEnableRenderDeferred(), "Advanced.EnableRenderDeferred"); + view_listener_t::addMenu(new LLAdvancedEnableRenderDeferredOptions(), "Advanced.EnableRenderDeferredOptions"); + view_listener_t::addMenu(new LLAdvancedToggleRandomizeFramerate(), "Advanced.ToggleRandomizeFramerate"); + view_listener_t::addMenu(new LLAdvancedCheckRandomizeFramerate(), "Advanced.CheckRandomizeFramerate"); + view_listener_t::addMenu(new LLAdvancedTogglePeriodicSlowFrame(), "Advanced.TogglePeriodicSlowFrame"); + view_listener_t::addMenu(new LLAdvancedCheckPeriodicSlowFrame(), "Advanced.CheckPeriodicSlowFrame"); + view_listener_t::addMenu(new LLAdvancedVectorizePerfTest(), "Advanced.VectorizePerfTest"); + view_listener_t::addMenu(new LLAdvancedToggleFrameTest(), "Advanced.ToggleFrameTest"); + view_listener_t::addMenu(new LLAdvancedCheckFrameTest(), "Advanced.CheckFrameTest"); + view_listener_t::addMenu(new LLAdvancedHandleAttachedLightParticles(), "Advanced.HandleAttachedLightParticles"); + view_listener_t::addMenu(new LLAdvancedCheckRenderShadowOption(), "Advanced.CheckRenderShadowOption"); + view_listener_t::addMenu(new LLAdvancedClickRenderShadowOption(), "Advanced.ClickRenderShadowOption"); + + + #ifdef TOGGLE_HACKED_GODLIKE_VIEWER + view_listener_t::addMenu(new LLAdvancedHandleToggleHackedGodmode(), "Advanced.HandleToggleHackedGodmode"); + view_listener_t::addMenu(new LLAdvancedCheckToggleHackedGodmode(), "Advanced.CheckToggleHackedGodmode"); + view_listener_t::addMenu(new LLAdvancedEnableToggleHackedGodmode(), "Advanced.EnableToggleHackedGodmode"); + #endif + + // Advanced > World + view_listener_t::addMenu(new LLAdvancedDumpScriptedCamera(), "Advanced.DumpScriptedCamera"); + view_listener_t::addMenu(new LLAdvancedDumpRegionObjectCache(), "Advanced.DumpRegionObjectCache"); + + // Advanced > UI + commit.add("Advanced.WebBrowserTest", boost::bind(&handle_web_browser_test, _2)); // sigh! this one opens the MEDIA browser + commit.add("Advanced.WebContentTest", boost::bind(&handle_web_content_test, _2)); // this one opens the Web Content floater + view_listener_t::addMenu(new LLAdvancedBuyCurrencyTest(), "Advanced.BuyCurrencyTest"); + view_listener_t::addMenu(new LLAdvancedDumpSelectMgr(), "Advanced.DumpSelectMgr"); + view_listener_t::addMenu(new LLAdvancedDumpInventory(), "Advanced.DumpInventory"); + commit.add("Advanced.DumpTimers", boost::bind(&handle_dump_timers) ); + commit.add("Advanced.DumpFocusHolder", boost::bind(&handle_dump_focus) ); + view_listener_t::addMenu(new LLAdvancedPrintSelectedObjectInfo(), "Advanced.PrintSelectedObjectInfo"); + view_listener_t::addMenu(new LLAdvancedPrintAgentInfo(), "Advanced.PrintAgentInfo"); + view_listener_t::addMenu(new LLAdvancedPrintTextureMemoryStats(), "Advanced.PrintTextureMemoryStats"); + view_listener_t::addMenu(new LLAdvancedToggleDebugClicks(), "Advanced.ToggleDebugClicks"); + view_listener_t::addMenu(new LLAdvancedCheckDebugClicks(), "Advanced.CheckDebugClicks"); + view_listener_t::addMenu(new LLAdvancedCheckDebugViews(), "Advanced.CheckDebugViews"); + view_listener_t::addMenu(new LLAdvancedToggleDebugViews(), "Advanced.ToggleDebugViews"); + view_listener_t::addMenu(new LLAdvancedToggleXUINameTooltips(), "Advanced.ToggleXUINameTooltips"); + view_listener_t::addMenu(new LLAdvancedCheckXUINameTooltips(), "Advanced.CheckXUINameTooltips"); + view_listener_t::addMenu(new LLAdvancedToggleDebugMouseEvents(), "Advanced.ToggleDebugMouseEvents"); + view_listener_t::addMenu(new LLAdvancedCheckDebugMouseEvents(), "Advanced.CheckDebugMouseEvents"); + view_listener_t::addMenu(new LLAdvancedToggleDebugKeys(), "Advanced.ToggleDebugKeys"); + view_listener_t::addMenu(new LLAdvancedCheckDebugKeys(), "Advanced.CheckDebugKeys"); + view_listener_t::addMenu(new LLAdvancedToggleDebugWindowProc(), "Advanced.ToggleDebugWindowProc"); + view_listener_t::addMenu(new LLAdvancedCheckDebugWindowProc(), "Advanced.CheckDebugWindowProc"); + commit.add("Advanced.ShowSideTray", boost::bind(&handle_show_side_tray)); + + // Advanced > XUI + commit.add("Advanced.ReloadColorSettings", boost::bind(&LLUIColorTable::loadFromSettings, LLUIColorTable::getInstance())); + view_listener_t::addMenu(new LLAdvancedToggleXUINames(), "Advanced.ToggleXUINames"); + view_listener_t::addMenu(new LLAdvancedCheckXUINames(), "Advanced.CheckXUINames"); + view_listener_t::addMenu(new LLAdvancedSendTestIms(), "Advanced.SendTestIMs"); + commit.add("Advanced.FlushNameCaches", boost::bind(&handle_flush_name_caches)); + + // Advanced > Character > Grab Baked Texture + view_listener_t::addMenu(new LLAdvancedGrabBakedTexture(), "Advanced.GrabBakedTexture"); + view_listener_t::addMenu(new LLAdvancedEnableGrabBakedTexture(), "Advanced.EnableGrabBakedTexture"); + + // Advanced > Character > Character Tests + view_listener_t::addMenu(new LLAdvancedAppearanceToXML(), "Advanced.AppearanceToXML"); + view_listener_t::addMenu(new LLAdvancedToggleCharacterGeometry(), "Advanced.ToggleCharacterGeometry"); + + view_listener_t::addMenu(new LLAdvancedTestMale(), "Advanced.TestMale"); + view_listener_t::addMenu(new LLAdvancedTestFemale(), "Advanced.TestFemale"); + view_listener_t::addMenu(new LLAdvancedTogglePG(), "Advanced.TogglePG"); + + // Advanced > Character (toplevel) + view_listener_t::addMenu(new LLAdvancedForceParamsToDefault(), "Advanced.ForceParamsToDefault"); + view_listener_t::addMenu(new LLAdvancedReloadVertexShader(), "Advanced.ReloadVertexShader"); + view_listener_t::addMenu(new LLAdvancedToggleAnimationInfo(), "Advanced.ToggleAnimationInfo"); + view_listener_t::addMenu(new LLAdvancedCheckAnimationInfo(), "Advanced.CheckAnimationInfo"); + view_listener_t::addMenu(new LLAdvancedToggleShowLookAt(), "Advanced.ToggleShowLookAt"); + view_listener_t::addMenu(new LLAdvancedCheckShowLookAt(), "Advanced.CheckShowLookAt"); + view_listener_t::addMenu(new LLAdvancedToggleShowPointAt(), "Advanced.ToggleShowPointAt"); + view_listener_t::addMenu(new LLAdvancedCheckShowPointAt(), "Advanced.CheckShowPointAt"); + view_listener_t::addMenu(new LLAdvancedToggleDebugJointUpdates(), "Advanced.ToggleDebugJointUpdates"); + view_listener_t::addMenu(new LLAdvancedCheckDebugJointUpdates(), "Advanced.CheckDebugJointUpdates"); + view_listener_t::addMenu(new LLAdvancedToggleDisableLOD(), "Advanced.ToggleDisableLOD"); + view_listener_t::addMenu(new LLAdvancedCheckDisableLOD(), "Advanced.CheckDisableLOD"); + view_listener_t::addMenu(new LLAdvancedToggleDebugCharacterVis(), "Advanced.ToggleDebugCharacterVis"); + view_listener_t::addMenu(new LLAdvancedCheckDebugCharacterVis(), "Advanced.CheckDebugCharacterVis"); + view_listener_t::addMenu(new LLAdvancedDumpAttachments(), "Advanced.DumpAttachments"); + view_listener_t::addMenu(new LLAdvancedRebakeTextures(), "Advanced.RebakeTextures"); + view_listener_t::addMenu(new LLAdvancedDebugAvatarTextures(), "Advanced.DebugAvatarTextures"); + view_listener_t::addMenu(new LLAdvancedDumpAvatarLocalTextures(), "Advanced.DumpAvatarLocalTextures"); + // Advanced > Network + view_listener_t::addMenu(new LLAdvancedEnableMessageLog(), "Advanced.EnableMessageLog"); + view_listener_t::addMenu(new LLAdvancedDisableMessageLog(), "Advanced.DisableMessageLog"); + view_listener_t::addMenu(new LLAdvancedDropPacket(), "Advanced.DropPacket"); + + // Advanced > Recorder + view_listener_t::addMenu(new LLAdvancedAgentPilot(), "Advanced.AgentPilot"); + view_listener_t::addMenu(new LLAdvancedToggleAgentPilotLoop(), "Advanced.ToggleAgentPilotLoop"); + view_listener_t::addMenu(new LLAdvancedCheckAgentPilotLoop(), "Advanced.CheckAgentPilotLoop"); + + // Advanced > Debugging + view_listener_t::addMenu(new LLAdvancedForceErrorBreakpoint(), "Advanced.ForceErrorBreakpoint"); + view_listener_t::addMenu(new LLAdvancedForceErrorLlerror(), "Advanced.ForceErrorLlerror"); + view_listener_t::addMenu(new LLAdvancedForceErrorBadMemoryAccess(), "Advanced.ForceErrorBadMemoryAccess"); + view_listener_t::addMenu(new LLAdvancedForceErrorInfiniteLoop(), "Advanced.ForceErrorInfiniteLoop"); + view_listener_t::addMenu(new LLAdvancedForceErrorSoftwareException(), "Advanced.ForceErrorSoftwareException"); + view_listener_t::addMenu(new LLAdvancedForceErrorDriverCrash(), "Advanced.ForceErrorDriverCrash"); + view_listener_t::addMenu(new LLAdvancedForceErrorDisconnectViewer(), "Advanced.ForceErrorDisconnectViewer"); + + // Advanced (toplevel) + view_listener_t::addMenu(new LLAdvancedToggleShowObjectUpdates(), "Advanced.ToggleShowObjectUpdates"); + view_listener_t::addMenu(new LLAdvancedCheckShowObjectUpdates(), "Advanced.CheckShowObjectUpdates"); + view_listener_t::addMenu(new LLAdvancedCompressImage(), "Advanced.CompressImage"); + view_listener_t::addMenu(new LLAdvancedShowDebugSettings(), "Advanced.ShowDebugSettings"); + view_listener_t::addMenu(new LLAdvancedEnableViewAdminOptions(), "Advanced.EnableViewAdminOptions"); + view_listener_t::addMenu(new LLAdvancedToggleViewAdminOptions(), "Advanced.ToggleViewAdminOptions"); + view_listener_t::addMenu(new LLAdvancedCheckViewAdminOptions(), "Advanced.CheckViewAdminOptions"); + view_listener_t::addMenu(new LLAdvancedRequestAdminStatus(), "Advanced.RequestAdminStatus"); + view_listener_t::addMenu(new LLAdvancedLeaveAdminStatus(), "Advanced.LeaveAdminStatus"); + + + // Admin >Object + view_listener_t::addMenu(new LLAdminForceTakeCopy(), "Admin.ForceTakeCopy"); + view_listener_t::addMenu(new LLAdminHandleObjectOwnerSelf(), "Admin.HandleObjectOwnerSelf"); + view_listener_t::addMenu(new LLAdminHandleObjectOwnerPermissive(), "Admin.HandleObjectOwnerPermissive"); + view_listener_t::addMenu(new LLAdminHandleForceDelete(), "Admin.HandleForceDelete"); + view_listener_t::addMenu(new LLAdminHandleObjectLock(), "Admin.HandleObjectLock"); + view_listener_t::addMenu(new LLAdminHandleObjectAssetIDs(), "Admin.HandleObjectAssetIDs"); + + // Admin >Parcel + view_listener_t::addMenu(new LLAdminHandleForceParcelOwnerToMe(), "Admin.HandleForceParcelOwnerToMe"); + view_listener_t::addMenu(new LLAdminHandleForceParcelToContent(), "Admin.HandleForceParcelToContent"); + view_listener_t::addMenu(new LLAdminHandleClaimPublicLand(), "Admin.HandleClaimPublicLand"); + + // Admin >Region + view_listener_t::addMenu(new LLAdminHandleRegionDumpTempAssetData(), "Admin.HandleRegionDumpTempAssetData"); + // Admin top level + view_listener_t::addMenu(new LLAdminOnSaveState(), "Admin.OnSaveState"); + + // Self context menu + view_listener_t::addMenu(new LLSelfStandUp(), "Self.StandUp"); + enable.add("Self.EnableStandUp", boost::bind(&enable_standup_self)); + view_listener_t::addMenu(new LLSelfSitDown(), "Self.SitDown"); + enable.add("Self.EnableSitDown", boost::bind(&enable_sitdown_self)); + view_listener_t::addMenu(new LLSelfRemoveAllAttachments(), "Self.RemoveAllAttachments"); + + view_listener_t::addMenu(new LLSelfEnableRemoveAllAttachments(), "Self.EnableRemoveAllAttachments"); + + // we don't use boost::bind directly to delay side tray construction + view_listener_t::addMenu( new LLTogglePanelPeopleTab(), "SideTray.PanelPeopleTab"); + + // Avatar pie menu + view_listener_t::addMenu(new LLObjectMute(), "Avatar.Mute"); + view_listener_t::addMenu(new LLAvatarAddFriend(), "Avatar.AddFriend"); + view_listener_t::addMenu(new LLAvatarAddContact(), "Avatar.AddContact"); + commit.add("Avatar.Freeze", boost::bind(&handle_avatar_freeze, LLSD())); + view_listener_t::addMenu(new LLAvatarDebug(), "Avatar.Debug"); + view_listener_t::addMenu(new LLAvatarVisibleDebug(), "Avatar.VisibleDebug"); + view_listener_t::addMenu(new LLAvatarInviteToGroup(), "Avatar.InviteToGroup"); + commit.add("Avatar.Eject", boost::bind(&handle_avatar_eject, LLSD())); + commit.add("Avatar.ShowInspector", boost::bind(&handle_avatar_show_inspector)); + view_listener_t::addMenu(new LLAvatarSendIM(), "Avatar.SendIM"); + view_listener_t::addMenu(new LLAvatarCall(), "Avatar.Call"); + enable.add("Avatar.EnableCall", boost::bind(&LLAvatarActions::canCall)); + view_listener_t::addMenu(new LLAvatarReportAbuse(), "Avatar.ReportAbuse"); + + view_listener_t::addMenu(new LLAvatarEnableAddFriend(), "Avatar.EnableAddFriend"); + enable.add("Avatar.EnableFreezeEject", boost::bind(&enable_freeze_eject, _2)); + + // Object pie menu + view_listener_t::addMenu(new LLObjectBuild(), "Object.Build"); + commit.add("Object.Touch", boost::bind(&handle_object_touch)); + commit.add("Object.SitOrStand", boost::bind(&handle_object_sit_or_stand)); + commit.add("Object.Delete", boost::bind(&handle_object_delete)); + view_listener_t::addMenu(new LLObjectAttachToAvatar(true), "Object.AttachToAvatar"); + view_listener_t::addMenu(new LLObjectAttachToAvatar(false), "Object.AttachAddToAvatar"); + view_listener_t::addMenu(new LLObjectReturn(), "Object.Return"); + view_listener_t::addMenu(new LLObjectReportAbuse(), "Object.ReportAbuse"); + view_listener_t::addMenu(new LLObjectMute(), "Object.Mute"); + + enable.add("Object.VisibleTake", boost::bind(&visible_take_object)); + enable.add("Object.VisibleBuy", boost::bind(&visible_buy_object)); + + commit.add("Object.Buy", boost::bind(&handle_buy)); + commit.add("Object.Edit", boost::bind(&handle_object_edit)); + commit.add("Object.Inspect", boost::bind(&handle_object_inspect)); + commit.add("Object.Open", boost::bind(&handle_object_open)); + commit.add("Object.Take", boost::bind(&handle_take)); + commit.add("Object.ShowInspector", boost::bind(&handle_object_show_inspector)); + enable.add("Object.EnableOpen", boost::bind(&enable_object_open)); + enable.add("Object.EnableTouch", boost::bind(&enable_object_touch, _1)); + enable.add("Object.EnableDelete", boost::bind(&enable_object_delete)); + enable.add("Object.EnableWear", boost::bind(&object_selected_and_point_valid)); + + enable.add("Object.EnableStandUp", boost::bind(&enable_object_stand_up)); + enable.add("Object.EnableSit", boost::bind(&enable_object_sit, _1)); + + view_listener_t::addMenu(new LLObjectEnableReturn(), "Object.EnableReturn"); + view_listener_t::addMenu(new LLObjectEnableReportAbuse(), "Object.EnableReportAbuse"); + + enable.add("Avatar.EnableMute", boost::bind(&enable_object_mute)); + enable.add("Object.EnableMute", boost::bind(&enable_object_mute)); + enable.add("Object.EnableBuy", boost::bind(&enable_buy_object)); + commit.add("Object.ZoomIn", boost::bind(&handle_look_at_selection, "zoom")); + + // Attachment pie menu + enable.add("Attachment.Label", boost::bind(&onEnableAttachmentLabel, _1, _2)); + view_listener_t::addMenu(new LLAttachmentDrop(), "Attachment.Drop"); + view_listener_t::addMenu(new LLAttachmentDetachFromPoint(), "Attachment.DetachFromPoint"); + view_listener_t::addMenu(new LLAttachmentDetach(), "Attachment.Detach"); + view_listener_t::addMenu(new LLAttachmentPointFilled(), "Attachment.PointFilled"); + view_listener_t::addMenu(new LLAttachmentEnableDrop(), "Attachment.EnableDrop"); + view_listener_t::addMenu(new LLAttachmentEnableDetach(), "Attachment.EnableDetach"); + + // Land pie menu + view_listener_t::addMenu(new LLLandBuild(), "Land.Build"); + view_listener_t::addMenu(new LLLandSit(), "Land.Sit"); + view_listener_t::addMenu(new LLLandBuyPass(), "Land.BuyPass"); + view_listener_t::addMenu(new LLLandEdit(), "Land.Edit"); + + view_listener_t::addMenu(new LLLandEnableBuyPass(), "Land.EnableBuyPass"); + commit.add("Land.Buy", boost::bind(&handle_buy_land)); + + // Generic actions + commit.add("ReportAbuse", boost::bind(&handle_report_abuse)); + commit.add("BuyCurrency", boost::bind(&handle_buy_currency)); + view_listener_t::addMenu(new LLShowHelp(), "ShowHelp"); + view_listener_t::addMenu(new LLToggleHelp(), "ToggleHelp"); + view_listener_t::addMenu(new LLPromptShowURL(), "PromptShowURL"); + view_listener_t::addMenu(new LLShowAgentProfile(), "ShowAgentProfile"); + view_listener_t::addMenu(new LLToggleAgentProfile(), "ToggleAgentProfile"); + view_listener_t::addMenu(new LLToggleControl(), "ToggleControl"); + view_listener_t::addMenu(new LLCheckControl(), "CheckControl"); + view_listener_t::addMenu(new LLGoToObject(), "GoToObject"); + commit.add("PayObject", boost::bind(&handle_give_money_dialog)); + + enable.add("EnablePayObject", boost::bind(&enable_pay_object)); + enable.add("EnablePayAvatar", boost::bind(&enable_pay_avatar)); + enable.add("EnableEdit", boost::bind(&enable_object_edit)); + enable.add("VisibleBuild", boost::bind(&enable_object_build)); + + view_listener_t::addMenu(new LLFloaterVisible(), "FloaterVisible"); + view_listener_t::addMenu(new LLShowSidetrayPanel(), "ShowSidetrayPanel"); + view_listener_t::addMenu(new LLSidetrayPanelVisible(), "SidetrayPanelVisible"); + view_listener_t::addMenu(new LLSomethingSelected(), "SomethingSelected"); + view_listener_t::addMenu(new LLSomethingSelectedNoHUD(), "SomethingSelectedNoHUD"); + view_listener_t::addMenu(new LLEditableSelected(), "EditableSelected"); + view_listener_t::addMenu(new LLEditableSelectedMono(), "EditableSelectedMono"); + + view_listener_t::addMenu(new LLToggleUIHints(), "ToggleUIHints"); + + commit.add("DestinationAndAvatar.show", boost::bind(&toggle_destination_and_avatar_picker, _2)); +} diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index c2449907ac..2758250175 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -1,6895 +1,6906 @@ -/** - * @file llviewermessage.cpp - * @brief Dumping ground for viewer-side message system callbacks. - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" -#include "llviewermessage.h" -#include "boost/lexical_cast.hpp" - -// Linden libraries -#include "llanimationstates.h" -#include "llaudioengine.h" -#include "llavataractions.h" -#include "llavatarnamecache.h" // IDEVO HACK -#include "lscript_byteformat.h" -#include "lleconomy.h" -#include "lleventtimer.h" -#include "llfloaterreg.h" -#include "llfollowcamparams.h" -#include "llinventorydefines.h" -#include "lllslconstants.h" -#include "llregionhandle.h" -#include "llsdserialize.h" -#include "llteleportflags.h" -#include "lltransactionflags.h" -#include "llvfile.h" -#include "llvfs.h" -#include "llxfermanager.h" -#include "mean_collision_data.h" - -#include "llagent.h" -#include "llagentcamera.h" -#include "llcallingcard.h" -#include "llbuycurrencyhtml.h" -#include "llfirstuse.h" -#include "llfloaterbuyland.h" -#include "llfloaterland.h" -#include "llfloaterregioninfo.h" -#include "llfloaterlandholdings.h" -#include "llfloaterpostcard.h" -#include "llfloaterpreference.h" -#include "llhudeffecttrail.h" -#include "llhudmanager.h" -#include "llinventoryfunctions.h" -#include "llinventoryobserver.h" -#include "llinventorypanel.h" -#include "llnearbychat.h" -#include "llnotifications.h" -#include "llnotificationsutil.h" -#include "llpanelgrouplandmoney.h" -#include "llrecentpeople.h" -#include "llscriptfloater.h" -#include "llselectmgr.h" -#include "llsidetray.h" -#include "llstartup.h" -#include "llsky.h" -#include "llslurl.h" -#include "llstatenums.h" -#include "llstatusbar.h" -#include "llimview.h" -#include "llspeakers.h" -#include "lltrans.h" -#include "lltranslate.h" -#include "llviewerfoldertype.h" -#include "llvoavatar.h" // IDEVO HACK -#include "lluri.h" -#include "llviewergenericmessage.h" -#include "llviewermenu.h" -#include "llviewerjoystick.h" -#include "llviewerobjectlist.h" -#include "llviewerparcelmgr.h" -#include "llviewerstats.h" -#include "llviewertexteditor.h" -#include "llviewerthrottle.h" -#include "llviewerwindow.h" -#include "llvlmanager.h" -#include "llvoavatarself.h" -#include "llvotextbubble.h" -#include "llworld.h" -#include "pipeline.h" -#include "llfloaterworldmap.h" -#include "llviewerdisplay.h" -#include "llkeythrottle.h" -#include "llgroupactions.h" -#include "llagentui.h" -#include "llpanelblockedlist.h" -#include "llpanelplaceprofile.h" - -#include // -#include - -#include "llnotificationmanager.h" // - -#if LL_MSVC -// disable boost::lexical_cast warning -#pragma warning (disable:4702) -#endif - -// -// Constants -// -const F32 BIRD_AUDIBLE_RADIUS = 32.0f; -const F32 SIT_DISTANCE_FROM_TARGET = 0.25f; -static const F32 LOGOUT_REPLY_TIME = 3.f; // Wait this long after LogoutReply before quitting. - -// Determine how quickly residents' scripts can issue question dialogs -// Allow bursts of up to 5 dialogs in 10 seconds. 10*2=20 seconds recovery if throttle kicks in -static const U32 LLREQUEST_PERMISSION_THROTTLE_LIMIT = 5; // requests -static const F32 LLREQUEST_PERMISSION_THROTTLE_INTERVAL = 10.0f; // seconds - -extern BOOL gDebugClicks; - -// function prototypes -bool check_offer_throttle(const std::string& from_name, bool check_only); -static void process_money_balance_reply_extended(LLMessageSystem* msg); - -//inventory offer throttle globals -LLFrameTimer gThrottleTimer; -const U32 OFFER_THROTTLE_MAX_COUNT=5; //number of items per time period -const F32 OFFER_THROTTLE_TIME=10.f; //time period in seconds - -//script permissions -const std::string SCRIPT_QUESTIONS[SCRIPT_PERMISSION_EOF] = - { - "ScriptTakeMoney", - "ActOnControlInputs", - "RemapControlInputs", - "AnimateYourAvatar", - "AttachToYourAvatar", - "ReleaseOwnership", - "LinkAndDelink", - "AddAndRemoveJoints", - "ChangePermissions", - "TrackYourCamera", - "ControlYourCamera" - }; - -const BOOL SCRIPT_QUESTION_IS_CAUTION[SCRIPT_PERMISSION_EOF] = -{ - TRUE, // ScriptTakeMoney, - FALSE, // ActOnControlInputs - FALSE, // RemapControlInputs - FALSE, // AnimateYourAvatar - FALSE, // AttachToYourAvatar - FALSE, // ReleaseOwnership, - FALSE, // LinkAndDelink, - FALSE, // AddAndRemoveJoints - FALSE, // ChangePermissions - FALSE, // TrackYourCamera, - FALSE // ControlYourCamera -}; - -bool friendship_offer_callback(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - LLMessageSystem* msg = gMessageSystem; - const LLSD& payload = notification["payload"]; - - // add friend to recent people list - LLRecentPeople::instance().add(payload["from_id"]); - - switch(option) - { - case 0: - { - // accept - LLAvatarTracker::formFriendship(payload["from_id"]); - - const LLUUID fid = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); - - // This will also trigger an onlinenotification if the user is online - msg->newMessageFast(_PREHASH_AcceptFriendship); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_TransactionBlock); - msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]); - msg->nextBlockFast(_PREHASH_FolderData); - msg->addUUIDFast(_PREHASH_FolderID, fid); - msg->sendReliable(LLHost(payload["sender"].asString())); - - LLSD payload = notification["payload"]; - payload["SUPPRESS_TOAST"] = true; - LLNotificationsUtil::add("FriendshipAcceptedByMe", - notification["substitutions"], payload); - break; - } - case 1: // Decline - { - LLSD payload = notification["payload"]; - payload["SUPPRESS_TOAST"] = true; - LLNotificationsUtil::add("FriendshipDeclinedByMe", - notification["substitutions"], payload); - } - // fall-through - case 2: // Send IM - decline and start IM session - { - // decline - // We no longer notify other viewers, but we DO still send - // the rejection to the simulator to delete the pending userop. - msg->newMessageFast(_PREHASH_DeclineFriendship); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_TransactionBlock); - msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]); - msg->sendReliable(LLHost(payload["sender"].asString())); - - // start IM session - if(2 == option) - { - LLAvatarActions::startIM(payload["from_id"].asUUID()); - } - } - default: - // close button probably, possibly timed out - break; - } - - return false; -} -static LLNotificationFunctorRegistration friendship_offer_callback_reg("OfferFriendship", friendship_offer_callback); -static LLNotificationFunctorRegistration friendship_offer_callback_reg_nm("OfferFriendshipNoMessage", friendship_offer_callback); - -//const char BUSY_AUTO_RESPONSE[] = "The Resident you messaged is in 'busy mode' which means they have " -// "requested not to be disturbed. Your message will still be shown in their IM " -// "panel for later viewing."; - -// -// Functions -// - -void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_group, - S32 trx_type, const std::string& desc) -{ - if(0 == amount || !region) return; - amount = abs(amount); - LL_INFOS("Messaging") << "give_money(" << uuid << "," << amount << ")"<< LL_ENDL; - if(can_afford_transaction(amount)) - { -// gStatusBar->debitBalance(amount); - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_MoneyTransferRequest); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_MoneyData); - msg->addUUIDFast(_PREHASH_SourceID, gAgent.getID() ); - msg->addUUIDFast(_PREHASH_DestID, uuid); - msg->addU8Fast(_PREHASH_Flags, pack_transaction_flags(FALSE, is_group)); - msg->addS32Fast(_PREHASH_Amount, amount); - msg->addU8Fast(_PREHASH_AggregatePermNextOwner, (U8)LLAggregatePermissions::AP_EMPTY); - msg->addU8Fast(_PREHASH_AggregatePermInventory, (U8)LLAggregatePermissions::AP_EMPTY); - msg->addS32Fast(_PREHASH_TransactionType, trx_type ); - msg->addStringFast(_PREHASH_Description, desc); - msg->sendReliable(region->getHost()); - } - else - { - LLStringUtil::format_map_t args; - args["AMOUNT"] = llformat("%d", amount); - LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("giving", args), amount ); - } -} - -void send_complete_agent_movement(const LLHost& sim_host) -{ - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_CompleteAgentMovement); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addU32Fast(_PREHASH_CircuitCode, msg->mOurCircuitCode); - msg->sendReliable(sim_host); -} - -void process_logout_reply(LLMessageSystem* msg, void**) -{ - // The server has told us it's ok to quit. - LL_DEBUGS("Messaging") << "process_logout_reply" << LL_ENDL; - - LLUUID agent_id; - msg->getUUID("AgentData", "AgentID", agent_id); - LLUUID session_id; - msg->getUUID("AgentData", "SessionID", session_id); - if((agent_id != gAgent.getID()) || (session_id != gAgent.getSessionID())) - { - LL_WARNS("Messaging") << "Bogus Logout Reply" << LL_ENDL; - } - - LLInventoryModel::update_map_t parents; - S32 count = msg->getNumberOfBlocksFast( _PREHASH_InventoryData ); - for(S32 i = 0; i < count; ++i) - { - LLUUID item_id; - msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i); - - if( (1 == count) && item_id.isNull() ) - { - // Detect dummy item. Indicates an empty list. - break; - } - - // We do not need to track the asset ids, just account for an - // updated inventory version. - LL_INFOS("Messaging") << "process_logout_reply itemID=" << item_id << LL_ENDL; - LLInventoryItem* item = gInventory.getItem( item_id ); - if( item ) - { - parents[item->getParentUUID()] = 0; - gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item_id); - } - else - { - LL_INFOS("Messaging") << "process_logout_reply item not found: " << item_id << LL_ENDL; - } - } - LLAppViewer::instance()->forceQuit(); -} - -void process_layer_data(LLMessageSystem *mesgsys, void **user_data) -{ - LLViewerRegion *regionp = LLWorld::getInstance()->getRegion(mesgsys->getSender()); - - if (!regionp || gNoRender) - { - return; - } - - - S32 size; - S8 type; - - mesgsys->getS8Fast(_PREHASH_LayerID, _PREHASH_Type, type); - size = mesgsys->getSizeFast(_PREHASH_LayerData, _PREHASH_Data); - if (0 == size) - { - LL_WARNS("Messaging") << "Layer data has zero size." << LL_ENDL; - return; - } - if (size < 0) - { - // getSizeFast() is probably trying to tell us about an error - LL_WARNS("Messaging") << "getSizeFast() returned negative result: " - << size - << LL_ENDL; - return; - } - U8 *datap = new U8[size]; - mesgsys->getBinaryDataFast(_PREHASH_LayerData, _PREHASH_Data, datap, size); - LLVLData *vl_datap = new LLVLData(regionp, type, datap, size); - if (mesgsys->getReceiveCompressedSize()) - { - gVLManager.addLayerData(vl_datap, mesgsys->getReceiveCompressedSize()); - } - else - { - gVLManager.addLayerData(vl_datap, mesgsys->getReceiveSize()); - } -} - -// S32 exported_object_count = 0; -// S32 exported_image_count = 0; -// S32 current_object_count = 0; -// S32 current_image_count = 0; - -// extern LLNotifyBox *gExporterNotify; -// extern LLUUID gExporterRequestID; -// extern std::string gExportDirectory; - -// extern LLUploadDialog *gExportDialog; - -// std::string gExportedFile; - -// std::map gImageChecksums; - -// void export_complete() -// { -// LLUploadDialog::modalUploadFinished(); -// gExporterRequestID.setNull(); -// gExportDirectory = ""; - -// LLFILE* fXML = LLFile::fopen(gExportedFile, "rb"); /* Flawfinder: ignore */ -// fseek(fXML, 0, SEEK_END); -// long length = ftell(fXML); -// fseek(fXML, 0, SEEK_SET); -// U8 *buffer = new U8[length + 1]; -// size_t nread = fread(buffer, 1, length, fXML); -// if (nread < (size_t) length) -// { -// LL_WARNS("Messaging") << "Short read" << LL_ENDL; -// } -// buffer[nread] = '\0'; -// fclose(fXML); - -// char *pos = (char *)buffer; -// while ((pos = strstr(pos+1, ""); - -// if (pos_uuid) -// { -// char image_uuid_str[UUID_STR_SIZE]; /* Flawfinder: ignore */ -// memcpy(image_uuid_str, pos_uuid+2, UUID_STR_SIZE-1); /* Flawfinder: ignore */ -// image_uuid_str[UUID_STR_SIZE-1] = 0; - -// LLUUID image_uuid(image_uuid_str); - -// LL_INFOS("Messaging") << "Found UUID: " << image_uuid << LL_ENDL; - -// std::map::iterator itor = gImageChecksums.find(image_uuid); -// if (itor != gImageChecksums.end()) -// { -// LL_INFOS("Messaging") << "Replacing with checksum: " << itor->second << LL_ENDL; -// if (!itor->second.empty()) -// { -// memcpy(&pos_check[10], itor->second.c_str(), 32); /* Flawfinder: ignore */ -// } -// } -// } -// } -// } - -// LLFILE* fXMLOut = LLFile::fopen(gExportedFile, "wb"); /* Flawfinder: ignore */ -// if (fwrite(buffer, 1, length, fXMLOut) != length) -// { -// LL_WARNS("Messaging") << "Short write" << LL_ENDL; -// } -// fclose(fXMLOut); - -// delete [] buffer; -// } - - -// void exported_item_complete(const LLTSCode status, void *user_data) -// { -// //std::string *filename = (std::string *)user_data; - -// if (status < LLTS_OK) -// { -// LL_WARNS("Messaging") << "Export failed!" << LL_ENDL; -// } -// else -// { -// ++current_object_count; -// if (current_image_count == exported_image_count && current_object_count == exported_object_count) -// { -// LL_INFOS("Messaging") << "*** Export complete ***" << LL_ENDL; - -// export_complete(); -// } -// else -// { -// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count)); -// } -// } -// } - -// struct exported_image_info -// { -// LLUUID image_id; -// std::string filename; -// U32 image_num; -// }; - -// void exported_j2c_complete(const LLTSCode status, void *user_data) -// { -// exported_image_info *info = (exported_image_info *)user_data; -// LLUUID image_id = info->image_id; -// U32 image_num = info->image_num; -// std::string filename = info->filename; -// delete info; - -// if (status < LLTS_OK) -// { -// LL_WARNS("Messaging") << "Image download failed!" << LL_ENDL; -// } -// else -// { -// LLFILE* fIn = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */ -// if (fIn) -// { -// LLPointer ImageUtility = new LLImageJ2C; -// LLPointer TargaUtility = new LLImageTGA; - -// fseek(fIn, 0, SEEK_END); -// S32 length = ftell(fIn); -// fseek(fIn, 0, SEEK_SET); -// U8 *buffer = ImageUtility->allocateData(length); -// if (fread(buffer, 1, length, fIn) != length) -// { -// LL_WARNS("Messaging") << "Short read" << LL_ENDL; -// } -// fclose(fIn); -// LLFile::remove(filename); - -// // Convert to TGA -// LLPointer image = new LLImageRaw(); - -// ImageUtility->updateData(); -// ImageUtility->decode(image, 100000.0f); - -// TargaUtility->encode(image); -// U8 *data = TargaUtility->getData(); -// S32 data_size = TargaUtility->getDataSize(); - -// std::string file_path = gDirUtilp->getDirName(filename); - -// std::string output_file = llformat("%s/image-%03d.tga", file_path.c_str(), image_num);//filename; -// //S32 name_len = output_file.length(); -// //strcpy(&output_file[name_len-3], "tga"); -// LLFILE* fOut = LLFile::fopen(output_file, "wb"); /* Flawfinder: ignore */ -// char md5_hash_string[33]; /* Flawfinder: ignore */ -// strcpy(md5_hash_string, "00000000000000000000000000000000"); /* Flawfinder: ignore */ -// if (fOut) -// { -// if (fwrite(data, 1, data_size, fOut) != data_size) -// { -// LL_WARNS("Messaging") << "Short write" << LL_ENDL; -// } -// fseek(fOut, 0, SEEK_SET); -// fclose(fOut); -// fOut = LLFile::fopen(output_file, "rb"); /* Flawfinder: ignore */ -// LLMD5 my_md5_hash(fOut); -// my_md5_hash.hex_digest(md5_hash_string); -// } - -// gImageChecksums.insert(std::pair(image_id, md5_hash_string)); -// } -// } - -// ++current_image_count; -// if (current_image_count == exported_image_count && current_object_count == exported_object_count) -// { -// LL_INFOS("Messaging") << "*** Export textures complete ***" << LL_ENDL; -// export_complete(); -// } -// else -// { -// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count)); -// } -//} - -void process_derez_ack(LLMessageSystem*, void**) -{ - if(gViewerWindow) gViewerWindow->getWindow()->decBusyCount(); -} - -void process_places_reply(LLMessageSystem* msg, void** data) -{ - LLUUID query_id; - - msg->getUUID("AgentData", "QueryID", query_id); - if (query_id.isNull()) - { - LLFloaterLandHoldings::processPlacesReply(msg, data); - } - else if(gAgent.isInGroup(query_id)) - { - LLPanelGroupLandMoney::processPlacesReply(msg, data); - } - else - { - LL_WARNS("Messaging") << "Got invalid PlacesReply message" << LL_ENDL; - } -} - -void send_sound_trigger(const LLUUID& sound_id, F32 gain) -{ - if (sound_id.isNull() || gAgent.getRegion() == NULL) - { - // disconnected agent or zero guids don't get sent (no sound) - return; - } - - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_SoundTrigger); - msg->nextBlockFast(_PREHASH_SoundData); - msg->addUUIDFast(_PREHASH_SoundID, sound_id); - // Client untrusted, ids set on sim - msg->addUUIDFast(_PREHASH_OwnerID, LLUUID::null ); - msg->addUUIDFast(_PREHASH_ObjectID, LLUUID::null ); - msg->addUUIDFast(_PREHASH_ParentID, LLUUID::null ); - - msg->addU64Fast(_PREHASH_Handle, gAgent.getRegion()->getHandle()); - - LLVector3 position = gAgent.getPositionAgent(); - msg->addVector3Fast(_PREHASH_Position, position); - msg->addF32Fast(_PREHASH_Gain, gain); - - gAgent.sendMessage(); -} - -bool join_group_response(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - BOOL delete_context_data = TRUE; - bool accept_invite = false; - - LLUUID group_id = notification["payload"]["group_id"].asUUID(); - LLUUID transaction_id = notification["payload"]["transaction_id"].asUUID(); - std::string name = notification["payload"]["name"].asString(); - std::string message = notification["payload"]["message"].asString(); - S32 fee = notification["payload"]["fee"].asInteger(); - - if (option == 2 && !group_id.isNull()) - { - LLGroupActions::show(group_id); - LLSD args; - args["MESSAGE"] = message; - LLNotificationsUtil::add("JoinGroup", args, notification["payload"]); - return false; - } - if(option == 0 && !group_id.isNull()) - { - // check for promotion or demotion. - S32 max_groups = gMaxAgentGroups; - if(gAgent.isInGroup(group_id)) ++max_groups; - - if(gAgent.mGroups.count() < max_groups) - { - accept_invite = true; - } - else - { - delete_context_data = FALSE; - LLSD args; - args["NAME"] = name; - LLNotificationsUtil::add("JoinedTooManyGroupsMember", args, notification["payload"]); - } - } - - if (accept_invite) - { - // If there is a fee to join this group, make - // sure the user is sure they want to join. - if (fee > 0) - { - delete_context_data = FALSE; - LLSD args; - args["COST"] = llformat("%d", fee); - // Set the fee for next time to 0, so that we don't keep - // asking about a fee. - LLSD next_payload = notification["payload"]; - next_payload["fee"] = 0; - LLNotificationsUtil::add("JoinGroupCanAfford", - args, - next_payload); - } - else - { - send_improved_im(group_id, - std::string("name"), - std::string("message"), - IM_ONLINE, - IM_GROUP_INVITATION_ACCEPT, - transaction_id); - } - } - else - { - send_improved_im(group_id, - std::string("name"), - std::string("message"), - IM_ONLINE, - IM_GROUP_INVITATION_DECLINE, - transaction_id); - } - - return false; -} - -static void highlight_inventory_items_in_panel(const std::vector& items, LLInventoryPanel *inventory_panel) -{ - if (NULL == inventory_panel) return; - - for (std::vector::const_iterator item_iter = items.begin(); - item_iter != items.end(); - ++item_iter) - { - const LLUUID& item_id = (*item_iter); - if(!highlight_offered_object(item_id)) - { - continue; - } - - LLInventoryItem* item = gInventory.getItem(item_id); - llassert(item); - if (!item) { - continue; - } - - LL_DEBUGS("Inventory_Move") << "Highlighting inventory item: " << item->getName() << ", " << item_id << LL_ENDL; - LLFolderView* fv = inventory_panel->getRootFolder(); - if (fv) - { - LLFolderViewItem* fv_item = fv->getItemByID(item_id); - if (fv_item) - { - LLFolderViewItem* fv_folder = fv_item->getParentFolder(); - if (fv_folder) - { - // Parent folders can be different in case of 2 consecutive drag and drop - // operations when the second one is started before the first one completes. - LL_DEBUGS("Inventory_Move") << "Open folder: " << fv_folder->getName() << LL_ENDL; - fv_folder->setOpen(TRUE); - if (fv_folder->isSelected()) - { - fv->changeSelection(fv_folder, FALSE); - } - } - fv->changeSelection(fv_item, TRUE); - } - } - } -} - -static LLNotificationFunctorRegistration jgr_1("JoinGroup", join_group_response); -static LLNotificationFunctorRegistration jgr_2("JoinedTooManyGroupsMember", join_group_response); -static LLNotificationFunctorRegistration jgr_3("JoinGroupCanAfford", join_group_response); - - -//----------------------------------------------------------------------------- -// Instant Message -//----------------------------------------------------------------------------- -class LLOpenAgentOffer : public LLInventoryFetchItemsObserver -{ -public: - LLOpenAgentOffer(const LLUUID& object_id, - const std::string& from_name) : - LLInventoryFetchItemsObserver(object_id), - mFromName(from_name) {} - /*virtual*/ void startFetch() - { - for (uuid_vec_t::const_iterator it = mIDs.begin(); it < mIDs.end(); ++it) - { - LLViewerInventoryCategory* cat = gInventory.getCategory(*it); - if (cat) - { - mComplete.push_back((*it)); - } - } - LLInventoryFetchItemsObserver::startFetch(); - } - /*virtual*/ void done() - { - open_inventory_offer(mComplete, mFromName); - gInventory.removeObserver(this); - delete this; - } -private: - std::string mFromName; -}; - -/** - * Class to observe adding of new items moved from the world to user's inventory to select them in inventory. - * - * We can't create it each time items are moved because "drop" event is sent separately for each - * element even while multi-dragging. We have to have the only instance of the observer. See EXT-4347. - */ -class LLViewerInventoryMoveFromWorldObserver : public LLInventoryAddItemByAssetObserver -{ -public: - LLViewerInventoryMoveFromWorldObserver() - : LLInventoryAddItemByAssetObserver() - , mActivePanel(NULL) - { - - } - - void setMoveIntoFolderID(const LLUUID& into_folder_uuid) {mMoveIntoFolderID = into_folder_uuid; } - -private: - /*virtual */void onAssetAdded(const LLUUID& asset_id) - { - // Store active Inventory panel. - mActivePanel = LLInventoryPanel::getActiveInventoryPanel(); - - // Store selected items (without destination folder) - mSelectedItems.clear(); - if (mActivePanel) - { - mSelectedItems = mActivePanel->getRootFolder()->getSelectionList(); - } - mSelectedItems.erase(mMoveIntoFolderID); - } - - /** - * Selects added inventory items watched by their Asset UUIDs if selection was not changed since - * all items were started to watch (dropped into a folder). - */ - void done() - { - // if selection is not changed since watch started lets hightlight new items. - if (mActivePanel && !isSelectionChanged()) - { - LL_DEBUGS("Inventory_Move") << "Selecting new items..." << LL_ENDL; - mActivePanel->clearSelection(); - highlight_inventory_items_in_panel(mAddedItems, mActivePanel); - } - } - - /** - * Returns true if selected inventory items were changed since moved inventory items were started to watch. - */ - bool isSelectionChanged() - { - const LLInventoryPanel * const current_active_panel = LLInventoryPanel::getActiveInventoryPanel(); - - if (NULL == mActivePanel || current_active_panel != mActivePanel) - { - return true; - } - - // get selected items (without destination folder) - selected_items_t selected_items = mActivePanel->getRootFolder()->getSelectionList(); - selected_items.erase(mMoveIntoFolderID); - - // compare stored & current sets of selected items - selected_items_t different_items; - std::set_symmetric_difference(mSelectedItems.begin(), mSelectedItems.end(), - selected_items.begin(), selected_items.end(), std::inserter(different_items, different_items.begin())); - - LL_DEBUGS("Inventory_Move") << "Selected firstly: " << mSelectedItems.size() - << ", now: " << selected_items.size() << ", difference: " << different_items.size() << LL_ENDL; - - return different_items.size() > 0; - } - - LLInventoryPanel *mActivePanel; - typedef std::set selected_items_t; - selected_items_t mSelectedItems; - - /** - * UUID of FolderViewFolder into which watched items are moved. - * - * Destination FolderViewFolder becomes selected while mouse hovering (when dragged items are dropped). - * - * If mouse is moved out it set unselected and number of selected items is changed - * even if selected items in Inventory stay the same. - * So, it is used to update stored selection list. - * - * @see onAssetAdded() - * @see isSelectionChanged() - */ - LLUUID mMoveIntoFolderID; -}; - -LLViewerInventoryMoveFromWorldObserver* gInventoryMoveObserver = NULL; - -void set_dad_inventory_item(LLInventoryItem* inv_item, const LLUUID& into_folder_uuid) -{ - start_new_inventory_observer(); - - gInventoryMoveObserver->setMoveIntoFolderID(into_folder_uuid); - gInventoryMoveObserver->watchAsset(inv_item->getAssetUUID()); -} - -//unlike the FetchObserver for AgentOffer, we only make one -//instance of the AddedObserver for TaskOffers -//and it never dies. We do this because we don't know the UUID of -//task offers until they are accepted, so we don't wouldn't -//know what to watch for, so instead we just watch for all additions. -class LLOpenTaskOffer : public LLInventoryAddedObserver -{ -protected: - /*virtual*/ void done() - { - for (uuid_vec_t::iterator it = mAdded.begin(); it != mAdded.end();) - { - const LLUUID& item_uuid = *it; - bool was_moved = false; - LLInventoryObject* added_object = gInventory.getObject(item_uuid); - if (added_object) - { - // cast to item to get Asset UUID - LLInventoryItem* added_item = dynamic_cast(added_object); - if (added_item) - { - const LLUUID& asset_uuid = added_item->getAssetUUID(); - if (gInventoryMoveObserver->isAssetWatched(asset_uuid)) - { - LL_DEBUGS("Inventory_Move") << "Found asset UUID: " << asset_uuid << LL_ENDL; - was_moved = true; - } - } - } - - if (was_moved) - { - it = mAdded.erase(it); - } - else ++it; - } - - open_inventory_offer(mAdded, ""); - mAdded.clear(); - } - }; - -class LLOpenTaskGroupOffer : public LLInventoryAddedObserver -{ -protected: - /*virtual*/ void done() - { - open_inventory_offer(mAdded, "group_offer"); - mAdded.clear(); - gInventory.removeObserver(this); - delete this; - } -}; - -//one global instance to bind them -LLOpenTaskOffer* gNewInventoryObserver=NULL; - -class LLNewInventoryHintObserver : public LLInventoryAddedObserver -{ -protected: - /*virtual*/ void done() - { - LLFirstUse::newInventory(); - } -}; - -void start_new_inventory_observer() -{ - if (!gNewInventoryObserver) //task offer observer - { - // Observer is deleted by gInventory - gNewInventoryObserver = new LLOpenTaskOffer; - gInventory.addObserver(gNewInventoryObserver); - } - - if (!gInventoryMoveObserver) //inventory move from the world observer - { - // Observer is deleted by gInventory - gInventoryMoveObserver = new LLViewerInventoryMoveFromWorldObserver; - gInventory.addObserver(gInventoryMoveObserver); - } - - gInventory.addObserver(new LLNewInventoryHintObserver()); -} - -class LLDiscardAgentOffer : public LLInventoryFetchItemsObserver -{ - LOG_CLASS(LLDiscardAgentOffer); -public: - LLDiscardAgentOffer(const LLUUID& folder_id, const LLUUID& object_id) : - LLInventoryFetchItemsObserver(object_id), - mFolderID(folder_id), - mObjectID(object_id) {} - virtual ~LLDiscardAgentOffer() {} - virtual void done() - { - LL_DEBUGS("Messaging") << "LLDiscardAgentOffer::done()" << LL_ENDL; - const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); - bool notify = false; - if(trash_id.notNull() && mObjectID.notNull()) - { - LLInventoryModel::update_list_t update; - LLInventoryModel::LLCategoryUpdate old_folder(mFolderID, -1); - update.push_back(old_folder); - LLInventoryModel::LLCategoryUpdate new_folder(trash_id, 1); - update.push_back(new_folder); - gInventory.accountForUpdate(update); - gInventory.moveObject(mObjectID, trash_id); - LLInventoryObject* obj = gInventory.getObject(mObjectID); - if(obj) - { - // no need to restamp since this is already a freshly - // stamped item. - obj->updateParentOnServer(FALSE); - notify = true; - } - } - else - { - LL_WARNS("Messaging") << "DiscardAgentOffer unable to find: " - << (trash_id.isNull() ? "trash " : "") - << (mObjectID.isNull() ? "object" : "") << LL_ENDL; - } - gInventory.removeObserver(this); - if(notify) - { - gInventory.notifyObservers(); - } - delete this; - } -protected: - LLUUID mFolderID; - LLUUID mObjectID; -}; - - -//Returns TRUE if we are OK, FALSE if we are throttled -//Set check_only true if you want to know the throttle status -//without registering a hit -bool check_offer_throttle(const std::string& from_name, bool check_only) -{ - static U32 throttle_count; - static bool throttle_logged; - LLChat chat; - std::string log_message; - - if (!gSavedSettings.getBOOL("ShowNewInventory")) - return false; - - if (check_only) - { - return gThrottleTimer.hasExpired(); - } - - if(gThrottleTimer.checkExpirationAndReset(OFFER_THROTTLE_TIME)) - { - LL_DEBUGS("Messaging") << "Throttle Expired" << LL_ENDL; - throttle_count=1; - throttle_logged=false; - return true; - } - else //has not expired - { - LL_DEBUGS("Messaging") << "Throttle Not Expired, Count: " << throttle_count << LL_ENDL; - // When downloading the initial inventory we get a lot of new items - // coming in and can't tell that from spam. - if (LLStartUp::getStartupState() >= STATE_STARTED - && throttle_count >= OFFER_THROTTLE_MAX_COUNT) - { - if (!throttle_logged) - { - // Use the name of the last item giver, who is probably the person - // spamming you. - - LLStringUtil::format_map_t arg; - std::string log_msg; - std::ostringstream time ; - time<getSecondLifeTitle(); - arg["TIME"] = time.str(); - - if (!from_name.empty()) - { - arg["FROM_NAME"] = from_name; - log_msg = LLTrans::getString("ItemsComingInTooFastFrom", arg); - } - else - { - log_msg = LLTrans::getString("ItemsComingInTooFast", arg); - } - - //this is kinda important, so actually put it on screen - LLSD args; - args["MESSAGE"] = log_msg; - LLNotificationsUtil::add("SystemMessage", args); - - throttle_logged=true; - } - return false; - } - else - { - throttle_count++; - return true; - } - } -} - -void open_inventory_offer(const uuid_vec_t& objects, const std::string& from_name) -{ - for (uuid_vec_t::const_iterator obj_iter = objects.begin(); - obj_iter != objects.end(); - ++obj_iter) - { - const LLUUID& obj_id = (*obj_iter); - if(!highlight_offered_object(obj_id)) - { - continue; - } - - const LLInventoryObject *obj = gInventory.getObject(obj_id); - if (!obj) - { - llwarns << "Cannot find object [ itemID:" << obj_id << " ] to open." << llendl; - continue; - } - - const LLAssetType::EType asset_type = obj->getActualType(); - - // Either an inventory item or a category. - const LLInventoryItem* item = dynamic_cast(obj); - if (item) - { - //////////////////////////////////////////////////////////////////////////////// - // Special handling for various types. - if (check_offer_throttle(from_name, false)) // If we are throttled, don't display - { - LL_DEBUGS("Messaging") << "Highlighting inventory item: " << item->getUUID() << LL_ENDL; - // If we opened this ourselves, focus it - const BOOL take_focus = from_name.empty() ? TAKE_FOCUS_YES : TAKE_FOCUS_NO; - switch(asset_type) - { - case LLAssetType::AT_NOTECARD: - { - LLFloaterReg::showInstance("preview_notecard", LLSD(obj_id), take_focus); - break; - } - case LLAssetType::AT_LANDMARK: - { - LLInventoryCategory* parent_folder = gInventory.getCategory(item->getParentUUID()); - if ("inventory_handler" == from_name) - { - //we have to filter inventory_handler messages to avoid notification displaying - LLSideTray::getInstance()->showPanel("panel_places", - LLSD().with("type", "landmark").with("id", item->getUUID())); - } - else if("group_offer" == from_name) - { - // "group_offer" is passed by LLOpenTaskGroupOffer - // Notification about added landmark will be generated under the "from_name.empty()" called from LLOpenTaskOffer::done(). - LLSD args; - args["type"] = "landmark"; - args["id"] = obj_id; - LLSideTray::getInstance()->showPanel("panel_places", args); - - continue; - } - else if(from_name.empty()) - { - std::string folder_name; - if (parent_folder) - { - // Localize folder name. - // *TODO: share this code? - folder_name = parent_folder->getName(); - if (LLFolderType::lookupIsProtectedType(parent_folder->getPreferredType())) - { - LLTrans::findString(folder_name, "InvFolder " + folder_name); - } - } - else - { - folder_name = LLTrans::getString("Unknown"); - } - - // we receive a message from LLOpenTaskOffer, it mean that new landmark has been added. - LLSD args; - args["LANDMARK_NAME"] = item->getName(); - args["FOLDER_NAME"] = folder_name; - LLNotificationsUtil::add("LandmarkCreated", args); - } - } - break; - case LLAssetType::AT_TEXTURE: - { - LLFloaterReg::showInstance("preview_texture", LLSD(obj_id), take_focus); - break; - } - case LLAssetType::AT_ANIMATION: - LLFloaterReg::showInstance("preview_anim", LLSD(obj_id), take_focus); - break; - case LLAssetType::AT_SCRIPT: - LLFloaterReg::showInstance("preview_script", LLSD(obj_id), take_focus); - break; - case LLAssetType::AT_SOUND: - LLFloaterReg::showInstance("preview_sound", LLSD(obj_id), take_focus); - break; - default: - break; - } - } - } - - //////////////////////////////////////////////////////////////////////////////// - // Highlight item - const BOOL auto_open = - gSavedSettings.getBOOL("ShowInInventory") && // don't open if showininventory is false - !(asset_type == LLAssetType::AT_CALLINGCARD) && // don't open if it's a calling card - !from_name.empty(); // don't open if it's not from anyone. - LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(auto_open); - if(active_panel) - { - LL_DEBUGS("Messaging") << "Highlighting" << obj_id << LL_ENDL; - LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus(); - active_panel->setSelection(obj_id, TAKE_FOCUS_NO); - gFocusMgr.setKeyboardFocus(focus_ctrl); - } - } -} - -bool highlight_offered_object(const LLUUID& obj_id) -{ - const LLInventoryObject* obj = gInventory.getObject(obj_id); - if(!obj) - { - LL_WARNS("Messaging") << "Unable to show inventory item: " << obj_id << LL_ENDL; - return false; - } - - //////////////////////////////////////////////////////////////////////////////// - // Don't highlight if it's in certain "quiet" folders which don't need UI - // notification (e.g. trash, cof, lost-and-found). - if(!gAgent.getAFK()) - { - const LLViewerInventoryCategory *parent = gInventory.getFirstNondefaultParent(obj_id); - if (parent) - { - const LLFolderType::EType parent_type = parent->getPreferredType(); - if (LLViewerFolderType::lookupIsQuietType(parent_type)) - { - return false; - } - } - } - - return true; -} - -void inventory_offer_mute_callback(const LLUUID& blocked_id, - const std::string& full_name, - bool is_group, - boost::shared_ptr offer_ptr) -{ - LLOfferInfo* offer = dynamic_cast(offer_ptr.get()); - - std::string from_name = full_name; - LLMute::EType type; - if (is_group) - { - type = LLMute::GROUP; - } - else if(offer && offer->mFromObject) - { - //we have to block object by name because blocked_id is an id of owner - type = LLMute::BY_NAME; - } - else - { - type = LLMute::AGENT; - } - - // id should be null for BY_NAME mute, see LLMuteList::add for details - LLMute mute(type == LLMute::BY_NAME ? LLUUID::null : blocked_id, from_name, type); - if (LLMuteList::getInstance()->add(mute)) - { - LLPanelBlockedList::showPanelAndSelect(blocked_id); - } - - // purge the message queue of any previously queued inventory offers from the same source. - class OfferMatcher : public LLNotificationsUI::LLScreenChannel::Matcher - { - public: - OfferMatcher(const LLUUID& to_block) : blocked_id(to_block) {} - bool matches(const LLNotificationPtr notification) const - { - if(notification->getName() == "ObjectGiveItem" - || notification->getName() == "UserGiveItem") - { - return (notification->getPayload()["from_id"].asUUID() == blocked_id); - } - return FALSE; - } - private: - const LLUUID& blocked_id; - }; - - LLNotificationsUI::LLChannelManager::getInstance()->killToastsFromChannel(LLUUID( - gSavedSettings.getString("NotificationChannelUUID")), OfferMatcher(blocked_id)); -} - -LLOfferInfo::LLOfferInfo() - : LLNotificationResponderInterface() - , mFromGroup(FALSE) - , mFromObject(FALSE) - , mIM(IM_NOTHING_SPECIAL) - , mType(LLAssetType::AT_NONE) - , mPersist(false) -{ -} - -LLOfferInfo::LLOfferInfo(const LLSD& sd) -{ - mIM = (EInstantMessage)sd["im_type"].asInteger(); - mFromID = sd["from_id"].asUUID(); - mFromGroup = sd["from_group"].asBoolean(); - mFromObject = sd["from_object"].asBoolean(); - mTransactionID = sd["transaction_id"].asUUID(); - mFolderID = sd["folder_id"].asUUID(); - mObjectID = sd["object_id"].asUUID(); - mType = LLAssetType::lookup(sd["type"].asString().c_str()); - mFromName = sd["from_name"].asString(); - mDesc = sd["description"].asString(); - mHost = LLHost(sd["sender"].asString()); - mPersist = sd["persist"].asBoolean(); -} - -LLOfferInfo::LLOfferInfo(const LLOfferInfo& info) -{ - mIM = info.mIM; - mFromID = info.mFromID; - mFromGroup = info.mFromGroup; - mFromObject = info.mFromObject; - mTransactionID = info.mTransactionID; - mFolderID = info.mFolderID; - mObjectID = info.mObjectID; - mType = info.mType; - mFromName = info.mFromName; - mDesc = info.mDesc; - mHost = info.mHost; - mPersist = info.mPersist; -} - -LLSD LLOfferInfo::asLLSD() -{ - LLSD sd; - sd["im_type"] = mIM; - sd["from_id"] = mFromID; - sd["from_group"] = mFromGroup; - sd["from_object"] = mFromObject; - sd["transaction_id"] = mTransactionID; - sd["folder_id"] = mFolderID; - sd["object_id"] = mObjectID; - sd["type"] = LLAssetType::lookup(mType); - sd["from_name"] = mFromName; - sd["description"] = mDesc; - sd["sender"] = mHost.getIPandPort(); - sd["persist"] = mPersist; - return sd; -} - -void LLOfferInfo::fromLLSD(const LLSD& params) -{ - *this = params; -} - -void LLOfferInfo::send_auto_receive_response(void) -{ - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_ImprovedInstantMessage); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_MessageBlock); - msg->addBOOLFast(_PREHASH_FromGroup, FALSE); - msg->addUUIDFast(_PREHASH_ToAgentID, mFromID); - msg->addU8Fast(_PREHASH_Offline, IM_ONLINE); - msg->addUUIDFast(_PREHASH_ID, mTransactionID); - msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary - std::string name; - LLAgentUI::buildFullname(name); - msg->addStringFast(_PREHASH_FromAgentName, name); - msg->addStringFast(_PREHASH_Message, ""); - msg->addU32Fast(_PREHASH_ParentEstateID, 0); - msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null); - msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent()); - - // Auto Receive Message. The math for the dialog works, because the accept - // for inventory_offered, task_inventory_offer or - // group_notice_inventory is 1 greater than the offer integer value. - // Generates IM_INVENTORY_ACCEPTED, IM_TASK_INVENTORY_ACCEPTED, - // or IM_GROUP_NOTICE_INVENTORY_ACCEPTED - msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 1)); - msg->addBinaryDataFast(_PREHASH_BinaryBucket, &(mFolderID.mData), - sizeof(mFolderID.mData)); - // send the message - msg->sendReliable(mHost); - - if(IM_INVENTORY_OFFERED == mIM) - { - // add buddy to recent people list - LLRecentPeople::instance().add(mFromID); - } -} - -void LLOfferInfo::handleRespond(const LLSD& notification, const LLSD& response) -{ - initRespondFunctionMap(); - - const std::string name = notification["name"].asString(); - if(mRespondFunctions.find(name) == mRespondFunctions.end()) - { - llwarns << "Unexpected notification name : " << name << llendl; - llassert(!"Unexpected notification name"); - return; - } - - mRespondFunctions[name](notification, response); -} - -bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& response) -{ - LLChat chat; - std::string log_message; - S32 button = LLNotificationsUtil::getSelectedOption(notification, response); - - LLInventoryObserver* opener = NULL; - LLViewerInventoryCategory* catp = NULL; - catp = (LLViewerInventoryCategory*)gInventory.getCategory(mObjectID); - LLViewerInventoryItem* itemp = NULL; - if(!catp) - { - itemp = (LLViewerInventoryItem*)gInventory.getItem(mObjectID); - } - - // For muting, we need to add the mute, then decline the offer. - // This must be done here because: - // * callback may be called immediately, - // * adding the mute sends a message, - // * we can't build two messages at once. - if (2 == button) // Block - { - LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); - - llassert(notification_ptr != NULL); - if (notification_ptr != NULL) - { - gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback,_1,_2,_3,notification_ptr->getResponderPtr())); - } - } - - std::string from_string; // Used in the pop-up. - std::string chatHistory_string; // Used in chat history. - - // TODO: when task inventory offers can also be handled the new way, migrate the code that sets these strings here: - from_string = chatHistory_string = mFromName; - - bool busy=FALSE; - - switch(button) - { - case IOR_SHOW: - // we will want to open this item when it comes back. - LL_DEBUGS("Messaging") << "Initializing an opener for tid: " << mTransactionID - << LL_ENDL; - switch (mIM) - { - case IM_INVENTORY_OFFERED: - { - // This is an offer from an agent. In this case, the back - // end has already copied the items into your inventory, - // so we can fetch it out of our inventory. - LLOpenAgentOffer* open_agent_offer = new LLOpenAgentOffer(mObjectID, from_string); - open_agent_offer->startFetch(); - if(catp || (itemp && itemp->isFinished())) - { - open_agent_offer->done(); - } - else - { - opener = open_agent_offer; - } - } - break; - case IM_GROUP_NOTICE: - opener = new LLOpenTaskGroupOffer; - send_auto_receive_response(); - break; - case IM_TASK_INVENTORY_OFFERED: - case IM_GROUP_NOTICE_REQUESTED: - // This is an offer from a task or group. - // We don't use a new instance of an opener - // We instead use the singular observer gOpenTaskOffer - // Since it already exists, we don't need to actually do anything - break; - default: - LL_WARNS("Messaging") << "inventory_offer_callback: unknown offer type" << LL_ENDL; - break; - } - break; - // end switch (mIM) - - case IOR_ACCEPT: - //don't spam them if they are getting flooded - if (check_offer_throttle(mFromName, true)) - { - log_message = chatHistory_string + " " + LLTrans::getString("InvOfferGaveYou") + " " + mDesc + LLTrans::getString("."); - LLSD args; - args["MESSAGE"] = log_message; - LLNotificationsUtil::add("SystemMessage", args); - } - break; - - case IOR_BUSY: - //Busy falls through to decline. Says to make busy message. - busy=TRUE; - case IOR_MUTE: - // MUTE falls through to decline - case IOR_DECLINE: - { - { - LLStringUtil::format_map_t log_message_args; - log_message_args["DESC"] = mDesc; - log_message_args["NAME"] = mFromName; - log_message = LLTrans::getString("InvOfferDecline", log_message_args); - } - chat.mText = log_message; - if( LLMuteList::getInstance()->isMuted(mFromID ) && ! LLMuteList::getInstance()->isLinden(mFromName) ) // muting for SL-42269 - { - chat.mMuted = TRUE; - } - - // *NOTE dzaporozhan - // Disabled logging to old chat floater to fix crash in group notices - EXT-4149 - // LLFloaterChat::addChatHistory(chat); - - LLDiscardAgentOffer* discard_agent_offer = new LLDiscardAgentOffer(mFolderID, mObjectID); - discard_agent_offer->startFetch(); - if (catp || (itemp && itemp->isFinished())) - { - discard_agent_offer->done(); - } - else - { - opener = discard_agent_offer; - } - - - if (busy && (!mFromGroup && !mFromObject)) - { - busy_message(gMessageSystem, mFromID); - } - break; - } - default: - // close button probably - // The item has already been fetched and is in your inventory, we simply won't highlight it - // OR delete it if the notification gets killed, since we don't want that to be a vector for - // losing inventory offers. - break; - } - - if(opener) - { - gInventory.addObserver(opener); - } - - if(!mPersist) - { - delete this; - } - return false; -} - -bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const LLSD& response) -{ - LLChat chat; - std::string log_message; - S32 button = LLNotification::getSelectedOption(notification, response); - - // For muting, we need to add the mute, then decline the offer. - // This must be done here because: - // * callback may be called immediately, - // * adding the mute sends a message, - // * we can't build two messages at once. - if (2 == button) - { - LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); - - llassert(notification_ptr != NULL); - if (notification_ptr != NULL) - { - gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback,_1,_2,_3,notification_ptr->getResponderPtr())); - } - } - - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_ImprovedInstantMessage); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_MessageBlock); - msg->addBOOLFast(_PREHASH_FromGroup, FALSE); - msg->addUUIDFast(_PREHASH_ToAgentID, mFromID); - msg->addU8Fast(_PREHASH_Offline, IM_ONLINE); - msg->addUUIDFast(_PREHASH_ID, mTransactionID); - msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary - std::string name; - LLAgentUI::buildFullname(name); - msg->addStringFast(_PREHASH_FromAgentName, name); - msg->addStringFast(_PREHASH_Message, ""); - msg->addU32Fast(_PREHASH_ParentEstateID, 0); - msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null); - msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent()); - LLInventoryObserver* opener = NULL; - - std::string from_string; // Used in the pop-up. - std::string chatHistory_string; // Used in chat history. - if (mFromObject == TRUE) - { - if (mFromGroup) - { - std::string group_name; - if (gCacheName->getGroupName(mFromID, group_name)) - { - from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+"'" - + mFromName + LLTrans::getString("'") +" " + LLTrans::getString("InvOfferOwnedByGroup") - + " "+ "'" + group_name + "'"; - - chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedByGroup") - + " " + group_name + "'"; - } - else - { - from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+"'" - + mFromName +"'"+ " " + LLTrans::getString("InvOfferOwnedByUnknownGroup"); - chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedByUnknownGroup"); - } - } - else - { - std::string full_name; - if (gCacheName->getFullName(mFromID, full_name)) - { - from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+ LLTrans::getString("'") + mFromName - + LLTrans::getString("'")+" " + LLTrans::getString("InvOfferOwnedBy") + full_name; - chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedBy") + " " + full_name; - } - else - { - from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+LLTrans::getString("'") - + mFromName + LLTrans::getString("'")+" " + LLTrans::getString("InvOfferOwnedByUnknownUser"); - chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedByUnknownUser"); - } - } - } - else - { - from_string = chatHistory_string = mFromName; - } - - bool busy=FALSE; - - switch(button) - { - case IOR_ACCEPT: - // ACCEPT. The math for the dialog works, because the accept - // for inventory_offered, task_inventory_offer or - // group_notice_inventory is 1 greater than the offer integer value. - // Generates IM_INVENTORY_ACCEPTED, IM_TASK_INVENTORY_ACCEPTED, - // or IM_GROUP_NOTICE_INVENTORY_ACCEPTED - msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 1)); - msg->addBinaryDataFast(_PREHASH_BinaryBucket, &(mFolderID.mData), - sizeof(mFolderID.mData)); - // send the message - msg->sendReliable(mHost); - - //don't spam them if they are getting flooded - if (check_offer_throttle(mFromName, true)) - { - log_message = chatHistory_string + " " + LLTrans::getString("InvOfferGaveYou") + " " + mDesc + LLTrans::getString("."); - LLSD args; - args["MESSAGE"] = log_message; - LLNotificationsUtil::add("SystemMessage", args); - } - - // we will want to open this item when it comes back. - LL_DEBUGS("Messaging") << "Initializing an opener for tid: " << mTransactionID - << LL_ENDL; - switch (mIM) - { - case IM_TASK_INVENTORY_OFFERED: - case IM_GROUP_NOTICE: - case IM_GROUP_NOTICE_REQUESTED: - { - // This is an offer from a task or group. - // We don't use a new instance of an opener - // We instead use the singular observer gOpenTaskOffer - // Since it already exists, we don't need to actually do anything - } - break; - default: - LL_WARNS("Messaging") << "inventory_offer_callback: unknown offer type" << LL_ENDL; - break; - } // end switch (mIM) - break; - - case IOR_BUSY: - //Busy falls through to decline. Says to make busy message. - busy=TRUE; - case IOR_MUTE: - // MUTE falls through to decline - case IOR_DECLINE: - // DECLINE. The math for the dialog works, because the decline - // for inventory_offered, task_inventory_offer or - // group_notice_inventory is 2 greater than the offer integer value. - // Generates IM_INVENTORY_DECLINED, IM_TASK_INVENTORY_DECLINED, - // or IM_GROUP_NOTICE_INVENTORY_DECLINED - default: - // close button probably (or any of the fall-throughs from above) - msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 2)); - msg->addBinaryDataFast(_PREHASH_BinaryBucket, EMPTY_BINARY_BUCKET, EMPTY_BINARY_BUCKET_SIZE); - // send the message - msg->sendReliable(mHost); - - if (gSavedSettings.getBOOL("LogInventoryDecline")) - { - LLStringUtil::format_map_t log_message_args; - log_message_args["DESC"] = mDesc; - log_message_args["NAME"] = mFromName; - log_message = LLTrans::getString("InvOfferDecline", log_message_args); - - LLSD args; - args["MESSAGE"] = log_message; - LLNotificationsUtil::add("SystemMessage", args); - } - - if (busy && (!mFromGroup && !mFromObject)) - { - busy_message(msg,mFromID); - } - break; - } - - if(opener) - { - gInventory.addObserver(opener); - } - - if(!mPersist) - { - delete this; - } - return false; -} - -class LLPostponedOfferNotification: public LLPostponedNotification -{ -protected: - /* virtual */ - void modifyNotificationParams() - { - LLSD substitutions = mParams.substitutions; - substitutions["NAME"] = mName; - mParams.substitutions = substitutions; - } -}; - -void LLOfferInfo::initRespondFunctionMap() -{ - if(mRespondFunctions.empty()) - { - mRespondFunctions["ObjectGiveItem"] = boost::bind(&LLOfferInfo::inventory_task_offer_callback, this, _1, _2); - mRespondFunctions["UserGiveItem"] = boost::bind(&LLOfferInfo::inventory_offer_callback, this, _1, _2); - } -} - -void inventory_offer_handler(LLOfferInfo* info) -{ - //Until throttling is implmented, busy mode should reject inventory instead of silently - //accepting it. SEE SL-39554 - if (gAgent.getBusy()) - { - info->forceResponse(IOR_BUSY); - return; - } - - //If muted, don't even go through the messaging stuff. Just curtail the offer here. - if (LLMuteList::getInstance()->isMuted(info->mFromID, info->mFromName)) - { - info->forceResponse(IOR_MUTE); - return; - } - - // Avoid the Accept/Discard dialog if the user so desires. JC - if (gSavedSettings.getBOOL("AutoAcceptNewInventory") - && (info->mType == LLAssetType::AT_NOTECARD - || info->mType == LLAssetType::AT_LANDMARK - || info->mType == LLAssetType::AT_TEXTURE)) - { - // For certain types, just accept the items into the inventory, - // and possibly open them on receipt depending upon "ShowNewInventory". - info->forceResponse(IOR_ACCEPT); - return; - } - - // Strip any SLURL from the message display. (DEV-2754) - std::string msg = info->mDesc; - int indx = msg.find(" ( http://slurl.com/secondlife/"); - if(indx == std::string::npos) - { - // try to find new slurl host - indx = msg.find(" ( http://maps.secondlife.com/secondlife/"); - } - if(indx >= 0) - { - LLStringUtil::truncate(msg, indx); - } - - LLSD args; - args["[OBJECTNAME]"] = msg; - - LLSD payload; - - // must protect against a NULL return from lookupHumanReadable() - std::string typestr = ll_safe_string(LLAssetType::lookupHumanReadable(info->mType)); - if (!typestr.empty()) - { - // human readable matches string name from strings.xml - // lets get asset type localized name - args["OBJECTTYPE"] = LLTrans::getString(typestr); - } - else - { - LL_WARNS("Messaging") << "LLAssetType::lookupHumanReadable() returned NULL - probably bad asset type: " << info->mType << LL_ENDL; - args["OBJECTTYPE"] = ""; - - // This seems safest, rather than propagating bogosity - LL_WARNS("Messaging") << "Forcing an inventory-decline for probably-bad asset type." << LL_ENDL; - info->forceResponse(IOR_DECLINE); - return; - } - - // If mObjectID is null then generate the object_id based on msg to prevent - // multiple creation of chiclets for same object. - LLUUID object_id = info->mObjectID; - if (object_id.isNull()) - object_id.generate(msg); - - payload["from_id"] = info->mFromID; - // Needed by LLScriptFloaterManager to bind original notification with - // faked for toast one. - payload["object_id"] = object_id; - // Flag indicating that this notification is faked for toast. - payload["give_inventory_notification"] = FALSE; - args["OBJECTFROMNAME"] = info->mFromName; - args["NAME"] = info->mFromName; - if (info->mFromGroup) - { - args["NAME_SLURL"] = LLSLURL("group", info->mFromID, "about").getSLURLString(); - } - else - { - args["NAME_SLURL"] = LLSLURL("agent", info->mFromID, "about").getSLURLString(); - } - std::string verb = "select?name=" + LLURI::escape(msg); - args["ITEM_SLURL"] = LLSLURL("inventory", info->mObjectID, verb.c_str()).getSLURLString(); - - LLNotification::Params p("ObjectGiveItem"); - - // Object -> Agent Inventory Offer - if (info->mFromObject) - { - // Inventory Slurls don't currently work for non agent transfers, so only display the object name. - args["ITEM_SLURL"] = msg; - // Note: sets inventory_task_offer_callback as the callback - p.substitutions(args).payload(payload).functor.responder(LLNotificationResponderPtr(info)); - info->mPersist = true; - p.name = "ObjectGiveItem"; - // Pop up inv offer chiclet and let the user accept (keep), or reject (and silently delete) the inventory. - LLPostponedNotification::add(p, info->mFromID, info->mFromGroup == TRUE); - } - else // Agent -> Agent Inventory Offer - { - p.responder = info; - // Note: sets inventory_offer_callback as the callback - // *TODO fix memory leak - // inventory_offer_callback() is not invoked if user received notification and - // closes viewer(without responding the notification) - p.substitutions(args).payload(payload).functor.responder(LLNotificationResponderPtr(info)); - info->mPersist = true; - p.name = "UserGiveItem"; - - // Prefetch the item into your local inventory. - LLInventoryFetchItemsObserver* fetch_item = new LLInventoryFetchItemsObserver(info->mObjectID); - fetch_item->startFetch(); - if(fetch_item->isFinished()) - { - fetch_item->done(); - } - else - { - gInventory.addObserver(fetch_item); - } - - // In viewer 2 we're now auto receiving inventory offers and messaging as such (not sending reject messages). - info->send_auto_receive_response(); - - // Inform user that there is a script floater via toast system - { - payload["give_inventory_notification"] = TRUE; - p.payload = payload; - LLPostponedNotification::add(p, info->mFromID, false); - } - } - - LLFirstUse::newInventory(); -} - -bool lure_callback(const LLSD& notification, const LLSD& response) -{ - S32 option = 0; - if (response.isInteger()) - { - option = response.asInteger(); - } - else - { - option = LLNotificationsUtil::getSelectedOption(notification, response); - } - - LLUUID from_id = notification["payload"]["from_id"].asUUID(); - LLUUID lure_id = notification["payload"]["lure_id"].asUUID(); - BOOL godlike = notification["payload"]["godlike"].asBoolean(); - - switch(option) - { - case 0: - { - // accept - gAgent.teleportViaLure(lure_id, godlike); - } - break; - case 1: - default: - // decline - send_simple_im(from_id, - LLStringUtil::null, - IM_LURE_DECLINED, - lure_id); - break; - } - return false; -} -static LLNotificationFunctorRegistration lure_callback_reg("TeleportOffered", lure_callback); - -bool goto_url_callback(const LLSD& notification, const LLSD& response) -{ - std::string url = notification["payload"]["url"].asString(); - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if(1 == option) - { - LLWeb::loadURL(url); - } - return false; -} -static LLNotificationFunctorRegistration goto_url_callback_reg("GotoURL", goto_url_callback); - -bool inspect_remote_object_callback(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (0 == option) - { - LLFloaterReg::showInstance("inspect_remote_object", notification["payload"]); - } - return false; -} -static LLNotificationFunctorRegistration inspect_remote_object_callback_reg("ServerObjectMessage", inspect_remote_object_callback); - -class LLPostponedServerObjectNotification: public LLPostponedNotification -{ -protected: - /* virtual */ - void modifyNotificationParams() - { - LLSD payload = mParams.payload; - mParams.payload = payload; - } -}; - -static bool parse_lure_bucket(const std::string& bucket, - U64& region_handle, - LLVector3& pos, - LLVector3& look_at, - U8& region_access) -{ - // tokenize the bucket - typedef boost::tokenizer > tokenizer; - boost::char_separator sep("|", "", boost::keep_empty_tokens); - tokenizer tokens(bucket, sep); - tokenizer::iterator iter = tokens.begin(); - - S32 gx,gy,rx,ry,rz,lx,ly,lz; - try - { - gx = boost::lexical_cast((*(iter)).c_str()); - gy = boost::lexical_cast((*(++iter)).c_str()); - rx = boost::lexical_cast((*(++iter)).c_str()); - ry = boost::lexical_cast((*(++iter)).c_str()); - rz = boost::lexical_cast((*(++iter)).c_str()); - lx = boost::lexical_cast((*(++iter)).c_str()); - ly = boost::lexical_cast((*(++iter)).c_str()); - lz = boost::lexical_cast((*(++iter)).c_str()); - } - catch( boost::bad_lexical_cast& ) - { - LL_WARNS("parse_lure_bucket") - << "Couldn't parse lure bucket." - << LL_ENDL; - return false; - } - // Grab region access - region_access = SIM_ACCESS_MIN; - if (++iter != tokens.end()) - { - std::string access_str((*iter).c_str()); - LLStringUtil::trim(access_str); - if ( access_str == "A" ) - { - region_access = SIM_ACCESS_ADULT; - } - else if ( access_str == "M" ) - { - region_access = SIM_ACCESS_MATURE; - } - else if ( access_str == "PG" ) - { - region_access = SIM_ACCESS_PG; - } - } - - pos.setVec((F32)rx, (F32)ry, (F32)rz); - look_at.setVec((F32)lx, (F32)ly, (F32)lz); - - region_handle = to_region_handle(gx, gy); - return true; -} - -// Strip out "Resident" for display, but only if the message came from a user -// (rather than a script) -static std::string clean_name_from_im(const std::string& name, EInstantMessage type) -{ - switch(type) - { - case IM_NOTHING_SPECIAL: - case IM_MESSAGEBOX: - case IM_GROUP_INVITATION: - case IM_INVENTORY_OFFERED: - case IM_INVENTORY_ACCEPTED: - case IM_INVENTORY_DECLINED: - case IM_GROUP_VOTE: - case IM_GROUP_MESSAGE_DEPRECATED: - //IM_TASK_INVENTORY_OFFERED - //IM_TASK_INVENTORY_ACCEPTED - //IM_TASK_INVENTORY_DECLINED - case IM_NEW_USER_DEFAULT: - case IM_SESSION_INVITE: - case IM_SESSION_P2P_INVITE: - case IM_SESSION_GROUP_START: - case IM_SESSION_CONFERENCE_START: - case IM_SESSION_SEND: - case IM_SESSION_LEAVE: - //IM_FROM_TASK - case IM_BUSY_AUTO_RESPONSE: - case IM_CONSOLE_AND_CHAT_HISTORY: - case IM_LURE_USER: - case IM_LURE_ACCEPTED: - case IM_LURE_DECLINED: - case IM_GODLIKE_LURE_USER: - case IM_YET_TO_BE_USED: - case IM_GROUP_ELECTION_DEPRECATED: - //IM_GOTO_URL - //IM_FROM_TASK_AS_ALERT - case IM_GROUP_NOTICE: - case IM_GROUP_NOTICE_INVENTORY_ACCEPTED: - case IM_GROUP_NOTICE_INVENTORY_DECLINED: - case IM_GROUP_INVITATION_ACCEPT: - case IM_GROUP_INVITATION_DECLINE: - case IM_GROUP_NOTICE_REQUESTED: - case IM_FRIENDSHIP_OFFERED: - case IM_FRIENDSHIP_ACCEPTED: - case IM_FRIENDSHIP_DECLINED_DEPRECATED: - //IM_TYPING_START - //IM_TYPING_STOP - return LLCacheName::cleanFullName(name); - default: - return name; - } -} - -static std::string clean_name_from_task_im(const std::string& msg, - BOOL from_group) -{ - boost::smatch match; - static const boost::regex returned_exp( - "(.*been returned to your inventory lost and found folder by )(.+)( (from|near).*)"); - if (boost::regex_match(msg, match, returned_exp)) - { - // match objects are 1-based for groups - std::string final = match[1].str(); - std::string name = match[2].str(); - // Don't try to clean up group names - if (!from_group) - { - if (LLAvatarNameCache::useDisplayNames()) - { - // ...just convert to username - final += LLCacheName::buildUsername(name); - } - else - { - // ...strip out legacy "Resident" name - final += LLCacheName::cleanFullName(name); - } - } - final += match[3].str(); - return final; - } - return msg; -} - -void notification_display_name_callback(const LLUUID& id, - const LLAvatarName& av_name, - const std::string& name, - LLSD& substitutions, - const LLSD& payload) -{ - substitutions["NAME"] = av_name.mDisplayName; - LLNotificationsUtil::add(name, substitutions, payload); -} - -class LLPostponedIMSystemTipNotification: public LLPostponedNotification -{ -protected: - /* virtual */ - void modifyNotificationParams() - { - LLSD payload = mParams.payload; - payload["SESSION_NAME"] = mName; - mParams.payload = payload; - } - -}; - -// Callback for name resolution of a god/estate message -void god_message_name_cb(const LLAvatarName& av_name, LLChat chat, std::string message) -{ - LLSD args; - args["NAME"] = av_name.getCompleteName(); - args["MESSAGE"] = message; - LLNotificationsUtil::add("GodMessage", args); - - // Treat like a system message and put in chat history. - chat.mText = av_name.getCompleteName() + ": " + message; - - LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat", LLSD()); - if(nearby_chat) - { - nearby_chat->addMessage(chat); - } - -} - -void process_improved_im(LLMessageSystem *msg, void **user_data) -{ - if (gNoRender) - { - return; - } - LLUUID from_id; - BOOL from_group; - LLUUID to_id; - U8 offline; - U8 d = 0; - LLUUID session_id; - U32 timestamp; - std::string name; - std::string message; - U32 parent_estate_id = 0; - LLUUID region_id; - LLVector3 position; - U8 binary_bucket[MTUBYTES]; - S32 binary_bucket_size; - LLChat chat; - std::string buffer; - - // *TODO: Translate - need to fix the full name to first/last (maybe) - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, from_id); - msg->getBOOLFast(_PREHASH_MessageBlock, _PREHASH_FromGroup, from_group); - msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ToAgentID, to_id); - msg->getU8Fast( _PREHASH_MessageBlock, _PREHASH_Offline, offline); - msg->getU8Fast( _PREHASH_MessageBlock, _PREHASH_Dialog, d); - msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ID, session_id); - msg->getU32Fast( _PREHASH_MessageBlock, _PREHASH_Timestamp, timestamp); - //msg->getData("MessageBlock", "Count", &count); - msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_FromAgentName, name); - msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_Message, message); - msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_ParentEstateID, parent_estate_id); - msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_RegionID, region_id); - msg->getVector3Fast(_PREHASH_MessageBlock, _PREHASH_Position, position); - msg->getBinaryDataFast( _PREHASH_MessageBlock, _PREHASH_BinaryBucket, binary_bucket, 0, 0, MTUBYTES); - binary_bucket_size = msg->getSizeFast(_PREHASH_MessageBlock, _PREHASH_BinaryBucket); - EInstantMessage dialog = (EInstantMessage)d; - - // make sure that we don't have an empty or all-whitespace name - LLStringUtil::trim(name); - if (name.empty()) - { - name = LLTrans::getString("Unnamed"); - } - // IDEVO convert new-style "Resident" names for display - name = clean_name_from_im(name, dialog); - - BOOL is_busy = gAgent.getBusy(); - BOOL is_muted = LLMuteList::getInstance()->isMuted(from_id, name, LLMute::flagTextChat); - BOOL is_linden = LLMuteList::getInstance()->isLinden(name); - BOOL is_owned_by_me = FALSE; - BOOL is_friend = (LLAvatarTracker::instance().getBuddyInfo(from_id) == NULL) ? false : true; - BOOL accept_im_from_only_friend = gSavedSettings.getBOOL("VoiceCallsFriendsOnly"); - - chat.mMuted = is_muted && !is_linden; - chat.mFromID = from_id; - chat.mFromName = name; - chat.mSourceType = (from_id.isNull() || (name == std::string(SYSTEM_FROM))) ? CHAT_SOURCE_SYSTEM : CHAT_SOURCE_AGENT; - - LLViewerObject *source = gObjectList.findObject(session_id); //Session ID is probably the wrong thing. - if (source) - { - is_owned_by_me = source->permYouOwner(); - } - - std::string separator_string(": "); - - LLSD args; - LLSD payload; - LLNotification::Params params; - - switch(dialog) - { - case IM_CONSOLE_AND_CHAT_HISTORY: - args["MESSAGE"] = message; - payload["from_id"] = from_id; - - params.name = "IMSystemMessageTip"; - params.substitutions = args; - params.payload = payload; - LLPostponedNotification::add(params, from_id, false); - break; - - case IM_NOTHING_SPECIAL: - // Don't show dialog, just do IM - if (!gAgent.isGodlike() - && gAgent.getRegion()->isPrelude() - && to_id.isNull() ) - { - // do nothing -- don't distract newbies in - // Prelude with global IMs - } - else if (offline == IM_ONLINE && !is_linden && is_busy && name != SYSTEM_FROM) - { - // return a standard "busy" message, but only do it to online IM - // (i.e. not other auto responses and not store-and-forward IM) - if (!gIMMgr->hasSession(session_id)) - { - // if there is not a panel for this conversation (i.e. it is a new IM conversation - // initiated by the other party) then... - std::string my_name; - LLAgentUI::buildFullname(my_name); - std::string response = gSavedPerAccountSettings.getString("BusyModeResponse"); - pack_instant_message( - gMessageSystem, - gAgent.getID(), - FALSE, - gAgent.getSessionID(), - from_id, - my_name, - response, - IM_ONLINE, - IM_BUSY_AUTO_RESPONSE, - session_id); - gAgent.sendReliableMessage(); - } - - // now store incoming IM in chat history - - buffer = message; - - LL_INFOS("Messaging") << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << LL_ENDL; - - // add to IM panel, but do not bother the user - gIMMgr->addMessage( - session_id, - from_id, - name, - buffer, - LLStringUtil::null, - dialog, - parent_estate_id, - region_id, - position, - true); - } - else if (from_id.isNull()) - { - LLSD args; - args["MESSAGE"] = message; - LLNotificationsUtil::add("SystemMessage", args); - } - else if (to_id.isNull()) - { - // Message to everyone from GOD, look up the fullname since - // server always slams name to legacy names - LLAvatarNameCache::get(from_id, boost::bind(god_message_name_cb, _2, chat, message)); - } - else - { - // standard message, not from system - std::string saved; - if(offline == IM_OFFLINE) - { - LLStringUtil::format_map_t args; - args["[LONG_TIMESTAMP]"] = formatted_time(timestamp); - saved = LLTrans::getString("Saved_message", args); - } - buffer = saved + message; - - LL_INFOS("Messaging") << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << LL_ENDL; - - bool mute_im = is_muted; - if(accept_im_from_only_friend&&!is_friend) - { - mute_im = true; - } - if (!mute_im || is_linden) - { - gIMMgr->addMessage( - session_id, - from_id, - name, - buffer, - LLStringUtil::null, - dialog, - parent_estate_id, - region_id, - position, - true); - } - else - { - /* - EXT-5099 - currently there is no way to store in history only... - using LLNotificationsUtil::add will add message to Nearby Chat - - // muted user, so don't start an IM session, just record line in chat - // history. Pretend the chat is from a local agent, - // so it will go into the history but not be shown on screen. - - LLSD args; - args["MESSAGE"] = buffer; - LLNotificationsUtil::add("SystemMessageTip", args); - */ - } - } - break; - - case IM_TYPING_START: - { - LLPointer im_info = new LLIMInfo(gMessageSystem); - gIMMgr->processIMTypingStart(im_info); - } - break; - - case IM_TYPING_STOP: - { - LLPointer im_info = new LLIMInfo(gMessageSystem); - gIMMgr->processIMTypingStop(im_info); - } - break; - - case IM_MESSAGEBOX: - { - // This is a block, modeless dialog. - //*TODO: Translate - args["MESSAGE"] = message; - LLNotificationsUtil::add("SystemMessageTip", args); - } - break; - case IM_GROUP_NOTICE: - case IM_GROUP_NOTICE_REQUESTED: - { - LL_INFOS("Messaging") << "Received IM_GROUP_NOTICE message." << LL_ENDL; - // Read the binary bucket for more information. - struct notice_bucket_header_t - { - U8 has_inventory; - U8 asset_type; - LLUUID group_id; - }; - struct notice_bucket_full_t - { - struct notice_bucket_header_t header; - U8 item_name[DB_INV_ITEM_NAME_BUF_SIZE]; - }* notice_bin_bucket; - - // Make sure the binary bucket is big enough to hold the header - // and a null terminated item name. - if ( (binary_bucket_size < (S32)((sizeof(notice_bucket_header_t) + sizeof(U8)))) - || (binary_bucket[binary_bucket_size - 1] != '\0') ) - { - LL_WARNS("Messaging") << "Malformed group notice binary bucket" << LL_ENDL; - break; - } - - notice_bin_bucket = (struct notice_bucket_full_t*) &binary_bucket[0]; - U8 has_inventory = notice_bin_bucket->header.has_inventory; - U8 asset_type = notice_bin_bucket->header.asset_type; - LLUUID group_id = notice_bin_bucket->header.group_id; - std::string item_name = ll_safe_string((const char*) notice_bin_bucket->item_name); - - // If there is inventory, give the user the inventory offer. - LLOfferInfo* info = NULL; - - if (has_inventory) - { - info = new LLOfferInfo(); - - info->mIM = IM_GROUP_NOTICE; - info->mFromID = from_id; - info->mFromGroup = from_group; - info->mTransactionID = session_id; - info->mType = (LLAssetType::EType) asset_type; - info->mFolderID = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(info->mType)); - std::string from_name; - - from_name += "A group member named "; - from_name += name; - - info->mFromName = from_name; - info->mDesc = item_name; - info->mHost = msg->getSender(); - } - - std::string str(message); - - // Tokenize the string. - // TODO: Support escaped tokens ("||" -> "|") - typedef boost::tokenizer > tokenizer; - boost::char_separator sep("|","",boost::keep_empty_tokens); - tokenizer tokens(str, sep); - tokenizer::iterator iter = tokens.begin(); - - std::string subj(*iter++); - std::string mes(*iter++); - - // Send the notification down the new path. - // For requested notices, we don't want to send the popups. - if (dialog != IM_GROUP_NOTICE_REQUESTED) - { - payload["subject"] = subj; - payload["message"] = mes; - payload["sender_name"] = name; - payload["group_id"] = group_id; - payload["inventory_name"] = item_name; - payload["inventory_offer"] = info ? info->asLLSD() : LLSD(); - - LLSD args; - args["SUBJECT"] = subj; - args["MESSAGE"] = mes; - LLNotifications::instance().add(LLNotification::Params("GroupNotice").substitutions(args).payload(payload).time_stamp(timestamp)); - } - - // Also send down the old path for now. - if (IM_GROUP_NOTICE_REQUESTED == dialog) - { - - LLPanelGroup::showNotice(subj,mes,group_id,has_inventory,item_name,info); - } - else - { - delete info; - } - } - break; - case IM_GROUP_INVITATION: - { - //if (!is_linden && (is_busy || is_muted)) - if ((is_busy || is_muted)) - { - LLMessageSystem *msg = gMessageSystem; - busy_message(msg,from_id); - } - else - { - LL_INFOS("Messaging") << "Received IM_GROUP_INVITATION message." << LL_ENDL; - // Read the binary bucket for more information. - struct invite_bucket_t - { - S32 membership_fee; - LLUUID role_id; - }* invite_bucket; - - // Make sure the binary bucket is the correct size. - if (binary_bucket_size != sizeof(invite_bucket_t)) - { - LL_WARNS("Messaging") << "Malformed group invite binary bucket" << LL_ENDL; - break; - } - - invite_bucket = (struct invite_bucket_t*) &binary_bucket[0]; - S32 membership_fee = ntohl(invite_bucket->membership_fee); - - LLSD payload; - payload["transaction_id"] = session_id; - payload["group_id"] = from_id; - payload["name"] = name; - payload["message"] = message; - payload["fee"] = membership_fee; - - LLSD args; - args["MESSAGE"] = message; - // we shouldn't pass callback functor since it is registered in LLFunctorRegistration - LLNotificationsUtil::add("JoinGroup", args, payload); - } - } - break; - - case IM_INVENTORY_OFFERED: - case IM_TASK_INVENTORY_OFFERED: - // Someone has offered us some inventory. - { - LLOfferInfo* info = new LLOfferInfo; - if (IM_INVENTORY_OFFERED == dialog) - { - struct offer_agent_bucket_t - { - S8 asset_type; - LLUUID object_id; - }* bucketp; - - if (sizeof(offer_agent_bucket_t) != binary_bucket_size) - { - LL_WARNS("Messaging") << "Malformed inventory offer from agent" << LL_ENDL; - delete info; - break; - } - bucketp = (struct offer_agent_bucket_t*) &binary_bucket[0]; - info->mType = (LLAssetType::EType) bucketp->asset_type; - info->mObjectID = bucketp->object_id; - } - else - { - if (sizeof(S8) != binary_bucket_size) - { - LL_WARNS("Messaging") << "Malformed inventory offer from object" << LL_ENDL; - delete info; - break; - } - info->mType = (LLAssetType::EType) binary_bucket[0]; - info->mObjectID = LLUUID::null; - } - - info->mIM = dialog; - info->mFromID = from_id; - info->mFromGroup = from_group; - info->mTransactionID = session_id; - info->mFolderID = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(info->mType)); - - if (dialog == IM_TASK_INVENTORY_OFFERED) - { - info->mFromObject = TRUE; - } - else - { - info->mFromObject = FALSE; - } - info->mFromName = name; - info->mDesc = message; - info->mHost = msg->getSender(); - //if (((is_busy && !is_owned_by_me) || is_muted)) - if (is_muted) - { - // Prefetch the offered item so that it can be discarded by the appropriate observer. (EXT-4331) - LLInventoryFetchItemsObserver* fetch_item = new LLInventoryFetchItemsObserver(info->mObjectID); - fetch_item->startFetch(); - delete fetch_item; - - // Same as closing window - info->forceResponse(IOR_DECLINE); - } - else - { - inventory_offer_handler(info); - } - } - break; - - case IM_INVENTORY_ACCEPTED: - { - args["NAME"] = LLSLURL("agent", from_id, "completename").getSLURLString();; - LLSD payload; - payload["from_id"] = from_id; - LLNotificationsUtil::add("InventoryAccepted", args, payload); - break; - } - case IM_INVENTORY_DECLINED: - { - args["NAME"] = LLSLURL("agent", from_id, "completename").getSLURLString();; - LLSD payload; - payload["from_id"] = from_id; - LLNotificationsUtil::add("InventoryDeclined", args, payload); - break; - } - // TODO: _DEPRECATED suffix as part of vote removal - DEV-24856 - case IM_GROUP_VOTE: - { - LL_WARNS("Messaging") << "Received IM: IM_GROUP_VOTE_DEPRECATED" << LL_ENDL; - } - break; - - case IM_GROUP_ELECTION_DEPRECATED: - { - LL_WARNS("Messaging") << "Received IM: IM_GROUP_ELECTION_DEPRECATED" << LL_ENDL; - } - break; - - case IM_SESSION_SEND: - { - if (!is_linden && is_busy) - { - return; - } - - // Only show messages if we have a session open (which - // should happen after you get an "invitation" - if ( !gIMMgr->hasSession(session_id) ) - { - return; - } - - // standard message, not from system - std::string saved; - if(offline == IM_OFFLINE) - { - saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); - } - buffer = saved + message; - BOOL is_this_agent = FALSE; - if(from_id == gAgentID) - { - is_this_agent = TRUE; - } - gIMMgr->addMessage( - session_id, - from_id, - name, - buffer, - ll_safe_string((char*)binary_bucket), - IM_SESSION_INVITE, - parent_estate_id, - region_id, - position, - true); - } - break; - - case IM_FROM_TASK: - { - if (is_busy && !is_owned_by_me) - { - return; - } - - // Build a link to open the object IM info window. - std::string location = ll_safe_string((char*)binary_bucket, binary_bucket_size-1); - - if (session_id.notNull()) - { - chat.mFromID = session_id; - } - else - { - // This message originated on a region without the updated code for task id and slurl information. - // We just need a unique ID for this object that isn't the owner ID. - // If it is the owner ID it will overwrite the style that contains the link to that owner's profile. - // This isn't ideal - it will make 1 style for all objects owned by the the same person/group. - // This works because the only thing we can really do in this case is show the owner name and link to their profile. - chat.mFromID = from_id ^ gAgent.getSessionID(); - } - - chat.mSourceType = CHAT_SOURCE_OBJECT; - - if(SYSTEM_FROM == name) - { - // System's UUID is NULL (fixes EXT-4766) - chat.mFromID = LLUUID::null; - chat.mSourceType = CHAT_SOURCE_SYSTEM; - } - - // IDEVO Some messages have embedded resident names - message = clean_name_from_task_im(message, from_group); - - LLSD query_string; - query_string["owner"] = from_id; - query_string["slurl"] = location; - query_string["name"] = name; - if (from_group) - { - query_string["groupowned"] = "true"; - } - - chat.mURL = LLSLURL("objectim", session_id, "").getSLURLString(); - chat.mText = message; - - // Note: lie to Nearby Chat, pretending that this is NOT an IM, because - // IMs from obejcts don't open IM sessions. - LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat", LLSD()); - if(SYSTEM_FROM != name && nearby_chat) - { - chat.mOwnerID = from_id; - LLSD args; - args["slurl"] = location; - args["type"] = LLNotificationsUI::NT_NEARBYCHAT; - - // Look for IRC-style emotes here so object name formatting is correct - std::string prefix = message.substr(0, 4); - if (prefix == "/me " || prefix == "/me'") - { - chat.mChatStyle = CHAT_STYLE_IRC; - } - - LLNotificationsUI::LLNotificationManager::instance().onChat(chat, args); - } - - - //Object IMs send with from name: 'Second Life' need to be displayed also in notification toasts (EXT-1590) - if (SYSTEM_FROM != name) break; - - LLSD substitutions; - substitutions["NAME"] = name; - substitutions["MSG"] = message; - - LLSD payload; - payload["object_id"] = session_id; - payload["owner_id"] = from_id; - payload["from_id"] = from_id; - payload["slurl"] = location; - payload["name"] = name; - std::string session_name; - if (from_group) - { - payload["group_owned"] = "true"; - } - - LLNotification::Params params("ServerObjectMessage"); - params.substitutions = substitutions; - params.payload = payload; - - LLPostponedNotification::add(params, from_id, from_group); - } - break; - case IM_FROM_TASK_AS_ALERT: - if (is_busy && !is_owned_by_me) - { - return; - } - { - // Construct a viewer alert for this message. - args["NAME"] = name; - args["MESSAGE"] = message; - LLNotificationsUtil::add("ObjectMessage", args); - } - break; - case IM_BUSY_AUTO_RESPONSE: - if (is_muted) - { - LL_DEBUGS("Messaging") << "Ignoring busy response from " << from_id << LL_ENDL; - return; - } - else - { - // TODO: after LLTrans hits release, get "busy response" into translatable file - buffer = llformat("%s (%s): %s", name.c_str(), "busy response", message.c_str()); - gIMMgr->addMessage(session_id, from_id, name, buffer); - } - break; - - case IM_LURE_USER: - { - if (is_muted) - { - return; - } - else if (is_busy) - { - busy_message(msg,from_id); - } - else - { - LLVector3 pos, look_at; - U64 region_handle; - U8 region_access = SIM_ACCESS_MIN; - std::string region_info = ll_safe_string((char*)binary_bucket, binary_bucket_size); - std::string region_access_str = LLStringUtil::null; - std::string region_access_icn = LLStringUtil::null; - - if (parse_lure_bucket(region_info, region_handle, pos, look_at, region_access)) - { - region_access_str = LLViewerRegion::accessToString(region_access); - region_access_icn = LLViewerRegion::getAccessIcon(region_access); - } - - LLSD args; - // *TODO: Translate -> [FIRST] [LAST] (maybe) - args["NAME_SLURL"] = LLSLURL("agent", from_id, "about").getSLURLString(); - args["MESSAGE"] = message; - args["MATURITY_STR"] = region_access_str; - args["MATURITY_ICON"] = region_access_icn; - LLSD payload; - payload["from_id"] = from_id; - payload["lure_id"] = session_id; - payload["godlike"] = FALSE; - - LLNotification::Params params("TeleportOffered"); - params.substitutions = args; - params.payload = payload; - LLPostponedNotification::add( params, from_id, false); - } - } - break; - - case IM_GODLIKE_LURE_USER: - { - LLSD payload; - payload["from_id"] = from_id; - payload["lure_id"] = session_id; - payload["godlike"] = TRUE; - // do not show a message box, because you're about to be - // teleported. - LLNotifications::instance().forceResponse(LLNotification::Params("TeleportOffered").payload(payload), 0); - } - break; - - case IM_GOTO_URL: - { - LLSD args; - // n.b. this is for URLs sent by the system, not for - // URLs sent by scripts (i.e. llLoadURL) - if (binary_bucket_size <= 0) - { - LL_WARNS("Messaging") << "bad binary_bucket_size: " - << binary_bucket_size - << " - aborting function." << LL_ENDL; - return; - } - - std::string url; - - url.assign((char*)binary_bucket, binary_bucket_size-1); - args["MESSAGE"] = message; - args["URL"] = url; - LLSD payload; - payload["url"] = url; - LLNotificationsUtil::add("GotoURL", args, payload ); - } - break; - - case IM_FRIENDSHIP_OFFERED: - { - LLSD payload; - payload["from_id"] = from_id; - payload["session_id"] = session_id;; - payload["online"] = (offline == IM_ONLINE); - payload["sender"] = msg->getSender().getIPandPort(); - - if (is_busy) - { - busy_message(msg, from_id); - LLNotifications::instance().forceResponse(LLNotification::Params("OfferFriendship").payload(payload), 1); - } - else if (is_muted) - { - LLNotifications::instance().forceResponse(LLNotification::Params("OfferFriendship").payload(payload), 1); - } - else - { - args["NAME_SLURL"] = LLSLURL("agent", from_id, "about").getSLURLString(); - if(message.empty()) - { - //support for frienship offers from clients before July 2008 - LLNotificationsUtil::add("OfferFriendshipNoMessage", args, payload); - } - else - { - args["[MESSAGE]"] = message; - LLNotification::Params params("OfferFriendship"); - params.substitutions = args; - params.payload = payload; - LLPostponedNotification::add( params, from_id, false); - } - } - } - break; - - case IM_FRIENDSHIP_ACCEPTED: - { - // In the case of an offline IM, the formFriendship() may be extraneous - // as the database should already include the relationship. But it - // doesn't hurt for dupes. - LLAvatarTracker::formFriendship(from_id); - - std::vector strings; - strings.push_back(from_id.asString()); - send_generic_message("requestonlinenotification", strings); - - args["NAME"] = name; - LLSD payload; - payload["from_id"] = from_id; - LLAvatarNameCache::get(from_id, boost::bind(¬ification_display_name_callback, - _1, - _2, - "FriendshipAccepted", - args, - payload)); - } - break; - - case IM_FRIENDSHIP_DECLINED_DEPRECATED: - default: - LL_WARNS("Messaging") << "Instant message calling for unknown dialog " - << (S32)dialog << LL_ENDL; - break; - } - - LLWindow* viewer_window = gViewerWindow->getWindow(); - if (viewer_window && viewer_window->getMinimized()) - { - viewer_window->flashIcon(5.f); - } -} - -void busy_message (LLMessageSystem* msg, LLUUID from_id) -{ - if (gAgent.getBusy()) - { - std::string my_name; - LLAgentUI::buildFullname(my_name); - std::string response = gSavedPerAccountSettings.getString("BusyModeResponse"); - pack_instant_message( - gMessageSystem, - gAgent.getID(), - FALSE, - gAgent.getSessionID(), - from_id, - my_name, - response, - IM_ONLINE, - IM_BUSY_AUTO_RESPONSE); - gAgent.sendReliableMessage(); - } -} - -bool callingcard_offer_callback(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - LLUUID fid; - LLUUID from_id; - LLMessageSystem* msg = gMessageSystem; - switch(option) - { - case 0: - // accept - msg->newMessageFast(_PREHASH_AcceptCallingCard); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_TransactionBlock); - msg->addUUIDFast(_PREHASH_TransactionID, notification["payload"]["transaction_id"].asUUID()); - fid = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); - msg->nextBlockFast(_PREHASH_FolderData); - msg->addUUIDFast(_PREHASH_FolderID, fid); - msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); - break; - case 1: - // decline - msg->newMessageFast(_PREHASH_DeclineCallingCard); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_TransactionBlock); - msg->addUUIDFast(_PREHASH_TransactionID, notification["payload"]["transaction_id"].asUUID()); - msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); - busy_message(msg, notification["payload"]["source_id"].asUUID()); - break; - default: - // close button probably, possibly timed out - break; - } - - return false; -} -static LLNotificationFunctorRegistration callingcard_offer_cb_reg("OfferCallingCard", callingcard_offer_callback); - -void process_offer_callingcard(LLMessageSystem* msg, void**) -{ - // someone has offered to form a friendship - LL_DEBUGS("Messaging") << "callingcard offer" << LL_ENDL; - - LLUUID source_id; - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, source_id); - LLUUID tid; - msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_TransactionID, tid); - - LLSD payload; - payload["transaction_id"] = tid; - payload["source_id"] = source_id; - payload["sender"] = msg->getSender().getIPandPort(); - - LLViewerObject* source = gObjectList.findObject(source_id); - LLSD args; - std::string source_name; - if(source && source->isAvatar()) - { - LLNameValue* nvfirst = source->getNVPair("FirstName"); - LLNameValue* nvlast = source->getNVPair("LastName"); - if (nvfirst && nvlast) - { - source_name = LLCacheName::buildFullName( - nvfirst->getString(), nvlast->getString()); - } - } - - if(!source_name.empty()) - { - if (gAgent.getBusy() - || LLMuteList::getInstance()->isMuted(source_id, source_name, LLMute::flagTextChat)) - { - // automatically decline offer - LLNotifications::instance().forceResponse(LLNotification::Params("OfferCallingCard").payload(payload), 1); - } - else - { - args["NAME"] = source_name; - LLNotificationsUtil::add("OfferCallingCard", args, payload); - } - } - else - { - LL_WARNS("Messaging") << "Calling card offer from an unknown source." << LL_ENDL; - } -} - -void process_accept_callingcard(LLMessageSystem* msg, void**) -{ - LLNotificationsUtil::add("CallingCardAccepted"); -} - -void process_decline_callingcard(LLMessageSystem* msg, void**) -{ - LLNotificationsUtil::add("CallingCardDeclined"); -} - -class ChatTranslationReceiver : public LLTranslate::TranslationReceiver -{ -public : - ChatTranslationReceiver(const std::string &from_lang, const std::string &to_lang, const std::string &mesg, - const LLChat &chat, const LLSD &toast_args) - : LLTranslate::TranslationReceiver(from_lang, to_lang), - m_chat(chat), - m_toastArgs(toast_args), - m_origMesg(mesg) - { - } - - static boost::intrusive_ptr build(const std::string &from_lang, const std::string &to_lang, const std::string &mesg, const LLChat &chat, const LLSD &toast_args) - { - return boost::intrusive_ptr(new ChatTranslationReceiver(from_lang, to_lang, mesg, chat, toast_args)); - } - -protected: - void handleResponse(const std::string &translation, const std::string &detected_language) - { - // filter out non-interesting responeses - if ( !translation.empty() - && (m_toLang != detected_language) - && (LLStringUtil::compareInsensitive(translation, m_origMesg) != 0) ) - { - m_chat.mText += " (" + translation + ")"; - } - - LLNotificationsUI::LLNotificationManager::instance().onChat(m_chat, m_toastArgs); - } - - void handleFailure() - { - LLTranslate::TranslationReceiver::handleFailure(); - m_chat.mText += " (?)"; - - LLNotificationsUI::LLNotificationManager::instance().onChat(m_chat, m_toastArgs); - } - -private: - LLChat m_chat; - std::string m_origMesg; - LLSD m_toastArgs; -}; -void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) -{ - LLChat chat; - std::string mesg; - std::string from_name; - U8 source_temp; - U8 type_temp; - U8 audible_temp; - LLColor4 color(1.0f, 1.0f, 1.0f, 1.0f); - LLUUID from_id; - LLUUID owner_id; - BOOL is_owned_by_me = FALSE; - LLViewerObject* chatter; - - msg->getString("ChatData", "FromName", from_name); - - msg->getUUID("ChatData", "SourceID", from_id); - chat.mFromID = from_id; - - // Object owner for objects - msg->getUUID("ChatData", "OwnerID", owner_id); - - msg->getU8Fast(_PREHASH_ChatData, _PREHASH_SourceType, source_temp); - chat.mSourceType = (EChatSourceType)source_temp; - - msg->getU8("ChatData", "ChatType", type_temp); - chat.mChatType = (EChatType)type_temp; - - msg->getU8Fast(_PREHASH_ChatData, _PREHASH_Audible, audible_temp); - chat.mAudible = (EChatAudible)audible_temp; - - chat.mTime = LLFrameTimer::getElapsedSeconds(); - - // IDEVO Correct for new-style "Resident" names - if (chat.mSourceType == CHAT_SOURCE_AGENT) - { - // I don't know if it's OK to change this here, if - // anything downstream does lookups by name, for instance - - LLAvatarName av_name; - if (LLAvatarNameCache::get(from_id, &av_name)) - { - chat.mFromName = av_name.mDisplayName; - } - else - { - chat.mFromName = LLCacheName::cleanFullName(from_name); - } - } - else - { - chat.mFromName = from_name; - } - - BOOL is_busy = gAgent.getBusy(); - - BOOL is_muted = FALSE; - BOOL is_linden = FALSE; - is_muted = LLMuteList::getInstance()->isMuted( - from_id, - from_name, - LLMute::flagTextChat) - || LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagTextChat); - is_linden = chat.mSourceType != CHAT_SOURCE_OBJECT && - LLMuteList::getInstance()->isLinden(from_name); - - BOOL is_audible = (CHAT_AUDIBLE_FULLY == chat.mAudible); - chatter = gObjectList.findObject(from_id); - if (chatter) - { - chat.mPosAgent = chatter->getPositionAgent(); - - // Make swirly things only for talking objects. (not script debug messages, though) - if (chat.mSourceType == CHAT_SOURCE_OBJECT - && chat.mChatType != CHAT_TYPE_DEBUG_MSG - && gSavedSettings.getBOOL("EffectScriptChatParticles") ) - { - LLPointer psc = new LLViewerPartSourceChat(chatter->getPositionAgent()); - psc->setSourceObject(chatter); - psc->setColor(color); - //We set the particles to be owned by the object's owner, - //just in case they should be muted by the mute list - psc->setOwnerUUID(owner_id); - LLViewerPartSim::getInstance()->addPartSource(psc); - } - - // record last audible utterance - if (is_audible - && (is_linden || (!is_muted && !is_busy))) - { - if (chat.mChatType != CHAT_TYPE_START - && chat.mChatType != CHAT_TYPE_STOP) - { - gAgent.heardChat(chat.mFromID); - } - } - - is_owned_by_me = chatter->permYouOwner(); - } - - if (is_audible) - { - BOOL visible_in_chat_bubble = FALSE; - std::string verb; - - color.setVec(1.f,1.f,1.f,1.f); - msg->getStringFast(_PREHASH_ChatData, _PREHASH_Message, mesg); - - BOOL ircstyle = FALSE; - - // Look for IRC-style emotes here so chatbubbles work - std::string prefix = mesg.substr(0, 4); - if (prefix == "/me " || prefix == "/me'") - { - ircstyle = TRUE; - } - chat.mText = mesg; - - // Look for the start of typing so we can put "..." in the bubbles. - if (CHAT_TYPE_START == chat.mChatType) - { - LLLocalSpeakerMgr::getInstance()->setSpeakerTyping(from_id, TRUE); - - // Might not have the avatar constructed yet, eg on login. - if (chatter && chatter->isAvatar()) - { - ((LLVOAvatar*)chatter)->startTyping(); - } - return; - } - else if (CHAT_TYPE_STOP == chat.mChatType) - { - LLLocalSpeakerMgr::getInstance()->setSpeakerTyping(from_id, FALSE); - - // Might not have the avatar constructed yet, eg on login. - if (chatter && chatter->isAvatar()) - { - ((LLVOAvatar*)chatter)->stopTyping(); - } - return; - } - - // Look for IRC-style emotes - if (ircstyle) - { - // set CHAT_STYLE_IRC to avoid adding Avatar Name as author of message. See EXT-656 - chat.mChatStyle = CHAT_STYLE_IRC; - - // Do nothing, ircstyle is fixed above for chat bubbles - } - else - { - switch(chat.mChatType) - { - case CHAT_TYPE_WHISPER: - verb = LLTrans::getString("whisper") + " "; - break; - case CHAT_TYPE_DEBUG_MSG: - case CHAT_TYPE_OWNER: - case CHAT_TYPE_NORMAL: - verb = ""; - break; - case CHAT_TYPE_SHOUT: - verb = LLTrans::getString("shout") + " "; - break; - case CHAT_TYPE_START: - case CHAT_TYPE_STOP: - LL_WARNS("Messaging") << "Got chat type start/stop in main chat processing." << LL_ENDL; - break; - default: - LL_WARNS("Messaging") << "Unknown type " << chat.mChatType << " in chat!" << LL_ENDL; - verb = ""; - break; - } - - - chat.mText = ""; - chat.mText += verb; - chat.mText += mesg; - } - - // We have a real utterance now, so can stop showing "..." and proceed. - if (chatter && chatter->isAvatar()) - { - LLLocalSpeakerMgr::getInstance()->setSpeakerTyping(from_id, FALSE); - ((LLVOAvatar*)chatter)->stopTyping(); - - if (!is_muted && !is_busy) - { - visible_in_chat_bubble = gSavedSettings.getBOOL("UseChatBubbles"); - std::string formated_msg = ""; - LLViewerChat::formatChatMsg(chat, formated_msg); - LLChat chat_bubble = chat; - chat_bubble.mText = formated_msg; - ((LLVOAvatar*)chatter)->addChat(chat_bubble); - } - } - - if (chatter) - { - chat.mPosAgent = chatter->getPositionAgent(); - } - - // truth table: - // LINDEN BUSY MUTED OWNED_BY_YOU TASK DISPLAY STORE IN HISTORY - // F F F F * Yes Yes - // F F F T * Yes Yes - // F F T F * No No - // F F T T * No No - // F T F F * No Yes - // F T F T * Yes Yes - // F T T F * No No - // F T T T * No No - // T * * * F Yes Yes - - chat.mMuted = is_muted && !is_linden; - - // pass owner_id to chat so that we can display the remote - // object inspect for an object that is chatting with you - LLSD args; - args["type"] = LLNotificationsUI::NT_NEARBYCHAT; - chat.mOwnerID = owner_id; - - if (gSavedSettings.getBOOL("TranslateChat") && chat.mSourceType != CHAT_SOURCE_SYSTEM) - { - if (chat.mChatStyle == CHAT_STYLE_IRC) - { - mesg = mesg.substr(4, std::string::npos); - } - const std::string from_lang = ""; // leave empty to trigger autodetect - const std::string to_lang = LLTranslate::getTranslateLanguage(); - - LLHTTPClient::ResponderPtr result = ChatTranslationReceiver::build(from_lang, to_lang, mesg, chat, args); - LLTranslate::translateMessage(result, from_lang, to_lang, mesg); - } - else - { - LLNotificationsUI::LLNotificationManager::instance().onChat(chat, args); - } - } -} - - -// Simulator we're on is informing the viewer that the agent -// is starting to teleport (perhaps to another sim, perhaps to the -// same sim). If we initiated the teleport process by sending some kind -// of TeleportRequest, then this info is redundant, but if the sim -// initiated the teleport (via a script call, being killed, etc.) -// then this info is news to us. -void process_teleport_start(LLMessageSystem *msg, void**) -{ - // on teleport, don't tell them about destination guide anymore - LLFirstUse::notUsingDestinationGuide(false); - U32 teleport_flags = 0x0; - msg->getU32("Info", "TeleportFlags", teleport_flags); - - LL_DEBUGS("Messaging") << "Got TeleportStart with TeleportFlags=" << teleport_flags << ". gTeleportDisplay: " << gTeleportDisplay << ", gAgent.mTeleportState: " << gAgent.getTeleportState() << LL_ENDL; - - if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL) - { - gViewerWindow->setProgressCancelButtonVisible(FALSE); - } - else - { - gViewerWindow->setProgressCancelButtonVisible(TRUE, LLTrans::getString("Cancel")); - } - - // Freeze the UI and show progress bar - // Note: could add data here to differentiate between normal teleport and death. - - if( gAgent.getTeleportState() == LLAgent::TELEPORT_NONE ) - { - gTeleportDisplay = TRUE; - gAgent.setTeleportState( LLAgent::TELEPORT_START ); - make_ui_sound("UISndTeleportOut"); - - LL_INFOS("Messaging") << "Teleport initiated by remote TeleportStart message with TeleportFlags: " << teleport_flags << LL_ENDL; - // Don't call LLFirstUse::useTeleport here because this could be - // due to being killed, which would send you home, not to a Telehub - } -} - -void process_teleport_progress(LLMessageSystem* msg, void**) -{ - LLUUID agent_id; - msg->getUUID("AgentData", "AgentID", agent_id); - if((gAgent.getID() != agent_id) - || (gAgent.getTeleportState() == LLAgent::TELEPORT_NONE)) - { - LL_WARNS("Messaging") << "Unexpected teleport progress message." << LL_ENDL; - return; - } - U32 teleport_flags = 0x0; - msg->getU32("Info", "TeleportFlags", teleport_flags); - if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL) - { - gViewerWindow->setProgressCancelButtonVisible(FALSE); - } - else - { - gViewerWindow->setProgressCancelButtonVisible(TRUE, LLTrans::getString("Cancel")); - } - std::string buffer; - msg->getString("Info", "Message", buffer); - LL_DEBUGS("Messaging") << "teleport progress: " << buffer << LL_ENDL; - - //Sorta hacky...default to using simulator raw messages - //if we don't find the coresponding mapping in our progress mappings - std::string message = buffer; - - if (LLAgent::sTeleportProgressMessages.find(buffer) != - LLAgent::sTeleportProgressMessages.end() ) - { - message = LLAgent::sTeleportProgressMessages[buffer]; - } - - gAgent.setTeleportMessage(LLAgent::sTeleportProgressMessages[message]); -} - -class LLFetchInWelcomeArea : public LLInventoryFetchDescendentsObserver -{ -public: - LLFetchInWelcomeArea(const uuid_vec_t &ids) : - LLInventoryFetchDescendentsObserver(ids) - {} - virtual void done() - { - LLIsType is_landmark(LLAssetType::AT_LANDMARK); - LLIsType is_card(LLAssetType::AT_CALLINGCARD); - - LLInventoryModel::cat_array_t card_cats; - LLInventoryModel::item_array_t card_items; - LLInventoryModel::cat_array_t land_cats; - LLInventoryModel::item_array_t land_items; - - uuid_vec_t::iterator it = mComplete.begin(); - uuid_vec_t::iterator end = mComplete.end(); - for(; it != end; ++it) - { - gInventory.collectDescendentsIf( - (*it), - land_cats, - land_items, - LLInventoryModel::EXCLUDE_TRASH, - is_landmark); - gInventory.collectDescendentsIf( - (*it), - card_cats, - card_items, - LLInventoryModel::EXCLUDE_TRASH, - is_card); - } - LLSD args; - if ( land_items.count() > 0 ) - { // Show notification that they can now teleport to landmarks. Use a random landmark from the inventory - S32 random_land = ll_rand( land_items.count() - 1 ); - args["NAME"] = land_items[random_land]->getName(); - LLNotificationsUtil::add("TeleportToLandmark",args); - } - if ( card_items.count() > 0 ) - { // Show notification that they can now contact people. Use a random calling card from the inventory - S32 random_card = ll_rand( card_items.count() - 1 ); - args["NAME"] = card_items[random_card]->getName(); - LLNotificationsUtil::add("TeleportToPerson",args); - } - - gInventory.removeObserver(this); - delete this; - } -}; - - - -class LLPostTeleportNotifiers : public LLEventTimer -{ -public: - LLPostTeleportNotifiers(); - virtual ~LLPostTeleportNotifiers(); - - //function to be called at the supplied frequency - virtual BOOL tick(); -}; - -LLPostTeleportNotifiers::LLPostTeleportNotifiers() : LLEventTimer( 2.0 ) -{ -}; - -LLPostTeleportNotifiers::~LLPostTeleportNotifiers() -{ -} - -BOOL LLPostTeleportNotifiers::tick() -{ - BOOL all_done = FALSE; - if ( gAgent.getTeleportState() == LLAgent::TELEPORT_NONE ) - { - // get callingcards and landmarks available to the user arriving. - uuid_vec_t folders; - const LLUUID callingcard_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); - if(callingcard_id.notNull()) - folders.push_back(callingcard_id); - const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); - if(folder_id.notNull()) - folders.push_back(folder_id); - if(!folders.empty()) - { - LLFetchInWelcomeArea* fetcher = new LLFetchInWelcomeArea(folders); - fetcher->startFetch(); - if(fetcher->isFinished()) - { - fetcher->done(); - } - else - { - gInventory.addObserver(fetcher); - } - } - all_done = TRUE; - } - - return all_done; -} - - - -// Teleport notification from the simulator -// We're going to pretend to be a new agent -void process_teleport_finish(LLMessageSystem* msg, void**) -{ - LL_DEBUGS("Messaging") << "Got teleport location message" << LL_ENDL; - LLUUID agent_id; - msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id); - if (agent_id != gAgent.getID()) - { - LL_WARNS("Messaging") << "Got teleport notification for wrong agent!" << LL_ENDL; - return; - } - - // Teleport is finished; it can't be cancelled now. - gViewerWindow->setProgressCancelButtonVisible(FALSE); - - // Do teleport effect for where you're leaving - // VEFFECT: TeleportStart - LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); - effectp->setPositionGlobal(gAgent.getPositionGlobal()); - effectp->setColor(LLColor4U(gAgent.getEffectColor())); - LLHUDManager::getInstance()->sendEffects(); - - U32 location_id; - U32 sim_ip; - U16 sim_port; - LLVector3 pos, look_at; - U64 region_handle; - msg->getU32Fast(_PREHASH_Info, _PREHASH_LocationID, location_id); - msg->getIPAddrFast(_PREHASH_Info, _PREHASH_SimIP, sim_ip); - msg->getIPPortFast(_PREHASH_Info, _PREHASH_SimPort, sim_port); - //msg->getVector3Fast(_PREHASH_Info, _PREHASH_Position, pos); - //msg->getVector3Fast(_PREHASH_Info, _PREHASH_LookAt, look_at); - msg->getU64Fast(_PREHASH_Info, _PREHASH_RegionHandle, region_handle); - U32 teleport_flags; - msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags); - - - std::string seedCap; - msg->getStringFast(_PREHASH_Info, _PREHASH_SeedCapability, seedCap); - - // update home location if we are teleporting out of prelude - specific to teleporting to welcome area - if((teleport_flags & TELEPORT_FLAGS_SET_HOME_TO_TARGET) - && (!gAgent.isGodlike())) - { - gAgent.setHomePosRegion(region_handle, pos); - - // Create a timer that will send notices when teleporting is all finished. Since this is - // based on the LLEventTimer class, it will be managed by that class and not orphaned or leaked. - new LLPostTeleportNotifiers(); - } - - LLHost sim_host(sim_ip, sim_port); - - // Viewer trusts the simulator. - gMessageSystem->enableCircuit(sim_host, TRUE); - LLViewerRegion* regionp = LLWorld::getInstance()->addRegion(region_handle, sim_host); - -/* - // send camera update to new region - gAgentCamera.updateCamera(); - - // likewise make sure the camera is behind the avatar - gAgentCamera.resetView(TRUE); - LLVector3 shift_vector = regionp->getPosRegionFromGlobal(gAgent.getRegion()->getOriginGlobal()); - gAgent.setRegion(regionp); - gObjectList.shiftObjects(shift_vector); - - if (isAgentAvatarValid()) - { - gAgentAvatarp->clearChatText(); - gAgentCamera.slamLookAt(look_at); - } - gAgent.setPositionAgent(pos); - gAssetStorage->setUpstream(sim); - gCacheName->setUpstream(sim); -*/ - - // now, use the circuit info to tell simulator about us! - LL_INFOS("Messaging") << "process_teleport_finish() Enabling " - << sim_host << " with code " << msg->mOurCircuitCode << LL_ENDL; - msg->newMessageFast(_PREHASH_UseCircuitCode); - msg->nextBlockFast(_PREHASH_CircuitCode); - msg->addU32Fast(_PREHASH_Code, msg->getOurCircuitCode()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addUUIDFast(_PREHASH_ID, gAgent.getID()); - msg->sendReliable(sim_host); - - send_complete_agent_movement(sim_host); - gAgent.setTeleportState( LLAgent::TELEPORT_MOVING ); - gAgent.setTeleportMessage(LLAgent::sTeleportProgressMessages["contacting"]); - - regionp->setSeedCapability(seedCap); - - // Don't send camera updates to the new region until we're - // actually there... - - - // Now do teleport effect for where you're going. - // VEFFECT: TeleportEnd - effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); - effectp->setPositionGlobal(gAgent.getPositionGlobal()); - - effectp->setColor(LLColor4U(gAgent.getEffectColor())); - LLHUDManager::getInstance()->sendEffects(); - -// gTeleportDisplay = TRUE; -// gTeleportDisplayTimer.reset(); -// gViewerWindow->setShowProgress(TRUE); -} - -// stuff we have to do every time we get an AvatarInitComplete from a sim -/* -void process_avatar_init_complete(LLMessageSystem* msg, void**) -{ - LLVector3 agent_pos; - msg->getVector3Fast(_PREHASH_AvatarData, _PREHASH_Position, agent_pos); - agent_movement_complete(msg->getSender(), agent_pos); -} -*/ - -void process_agent_movement_complete(LLMessageSystem* msg, void**) -{ - gAgentMovementCompleted = true; - - LLUUID agent_id; - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); - LLUUID session_id; - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); - if((gAgent.getID() != agent_id) || (gAgent.getSessionID() != session_id)) - { - LL_WARNS("Messaging") << "Incorrect id in process_agent_movement_complete()" - << LL_ENDL; - return; - } - - LL_DEBUGS("Messaging") << "process_agent_movement_complete()" << LL_ENDL; - - // *TODO: check timestamp to make sure the movement compleation - // makes sense. - LLVector3 agent_pos; - msg->getVector3Fast(_PREHASH_Data, _PREHASH_Position, agent_pos); - LLVector3 look_at; - msg->getVector3Fast(_PREHASH_Data, _PREHASH_LookAt, look_at); - U64 region_handle; - msg->getU64Fast(_PREHASH_Data, _PREHASH_RegionHandle, region_handle); - - std::string version_channel; - msg->getString("SimData", "ChannelVersion", version_channel); - - if (!isAgentAvatarValid()) - { - // Could happen if you were immediately god-teleported away on login, - // maybe other cases. Continue, but warn. - LL_WARNS("Messaging") << "agent_movement_complete() with NULL avatarp." << LL_ENDL; - } - - F32 x, y; - from_region_handle(region_handle, &x, &y); - LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromHandle(region_handle); - if (!regionp) - { - if (gAgent.getRegion()) - { - LL_WARNS("Messaging") << "current region " << gAgent.getRegion()->getOriginGlobal() << LL_ENDL; - } - - LL_WARNS("Messaging") << "Agent being sent to invalid home region: " - << x << ":" << y - << " current pos " << gAgent.getPositionGlobal() - << LL_ENDL; - LLAppViewer::instance()->forceDisconnect(LLTrans::getString("SentToInvalidRegion")); - return; - - } - - LL_INFOS("Messaging") << "Changing home region to " << x << ":" << y << LL_ENDL; - - // set our upstream host the new simulator and shuffle things as - // appropriate. - LLVector3 shift_vector = regionp->getPosRegionFromGlobal( - gAgent.getRegion()->getOriginGlobal()); - gAgent.setRegion(regionp); - gObjectList.shiftObjects(shift_vector); - gAssetStorage->setUpstream(msg->getSender()); - gCacheName->setUpstream(msg->getSender()); - gViewerThrottle.sendToSim(); - gViewerWindow->sendShapeToSim(); - - bool is_teleport = gAgent.getTeleportState() == LLAgent::TELEPORT_MOVING; - - if( is_teleport ) - { - if (gAgent.getTeleportKeepsLookAt()) - { - // *NOTE: the LookAt data we get from the sim here doesn't - // seem to be useful, so get it from the camera instead - look_at = LLViewerCamera::getInstance()->getAtAxis(); - } - // Force the camera back onto the agent, don't animate. - gAgentCamera.setFocusOnAvatar(TRUE, FALSE); - gAgentCamera.slamLookAt(look_at); - gAgentCamera.updateCamera(); - - gAgent.setTeleportState( LLAgent::TELEPORT_START_ARRIVAL ); - - // set the appearance on teleport since the new sim does not - // know what you look like. - gAgent.sendAgentSetAppearance(); - - if (isAgentAvatarValid()) - { - // Chat the "back" SLURL. (DEV-4907) - - LLSLURL slurl; - gAgent.getTeleportSourceSLURL(slurl); - LLSD substitution = LLSD().with("[T_SLURL]", slurl.getSLURLString()); - std::string completed_from = LLAgent::sTeleportProgressMessages["completed_from"]; - LLStringUtil::format(completed_from, substitution); - - LLSD args; - args["MESSAGE"] = completed_from; - LLNotificationsUtil::add("SystemMessageTip", args); - - // Set the new position - gAgentAvatarp->setPositionAgent(agent_pos); - gAgentAvatarp->clearChat(); - gAgentAvatarp->slamPosition(); - } - } - else - { - // This is likely just the initial logging in phase. - gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); - } - - if ( LLTracker::isTracking(NULL) ) - { - // Check distance to beacon, if < 5m, remove beacon - LLVector3d beacon_pos = LLTracker::getTrackedPositionGlobal(); - LLVector3 beacon_dir(agent_pos.mV[VX] - (F32)fmod(beacon_pos.mdV[VX], 256.0), agent_pos.mV[VY] - (F32)fmod(beacon_pos.mdV[VY], 256.0), 0); - if (beacon_dir.magVecSquared() < 25.f) - { - LLTracker::stopTracking(NULL); - } - else if ( is_teleport && !gAgent.getTeleportKeepsLookAt() ) - { - //look at the beacon - LLVector3 global_agent_pos = agent_pos; - global_agent_pos[0] += x; - global_agent_pos[1] += y; - look_at = (LLVector3)beacon_pos - global_agent_pos; - look_at.normVec(); - gAgentCamera.slamLookAt(look_at); - } - } - - // TODO: Put back a check for flying status! DK 12/19/05 - // Sim tells us whether the new position is off the ground - /* - if (teleport_flags & TELEPORT_FLAGS_IS_FLYING) - { - gAgent.setFlying(TRUE); - } - else - { - gAgent.setFlying(FALSE); - } - */ - - send_agent_update(TRUE, TRUE); - - if (gAgent.getRegion()->getBlockFly()) - { - gAgent.setFlying(gAgent.canFly()); - } - - // force simulator to recognize busy state - if (gAgent.getBusy()) - { - gAgent.setBusy(); - } - else - { - gAgent.clearBusy(); - } - - if (isAgentAvatarValid()) - { - gAgentAvatarp->mFootPlane.clearVec(); - } - - // send walk-vs-run status - gAgent.sendWalkRun(gAgent.getRunning() || gAgent.getAlwaysRun()); - - // If the server version has changed, display an info box and offer - // to display the release notes, unless this is the initial log in. - if (gLastVersionChannel == version_channel) - { - return; - } - - gLastVersionChannel = version_channel; -} - -void process_crossed_region(LLMessageSystem* msg, void**) -{ - LLUUID agent_id; - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); - LLUUID session_id; - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); - if((gAgent.getID() != agent_id) || (gAgent.getSessionID() != session_id)) - { - LL_WARNS("Messaging") << "Incorrect id in process_crossed_region()" - << LL_ENDL; - return; - } - LL_INFOS("Messaging") << "process_crossed_region()" << LL_ENDL; - gAgentAvatarp->resetRegionCrossingTimer(); - - U32 sim_ip; - msg->getIPAddrFast(_PREHASH_RegionData, _PREHASH_SimIP, sim_ip); - U16 sim_port; - msg->getIPPortFast(_PREHASH_RegionData, _PREHASH_SimPort, sim_port); - LLHost sim_host(sim_ip, sim_port); - U64 region_handle; - msg->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle); - - std::string seedCap; - msg->getStringFast(_PREHASH_RegionData, _PREHASH_SeedCapability, seedCap); - - send_complete_agent_movement(sim_host); - - LLViewerRegion* regionp = LLWorld::getInstance()->addRegion(region_handle, sim_host); - regionp->setSeedCapability(seedCap); -} - - - -// Sends avatar and camera information to simulator. -// Sent roughly once per frame, or 20 times per second, whichever is less often - -const F32 THRESHOLD_HEAD_ROT_QDOT = 0.9997f; // ~= 2.5 degrees -- if its less than this we need to update head_rot -const F32 MAX_HEAD_ROT_QDOT = 0.99999f; // ~= 0.5 degrees -- if its greater than this then no need to update head_rot - // between these values we delay the updates (but no more than one second) - -static LLFastTimer::DeclareTimer FTM_AGENT_UPDATE_SEND("Send Message"); - -void send_agent_update(BOOL force_send, BOOL send_reliable) -{ - if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) - { - // We don't care if they want to send an agent update, they're not allowed to until the simulator - // that's the target is ready to receive them (after avatar_init_complete is received) - return; - } - - // We have already requested to log out. Don't send agent updates. - if(LLAppViewer::instance()->logoutRequestSent()) - { - return; - } - - // no region to send update to - if(gAgent.getRegion() == NULL) - { - return; - } - - const F32 TRANSLATE_THRESHOLD = 0.01f; - - // NOTA BENE: This is (intentionally?) using the small angle sine approximation to test for rotation - // Plus, there is an extra 0.5 in the mix since the perpendicular between last_camera_at and getAtAxis() bisects cam_rot_change - // Thus, we're actually testing against 0.2 degrees - const F32 ROTATION_THRESHOLD = 0.1f * 2.f*F_PI/360.f; // Rotation thresh 0.2 deg, see note above - - const U8 DUP_MSGS = 1; // HACK! number of times to repeat data on motionless agent - - // Store data on last sent update so that if no changes, no send - static LLVector3 last_camera_pos_agent, - last_camera_at, - last_camera_left, - last_camera_up; - - static LLVector3 cam_center_chg, - cam_rot_chg; - - static LLQuaternion last_head_rot; - static U32 last_control_flags = 0; - static U8 last_render_state; - static U8 duplicate_count = 0; - static F32 head_rot_chg = 1.0; - static U8 last_flags; - - LLMessageSystem *msg = gMessageSystem; - LLVector3 camera_pos_agent; // local to avatar's region - U8 render_state; - - LLQuaternion body_rotation = gAgent.getFrameAgent().getQuaternion(); - LLQuaternion head_rotation = gAgent.getHeadRotation(); - - camera_pos_agent = gAgentCamera.getCameraPositionAgent(); - - render_state = gAgent.getRenderState(); - - U32 control_flag_change = 0; - U8 flag_change = 0; - - cam_center_chg = last_camera_pos_agent - camera_pos_agent; - cam_rot_chg = last_camera_at - LLViewerCamera::getInstance()->getAtAxis(); - - // If a modifier key is held down, turn off - // LBUTTON and ML_LBUTTON so that using the camera (alt-key) doesn't - // trigger a control event. - U32 control_flags = gAgent.getControlFlags(); - MASK key_mask = gKeyboard->currentMask(TRUE); - if (key_mask & MASK_ALT || key_mask & MASK_CONTROL) - { - control_flags &= ~( AGENT_CONTROL_LBUTTON_DOWN | - AGENT_CONTROL_ML_LBUTTON_DOWN ); - control_flags |= AGENT_CONTROL_LBUTTON_UP | - AGENT_CONTROL_ML_LBUTTON_UP ; - } - - control_flag_change = last_control_flags ^ control_flags; - - U8 flags = AU_FLAGS_NONE; - if (gAgent.isGroupTitleHidden()) - { - flags |= AU_FLAGS_HIDETITLE; - } - if (gAgent.getAutoPilot()) - { - flags |= AU_FLAGS_CLIENT_AUTOPILOT; - } - - flag_change = last_flags ^ flags; - - head_rot_chg = dot(last_head_rot, head_rotation); - - if (force_send || - (cam_center_chg.magVec() > TRANSLATE_THRESHOLD) || - (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT) || - (last_render_state != render_state) || - (cam_rot_chg.magVec() > ROTATION_THRESHOLD) || - control_flag_change != 0 || - flag_change != 0) - { -/* - if (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT) - { - //LL_INFOS("Messaging") << "head rot " << head_rotation << LL_ENDL; - LL_INFOS("Messaging") << "head_rot_chg = " << head_rot_chg << LL_ENDL; - } - if (cam_rot_chg.magVec() > ROTATION_THRESHOLD) - { - LL_INFOS("Messaging") << "cam rot " << cam_rot_chg.magVec() << LL_ENDL; - } - if (cam_center_chg.magVec() > TRANSLATE_THRESHOLD) - { - LL_INFOS("Messaging") << "cam center " << cam_center_chg.magVec() << LL_ENDL; - } -// if (drag_delta_chg.magVec() > TRANSLATE_THRESHOLD) -// { -// LL_INFOS("Messaging") << "drag delta " << drag_delta_chg.magVec() << LL_ENDL; -// } - if (control_flag_change) - { - LL_INFOS("Messaging") << "dcf = " << control_flag_change << LL_ENDL; - } -*/ - - duplicate_count = 0; - } - else - { - duplicate_count++; - - if (head_rot_chg < MAX_HEAD_ROT_QDOT && duplicate_count < AGENT_UPDATES_PER_SECOND) - { - // The head_rotation is sent for updating things like attached guns. - // We only trigger a new update when head_rotation deviates beyond - // some threshold from the last update, however this can break fine - // adjustments when trying to aim an attached gun, so what we do here - // (where we would normally skip sending an update when nothing has changed) - // is gradually reduce the threshold to allow a better update to - // eventually get sent... should update to within 0.5 degrees in less - // than a second. - if (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT + (MAX_HEAD_ROT_QDOT - THRESHOLD_HEAD_ROT_QDOT) * duplicate_count / AGENT_UPDATES_PER_SECOND) - { - duplicate_count = 0; - } - else - { - return; - } - } - else - { - return; - } - } - - if (duplicate_count < DUP_MSGS && !gDisconnected) - { - LLFastTimer t(FTM_AGENT_UPDATE_SEND); - // Build the message - msg->newMessageFast(_PREHASH_AgentUpdate); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addQuatFast(_PREHASH_BodyRotation, body_rotation); - msg->addQuatFast(_PREHASH_HeadRotation, head_rotation); - msg->addU8Fast(_PREHASH_State, render_state); - msg->addU8Fast(_PREHASH_Flags, flags); - -// if (camera_pos_agent.mV[VY] > 255.f) -// { -// LL_INFOS("Messaging") << "Sending camera center " << camera_pos_agent << LL_ENDL; -// } - - msg->addVector3Fast(_PREHASH_CameraCenter, camera_pos_agent); - msg->addVector3Fast(_PREHASH_CameraAtAxis, LLViewerCamera::getInstance()->getAtAxis()); - msg->addVector3Fast(_PREHASH_CameraLeftAxis, LLViewerCamera::getInstance()->getLeftAxis()); - msg->addVector3Fast(_PREHASH_CameraUpAxis, LLViewerCamera::getInstance()->getUpAxis()); - msg->addF32Fast(_PREHASH_Far, gAgentCamera.mDrawDistance); - - msg->addU32Fast(_PREHASH_ControlFlags, control_flags); - - if (gDebugClicks) - { - if (control_flags & AGENT_CONTROL_LBUTTON_DOWN) - { - LL_INFOS("Messaging") << "AgentUpdate left button down" << LL_ENDL; - } - - if (control_flags & AGENT_CONTROL_LBUTTON_UP) - { - LL_INFOS("Messaging") << "AgentUpdate left button up" << LL_ENDL; - } - } - - gAgent.enableControlFlagReset(); - - if (!send_reliable) - { - gAgent.sendMessage(); - } - else - { - gAgent.sendReliableMessage(); - } - -// LL_DEBUGS("Messaging") << "agent " << avatar_pos_agent << " cam " << camera_pos_agent << LL_ENDL; - - // Copy the old data - last_head_rot = head_rotation; - last_render_state = render_state; - last_camera_pos_agent = camera_pos_agent; - last_camera_at = LLViewerCamera::getInstance()->getAtAxis(); - last_camera_left = LLViewerCamera::getInstance()->getLeftAxis(); - last_camera_up = LLViewerCamera::getInstance()->getUpAxis(); - last_control_flags = control_flags; - last_flags = flags; - } -} - - - -// *TODO: Remove this dependency, or figure out a better way to handle -// this hack. -extern U32 gObjectBits; - -void process_object_update(LLMessageSystem *mesgsys, void **user_data) -{ - LLMemType mt(LLMemType::MTYPE_OBJECT); - // Update the data counters - if (mesgsys->getReceiveCompressedSize()) - { - gObjectBits += mesgsys->getReceiveCompressedSize() * 8; - } - else - { - gObjectBits += mesgsys->getReceiveSize() * 8; - } - - // Update the object... - gObjectList.processObjectUpdate(mesgsys, user_data, OUT_FULL); -} - -void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data) -{ - LLMemType mt(LLMemType::MTYPE_OBJECT); - // Update the data counters - if (mesgsys->getReceiveCompressedSize()) - { - gObjectBits += mesgsys->getReceiveCompressedSize() * 8; - } - else - { - gObjectBits += mesgsys->getReceiveSize() * 8; - } - - // Update the object... - gObjectList.processCompressedObjectUpdate(mesgsys, user_data, OUT_FULL_COMPRESSED); -} - -void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data) -{ - LLMemType mt(LLMemType::MTYPE_OBJECT); - // Update the data counters - if (mesgsys->getReceiveCompressedSize()) - { - gObjectBits += mesgsys->getReceiveCompressedSize() * 8; - } - else - { - gObjectBits += mesgsys->getReceiveSize() * 8; - } - - // Update the object... - gObjectList.processCachedObjectUpdate(mesgsys, user_data, OUT_FULL_CACHED); -} - - -void process_terse_object_update_improved(LLMessageSystem *mesgsys, void **user_data) -{ - LLMemType mt(LLMemType::MTYPE_OBJECT); - if (mesgsys->getReceiveCompressedSize()) - { - gObjectBits += mesgsys->getReceiveCompressedSize() * 8; - } - else - { - gObjectBits += mesgsys->getReceiveSize() * 8; - } - - gObjectList.processCompressedObjectUpdate(mesgsys, user_data, OUT_TERSE_IMPROVED); -} - -static LLFastTimer::DeclareTimer FTM_PROCESS_OBJECTS("Process Objects"); - - -void process_kill_object(LLMessageSystem *mesgsys, void **user_data) -{ - LLFastTimer t(FTM_PROCESS_OBJECTS); - - LLUUID id; - U32 local_id; - S32 i; - S32 num_objects; - - num_objects = mesgsys->getNumberOfBlocksFast(_PREHASH_ObjectData); - - for (i = 0; i < num_objects; i++) - { - mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i); - - LLViewerObjectList::getUUIDFromLocal(id, - local_id, - gMessageSystem->getSenderIP(), - gMessageSystem->getSenderPort()); - if (id == LLUUID::null) - { - LL_DEBUGS("Messaging") << "Unknown kill for local " << local_id << LL_ENDL; - gObjectList.mNumUnknownKills++; - continue; - } - else - { - LL_DEBUGS("Messaging") << "Kill message for local " << local_id << LL_ENDL; - } - - LLSelectMgr::getInstance()->removeObjectFromSelections(id); - - // ...don't kill the avatar - if (!(id == gAgentID)) - { - LLViewerObject *objectp = gObjectList.findObject(id); - if (objectp) - { - // Display green bubble on kill - if ( gShowObjectUpdates ) - { - LLViewerObject* newobject; - newobject = gObjectList.createObjectViewer(LL_PCODE_LEGACY_TEXT_BUBBLE, objectp->getRegion()); - - LLVOTextBubble* bubble = (LLVOTextBubble*) newobject; - - bubble->mColor.setVec(0.f, 1.f, 0.f, 1.f); - bubble->setScale( 2.0f * bubble->getScale() ); - bubble->setPositionGlobal(objectp->getPositionGlobal()); - gPipeline.addObject(bubble); - } - - // Do the kill - gObjectList.killObject(objectp); - } - else - { - LL_WARNS("Messaging") << "Object in UUID lookup, but not on object list in kill!" << LL_ENDL; - gObjectList.mNumUnknownKills++; - } - } - } -} - -void process_time_synch(LLMessageSystem *mesgsys, void **user_data) -{ - LLVector3 sun_direction; - LLVector3 sun_ang_velocity; - F32 phase; - U64 space_time_usec; - - U32 seconds_per_day; - U32 seconds_per_year; - - // "SimulatorViewerTimeMessage" - mesgsys->getU64Fast(_PREHASH_TimeInfo, _PREHASH_UsecSinceStart, space_time_usec); - mesgsys->getU32Fast(_PREHASH_TimeInfo, _PREHASH_SecPerDay, seconds_per_day); - mesgsys->getU32Fast(_PREHASH_TimeInfo, _PREHASH_SecPerYear, seconds_per_year); - - // This should eventually be moved to an "UpdateHeavenlyBodies" message - mesgsys->getF32Fast(_PREHASH_TimeInfo, _PREHASH_SunPhase, phase); - mesgsys->getVector3Fast(_PREHASH_TimeInfo, _PREHASH_SunDirection, sun_direction); - mesgsys->getVector3Fast(_PREHASH_TimeInfo, _PREHASH_SunAngVelocity, sun_ang_velocity); - - LLWorld::getInstance()->setSpaceTimeUSec(space_time_usec); - - //LL_DEBUGS("Messaging") << "time_synch() - " << sun_direction << ", " << sun_ang_velocity - // << ", " << phase << LL_ENDL; - - gSky.setSunPhase(phase); - gSky.setSunTargetDirection(sun_direction, sun_ang_velocity); - if (!gNoRender && !(gSavedSettings.getBOOL("SkyOverrideSimSunPosition") || gSky.getOverrideSun())) - { - gSky.setSunDirection(sun_direction, sun_ang_velocity); - } -} - -void process_sound_trigger(LLMessageSystem *msg, void **) -{ - if (!gAudiop) return; - - U64 region_handle = 0; - F32 gain = 0; - LLUUID sound_id; - LLUUID owner_id; - LLUUID object_id; - LLUUID parent_id; - LLVector3 pos_local; - - msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_SoundID, sound_id); - msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_OwnerID, owner_id); - msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_ObjectID, object_id); - msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_ParentID, parent_id); - msg->getU64Fast(_PREHASH_SoundData, _PREHASH_Handle, region_handle); - msg->getVector3Fast(_PREHASH_SoundData, _PREHASH_Position, pos_local); - msg->getF32Fast(_PREHASH_SoundData, _PREHASH_Gain, gain); - - // adjust sound location to true global coords - LLVector3d pos_global = from_region_handle(region_handle); - pos_global.mdV[VX] += pos_local.mV[VX]; - pos_global.mdV[VY] += pos_local.mV[VY]; - pos_global.mdV[VZ] += pos_local.mV[VZ]; - - // Don't play a trigger sound if you can't hear it due - // to parcel "local audio only" settings. - if (!LLViewerParcelMgr::getInstance()->canHearSound(pos_global)) return; - - // Don't play sounds triggered by someone you muted. - if (LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagObjectSounds)) return; - - // Don't play sounds from an object you muted - if (LLMuteList::getInstance()->isMuted(object_id)) return; - - // Don't play sounds from an object whose parent you muted - if (parent_id.notNull() - && LLMuteList::getInstance()->isMuted(parent_id)) - { - return; - } - - // Don't play sounds from a region with maturity above current agent maturity - if( !gAgent.canAccessMaturityInRegion( region_handle ) ) - { - return; - } - - gAudiop->triggerSound(sound_id, owner_id, gain, LLAudioEngine::AUDIO_TYPE_SFX, pos_global); -} - -void process_preload_sound(LLMessageSystem *msg, void **user_data) -{ - if (!gAudiop) - { - return; - } - - LLUUID sound_id; - LLUUID object_id; - LLUUID owner_id; - - msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_SoundID, sound_id); - msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_id); - msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_OwnerID, owner_id); - - LLViewerObject *objectp = gObjectList.findObject(object_id); - if (!objectp) return; - - if (LLMuteList::getInstance()->isMuted(object_id)) return; - if (LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagObjectSounds)) return; - - LLAudioSource *sourcep = objectp->getAudioSource(owner_id); - if (!sourcep) return; - - LLAudioData *datap = gAudiop->getAudioData(sound_id); - - // Note that I don't actually do any loading of the - // audio data into a buffer at this point, as it won't actually - // help us out. - - // Don't play sounds from a region with maturity above current agent maturity - LLVector3d pos_global = objectp->getPositionGlobal(); - if (gAgent.canAccessMaturityAtGlobal(pos_global)) - { - // Add audioData starts a transfer internally. - sourcep->addAudioData(datap, FALSE); -} -} - -void process_attached_sound(LLMessageSystem *msg, void **user_data) -{ - F32 gain = 0; - LLUUID sound_id; - LLUUID object_id; - LLUUID owner_id; - U8 flags; - - msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_SoundID, sound_id); - msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_id); - msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_OwnerID, owner_id); - msg->getF32Fast(_PREHASH_DataBlock, _PREHASH_Gain, gain); - msg->getU8Fast(_PREHASH_DataBlock, _PREHASH_Flags, flags); - - LLViewerObject *objectp = gObjectList.findObject(object_id); - if (!objectp) - { - // we don't know about this object, just bail - return; - } - - if (LLMuteList::getInstance()->isMuted(object_id)) return; - - if (LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagObjectSounds)) return; - - - // Don't play sounds from a region with maturity above current agent maturity - LLVector3d pos = objectp->getPositionGlobal(); - if( !gAgent.canAccessMaturityAtGlobal(pos) ) - { - return; - } - - objectp->setAttachedSound(sound_id, owner_id, gain, flags); -} - - -void process_attached_sound_gain_change(LLMessageSystem *mesgsys, void **user_data) -{ - F32 gain = 0; - LLUUID object_guid; - LLViewerObject *objectp = NULL; - - mesgsys->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_guid); - - if (!((objectp = gObjectList.findObject(object_guid)))) - { - // we don't know about this object, just bail - return; - } - - mesgsys->getF32Fast(_PREHASH_DataBlock, _PREHASH_Gain, gain); - - objectp->adjustAudioGain(gain); -} - - -void process_health_message(LLMessageSystem *mesgsys, void **user_data) -{ - F32 health; - - mesgsys->getF32Fast(_PREHASH_HealthData, _PREHASH_Health, health); - - if (gStatusBar) - { - gStatusBar->setHealth((S32)health); - } -} - - -void process_sim_stats(LLMessageSystem *msg, void **user_data) -{ - S32 count = msg->getNumberOfBlocks("Stat"); - for (S32 i = 0; i < count; ++i) - { - U32 stat_id; - F32 stat_value; - msg->getU32("Stat", "StatID", stat_id, i); - msg->getF32("Stat", "StatValue", stat_value, i); - switch (stat_id) - { - case LL_SIM_STAT_TIME_DILATION: - LLViewerStats::getInstance()->mSimTimeDilation.addValue(stat_value); - break; - case LL_SIM_STAT_FPS: - LLViewerStats::getInstance()->mSimFPS.addValue(stat_value); - break; - case LL_SIM_STAT_PHYSFPS: - LLViewerStats::getInstance()->mSimPhysicsFPS.addValue(stat_value); - break; - case LL_SIM_STAT_AGENTUPS: - LLViewerStats::getInstance()->mSimAgentUPS.addValue(stat_value); - break; - case LL_SIM_STAT_FRAMEMS: - LLViewerStats::getInstance()->mSimFrameMsec.addValue(stat_value); - break; - case LL_SIM_STAT_NETMS: - LLViewerStats::getInstance()->mSimNetMsec.addValue(stat_value); - break; - case LL_SIM_STAT_SIMOTHERMS: - LLViewerStats::getInstance()->mSimSimOtherMsec.addValue(stat_value); - break; - case LL_SIM_STAT_SIMPHYSICSMS: - LLViewerStats::getInstance()->mSimSimPhysicsMsec.addValue(stat_value); - break; - case LL_SIM_STAT_AGENTMS: - LLViewerStats::getInstance()->mSimAgentMsec.addValue(stat_value); - break; - case LL_SIM_STAT_IMAGESMS: - LLViewerStats::getInstance()->mSimImagesMsec.addValue(stat_value); - break; - case LL_SIM_STAT_SCRIPTMS: - LLViewerStats::getInstance()->mSimScriptMsec.addValue(stat_value); - break; - case LL_SIM_STAT_NUMTASKS: - LLViewerStats::getInstance()->mSimObjects.addValue(stat_value); - break; - case LL_SIM_STAT_NUMTASKSACTIVE: - LLViewerStats::getInstance()->mSimActiveObjects.addValue(stat_value); - break; - case LL_SIM_STAT_NUMAGENTMAIN: - LLViewerStats::getInstance()->mSimMainAgents.addValue(stat_value); - break; - case LL_SIM_STAT_NUMAGENTCHILD: - LLViewerStats::getInstance()->mSimChildAgents.addValue(stat_value); - break; - case LL_SIM_STAT_NUMSCRIPTSACTIVE: - LLViewerStats::getInstance()->mSimActiveScripts.addValue(stat_value); - break; - case LL_SIM_STAT_SCRIPT_EPS: - LLViewerStats::getInstance()->mSimScriptEPS.addValue(stat_value); - break; - case LL_SIM_STAT_INPPS: - LLViewerStats::getInstance()->mSimInPPS.addValue(stat_value); - break; - case LL_SIM_STAT_OUTPPS: - LLViewerStats::getInstance()->mSimOutPPS.addValue(stat_value); - break; - case LL_SIM_STAT_PENDING_DOWNLOADS: - LLViewerStats::getInstance()->mSimPendingDownloads.addValue(stat_value); - break; - case LL_SIM_STAT_PENDING_UPLOADS: - LLViewerStats::getInstance()->mSimPendingUploads.addValue(stat_value); - break; - case LL_SIM_STAT_PENDING_LOCAL_UPLOADS: - LLViewerStats::getInstance()->mSimPendingLocalUploads.addValue(stat_value); - break; - case LL_SIM_STAT_TOTAL_UNACKED_BYTES: - LLViewerStats::getInstance()->mSimTotalUnackedBytes.addValue(stat_value / 1024.f); - break; - case LL_SIM_STAT_PHYSICS_PINNED_TASKS: - LLViewerStats::getInstance()->mPhysicsPinnedTasks.addValue(stat_value); - break; - case LL_SIM_STAT_PHYSICS_LOD_TASKS: - LLViewerStats::getInstance()->mPhysicsLODTasks.addValue(stat_value); - break; - case LL_SIM_STAT_SIMPHYSICSSTEPMS: - LLViewerStats::getInstance()->mSimSimPhysicsStepMsec.addValue(stat_value); - break; - case LL_SIM_STAT_SIMPHYSICSSHAPEMS: - LLViewerStats::getInstance()->mSimSimPhysicsShapeUpdateMsec.addValue(stat_value); - break; - case LL_SIM_STAT_SIMPHYSICSOTHERMS: - LLViewerStats::getInstance()->mSimSimPhysicsOtherMsec.addValue(stat_value); - break; - case LL_SIM_STAT_SIMPHYSICSMEMORY: - LLViewerStats::getInstance()->mPhysicsMemoryAllocated.addValue(stat_value); - break; - case LL_SIM_STAT_SIMSPARETIME: - LLViewerStats::getInstance()->mSimSpareMsec.addValue(stat_value); - break; - case LL_SIM_STAT_SIMSLEEPTIME: - LLViewerStats::getInstance()->mSimSleepMsec.addValue(stat_value); - break; - case LL_SIM_STAT_IOPUMPTIME: - LLViewerStats::getInstance()->mSimPumpIOMsec.addValue(stat_value); - break; - default: - // Used to be a commented out warning. - LL_DEBUGS("Messaging") << "Unknown stat id" << stat_id << LL_ENDL; - break; - } - } - - /* - msg->getF32Fast(_PREHASH_Statistics, _PREHASH_PhysicsTimeDilation, time_dilation); - LLViewerStats::getInstance()->mSimTDStat.addValue(time_dilation); - - // Process information - // { CpuUsage F32 } - // { SimMemTotal F32 } - // { SimMemRSS F32 } - // { ProcessUptime F32 } - F32 cpu_usage; - F32 sim_mem_total; - F32 sim_mem_rss; - F32 process_uptime; - msg->getF32Fast(_PREHASH_Statistics, _PREHASH_CpuUsage, cpu_usage); - msg->getF32Fast(_PREHASH_Statistics, _PREHASH_SimMemTotal, sim_mem_total); - msg->getF32Fast(_PREHASH_Statistics, _PREHASH_SimMemRSS, sim_mem_rss); - msg->getF32Fast(_PREHASH_Statistics, _PREHASH_ProcessUptime, process_uptime); - LLViewerStats::getInstance()->mSimCPUUsageStat.addValue(cpu_usage); - LLViewerStats::getInstance()->mSimMemTotalStat.addValue(sim_mem_total); - LLViewerStats::getInstance()->mSimMemRSSStat.addValue(sim_mem_rss); - */ - - // - // Various hacks that aren't statistics, but are being handled here. - // - U32 max_tasks_per_region; - U32 region_flags; - msg->getU32("Region", "ObjectCapacity", max_tasks_per_region); - msg->getU32("Region", "RegionFlags", region_flags); - - LLViewerRegion* regionp = gAgent.getRegion(); - if (regionp) - { - BOOL was_flying = gAgent.getFlying(); - regionp->setRegionFlags(region_flags); - regionp->setMaxTasks(max_tasks_per_region); - // HACK: This makes agents drop from the sky if the region is - // set to no fly while people are still in the sim. - if (was_flying && regionp->getBlockFly()) - { - gAgent.setFlying(gAgent.canFly()); - } - } -} - - - -void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data) -{ - LLUUID animation_id; - LLUUID uuid; - S32 anim_sequence_id; - LLVOAvatar *avatarp; - - mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid); - - //clear animation flags - avatarp = (LLVOAvatar *)gObjectList.findObject(uuid); - - if (!avatarp) - { - // no agent by this ID...error? - LL_WARNS("Messaging") << "Received animation state for unknown avatar" << uuid << LL_ENDL; - return; - } - - S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList); - S32 num_source_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationSourceList); - - avatarp->mSignaledAnimations.clear(); - - if (avatarp->isSelf()) - { - LLUUID object_id; - - for( S32 i = 0; i < num_blocks; i++ ) - { - mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i); - mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i); - - LL_DEBUGS("Messaging") << "Anim sequence ID: " << anim_sequence_id << LL_ENDL; - - avatarp->mSignaledAnimations[animation_id] = anim_sequence_id; - - // *HACK: Disabling flying mode if it has been enabled shortly before the agent - // stand up animation is signaled. In this case we don't get a signal to start - // flying animation from server, the AGENT_CONTROL_FLY flag remains set but the - // avatar does not play flying animation, so we switch flying mode off. - // See LLAgent::setFlying(). This may cause "Stop Flying" button to blink. - // See EXT-2781. - if (animation_id == ANIM_AGENT_STANDUP && gAgent.getFlying()) - { - gAgent.setFlying(FALSE); - } - - if (i < num_source_blocks) - { - mesgsys->getUUIDFast(_PREHASH_AnimationSourceList, _PREHASH_ObjectID, object_id, i); - - LLViewerObject* object = gObjectList.findObject(object_id); - if (object) - { - object->mFlags |= FLAGS_ANIM_SOURCE; - - BOOL anim_found = FALSE; - LLVOAvatar::AnimSourceIterator anim_it = avatarp->mAnimationSources.find(object_id); - for (;anim_it != avatarp->mAnimationSources.end(); ++anim_it) - { - if (anim_it->second == animation_id) - { - anim_found = TRUE; - break; - } - } - - if (!anim_found) - { - avatarp->mAnimationSources.insert(LLVOAvatar::AnimationSourceMap::value_type(object_id, animation_id)); - } - } - } - } - } - else - { - for( S32 i = 0; i < num_blocks; i++ ) - { - mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i); - mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i); - avatarp->mSignaledAnimations[animation_id] = anim_sequence_id; - } - } - - if (num_blocks) - { - avatarp->processAnimationStateChanges(); - } -} - -void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data) -{ - LLUUID uuid; - mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid); - - LLVOAvatar* avatarp = (LLVOAvatar *)gObjectList.findObject(uuid); - if (avatarp) - { - avatarp->processAvatarAppearance( mesgsys ); - } - else - { - LL_WARNS("Messaging") << "avatar_appearance sent for unknown avatar " << uuid << LL_ENDL; - } -} - -void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data) -{ - LLVector4 cameraCollidePlane; - mesgsys->getVector4Fast(_PREHASH_CameraCollidePlane, _PREHASH_Plane, cameraCollidePlane); - - gAgentCamera.setCameraCollidePlane(cameraCollidePlane); -} - -void near_sit_object(BOOL success, void *data) -{ - if (success) - { - // Send message to sit on object - gMessageSystem->newMessageFast(_PREHASH_AgentSit); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - gAgent.sendReliableMessage(); - } -} - -void process_avatar_sit_response(LLMessageSystem *mesgsys, void **user_data) -{ - LLVector3 sitPosition; - LLQuaternion sitRotation; - LLUUID sitObjectID; - BOOL use_autopilot; - mesgsys->getUUIDFast(_PREHASH_SitObject, _PREHASH_ID, sitObjectID); - mesgsys->getBOOLFast(_PREHASH_SitTransform, _PREHASH_AutoPilot, use_autopilot); - mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_SitPosition, sitPosition); - mesgsys->getQuatFast(_PREHASH_SitTransform, _PREHASH_SitRotation, sitRotation); - LLVector3 camera_eye; - mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_CameraEyeOffset, camera_eye); - LLVector3 camera_at; - mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_CameraAtOffset, camera_at); - BOOL force_mouselook; - mesgsys->getBOOLFast(_PREHASH_SitTransform, _PREHASH_ForceMouselook, force_mouselook); - - if (isAgentAvatarValid() && dist_vec_squared(camera_eye, camera_at) > 0.0001f) - { - gAgentCamera.setSitCamera(sitObjectID, camera_eye, camera_at); - } - - gAgentCamera.setForceMouselook(force_mouselook); - // Forcing turning off flying here to prevent flying after pressing "Stand" - // to stand up from an object. See EXT-1655. - gAgent.setFlying(FALSE); - - LLViewerObject* object = gObjectList.findObject(sitObjectID); - if (object) - { - LLVector3 sit_spot = object->getPositionAgent() + (sitPosition * object->getRotation()); - if (!use_autopilot || isAgentAvatarValid() && gAgentAvatarp->isSitting() && gAgentAvatarp->getRoot() == object->getRoot()) - { - //we're already sitting on this object, so don't autopilot - } - else - { - gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(sit_spot), "Sit", &sitRotation, near_sit_object, NULL, 0.5f); - } - } - else - { - LL_WARNS("Messaging") << "Received sit approval for unknown object " << sitObjectID << LL_ENDL; - } -} - -void process_clear_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data) -{ - LLUUID source_id; - - mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, source_id); - - LLFollowCamMgr::removeFollowCamParams(source_id); -} - -void process_set_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data) -{ - S32 type; - F32 value; - bool settingPosition = false; - bool settingFocus = false; - bool settingFocusOffset = false; - LLVector3 position; - LLVector3 focus; - LLVector3 focus_offset; - - LLUUID source_id; - - mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, source_id); - - LLViewerObject* objectp = gObjectList.findObject(source_id); - if (objectp) - { - objectp->mFlags |= FLAGS_CAMERA_SOURCE; - } - - S32 num_objects = mesgsys->getNumberOfBlocks("CameraProperty"); - for (S32 block_index = 0; block_index < num_objects; block_index++) - { - mesgsys->getS32("CameraProperty", "Type", type, block_index); - mesgsys->getF32("CameraProperty", "Value", value, block_index); - switch(type) - { - case FOLLOWCAM_PITCH: - LLFollowCamMgr::setPitch(source_id, value); - break; - case FOLLOWCAM_FOCUS_OFFSET_X: - focus_offset.mV[VX] = value; - settingFocusOffset = true; - break; - case FOLLOWCAM_FOCUS_OFFSET_Y: - focus_offset.mV[VY] = value; - settingFocusOffset = true; - break; - case FOLLOWCAM_FOCUS_OFFSET_Z: - focus_offset.mV[VZ] = value; - settingFocusOffset = true; - break; - case FOLLOWCAM_POSITION_LAG: - LLFollowCamMgr::setPositionLag(source_id, value); - break; - case FOLLOWCAM_FOCUS_LAG: - LLFollowCamMgr::setFocusLag(source_id, value); - break; - case FOLLOWCAM_DISTANCE: - LLFollowCamMgr::setDistance(source_id, value); - break; - case FOLLOWCAM_BEHINDNESS_ANGLE: - LLFollowCamMgr::setBehindnessAngle(source_id, value); - break; - case FOLLOWCAM_BEHINDNESS_LAG: - LLFollowCamMgr::setBehindnessLag(source_id, value); - break; - case FOLLOWCAM_POSITION_THRESHOLD: - LLFollowCamMgr::setPositionThreshold(source_id, value); - break; - case FOLLOWCAM_FOCUS_THRESHOLD: - LLFollowCamMgr::setFocusThreshold(source_id, value); - break; - case FOLLOWCAM_ACTIVE: - //if 1, set using followcam,. - LLFollowCamMgr::setCameraActive(source_id, value != 0.f); - break; - case FOLLOWCAM_POSITION_X: - settingPosition = true; - position.mV[ 0 ] = value; - break; - case FOLLOWCAM_POSITION_Y: - settingPosition = true; - position.mV[ 1 ] = value; - break; - case FOLLOWCAM_POSITION_Z: - settingPosition = true; - position.mV[ 2 ] = value; - break; - case FOLLOWCAM_FOCUS_X: - settingFocus = true; - focus.mV[ 0 ] = value; - break; - case FOLLOWCAM_FOCUS_Y: - settingFocus = true; - focus.mV[ 1 ] = value; - break; - case FOLLOWCAM_FOCUS_Z: - settingFocus = true; - focus.mV[ 2 ] = value; - break; - case FOLLOWCAM_POSITION_LOCKED: - LLFollowCamMgr::setPositionLocked(source_id, value != 0.f); - break; - case FOLLOWCAM_FOCUS_LOCKED: - LLFollowCamMgr::setFocusLocked(source_id, value != 0.f); - break; - - default: - break; - } - } - - if ( settingPosition ) - { - LLFollowCamMgr::setPosition(source_id, position); - } - if ( settingFocus ) - { - LLFollowCamMgr::setFocus(source_id, focus); - } - if ( settingFocusOffset ) - { - LLFollowCamMgr::setFocusOffset(source_id, focus_offset); - } -} -//end Ventrella - - -// Culled from newsim lltask.cpp -void process_name_value(LLMessageSystem *mesgsys, void **user_data) -{ - std::string temp_str; - LLUUID id; - S32 i, num_blocks; - - mesgsys->getUUIDFast(_PREHASH_TaskData, _PREHASH_ID, id); - - LLViewerObject* object = gObjectList.findObject(id); - - if (object) - { - num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_NameValueData); - for (i = 0; i < num_blocks; i++) - { - mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, temp_str, i); - LL_INFOS("Messaging") << "Added to object Name Value: " << temp_str << LL_ENDL; - object->addNVPair(temp_str); - } - } - else - { - LL_INFOS("Messaging") << "Can't find object " << id << " to add name value pair" << LL_ENDL; - } -} - -void process_remove_name_value(LLMessageSystem *mesgsys, void **user_data) -{ - std::string temp_str; - LLUUID id; - S32 i, num_blocks; - - mesgsys->getUUIDFast(_PREHASH_TaskData, _PREHASH_ID, id); - - LLViewerObject* object = gObjectList.findObject(id); - - if (object) - { - num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_NameValueData); - for (i = 0; i < num_blocks; i++) - { - mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, temp_str, i); - LL_INFOS("Messaging") << "Removed from object Name Value: " << temp_str << LL_ENDL; - object->removeNVPair(temp_str); - } - } - else - { - LL_INFOS("Messaging") << "Can't find object " << id << " to remove name value pair" << LL_ENDL; - } -} - -void process_kick_user(LLMessageSystem *msg, void** /*user_data*/) -{ - std::string message; - - msg->getStringFast(_PREHASH_UserInfo, _PREHASH_Reason, message); - - LLAppViewer::instance()->forceDisconnect(message); -} - - -/* -void process_user_list_reply(LLMessageSystem *msg, void **user_data) -{ - LLUserList::processUserListReply(msg, user_data); - return; - char firstname[MAX_STRING+1]; - char lastname[MAX_STRING+1]; - U8 status; - S32 user_count; - - user_count = msg->getNumberOfBlocks("UserBlock"); - - for (S32 i = 0; i < user_count; i++) - { - msg->getData("UserBlock", i, "FirstName", firstname); - msg->getData("UserBlock", i, "LastName", lastname); - msg->getData("UserBlock", i, "Status", &status); - - if (status & 0x01) - { - dialog_friends_add_friend(buffer, TRUE); - } - else - { - dialog_friends_add_friend(buffer, FALSE); - } - } - - dialog_friends_done_adding(); -} -*/ - -// this is not handled in processUpdateMessage -/* -void process_time_dilation(LLMessageSystem *msg, void **user_data) -{ - // get the time_dilation - U16 foo; - msg->getData("TimeDilation", "TimeDilation", &foo); - F32 time_dilation = ((F32) foo) / 65535.f; - - // get the pointer to the right region - U32 ip = msg->getSenderIP(); - U32 port = msg->getSenderPort(); - LLViewerRegion *regionp = LLWorld::getInstance()->getRegion(ip, port); - if (regionp) - { - regionp->setTimeDilation(time_dilation); - } -} -*/ - - -void process_money_balance_reply( LLMessageSystem* msg, void** ) -{ - S32 balance = 0; - S32 credit = 0; - S32 committed = 0; - std::string desc; - LLUUID tid; - - msg->getUUID("MoneyData", "TransactionID", tid); - msg->getS32("MoneyData", "MoneyBalance", balance); - msg->getS32("MoneyData", "SquareMetersCredit", credit); - msg->getS32("MoneyData", "SquareMetersCommitted", committed); - msg->getStringFast(_PREHASH_MoneyData, _PREHASH_Description, desc); - LL_INFOS("Messaging") << "L$, credit, committed: " << balance << " " << credit << " " - << committed << LL_ENDL; - - if (gStatusBar) - { - gStatusBar->setBalance(balance); - gStatusBar->setLandCredit(credit); - gStatusBar->setLandCommitted(committed); - } - - if (desc.empty() - || !gSavedSettings.getBOOL("NotifyMoneyChange")) - { - // ...nothing to display - return; - } - - // Suppress duplicate messages about the same transaction - static std::deque recent; - if (std::find(recent.rbegin(), recent.rend(), tid) != recent.rend()) - { - return; - } - - // Once the 'recent' container gets large enough, chop some - // off the beginning. - const U32 MAX_LOOKBACK = 30; - const S32 POP_FRONT_SIZE = 12; - if(recent.size() > MAX_LOOKBACK) - { - LL_DEBUGS("Messaging") << "Removing oldest transaction records" << LL_ENDL; - recent.erase(recent.begin(), recent.begin() + POP_FRONT_SIZE); - } - //LL_DEBUGS("Messaging") << "Pushing back transaction " << tid << LL_ENDL; - recent.push_back(tid); - - if (msg->has("TransactionInfo")) - { - // ...message has extended info for localization - process_money_balance_reply_extended(msg); - } - else - { - // Only old dev grids will not supply the TransactionInfo block, - // so we can just use the hard-coded English string. - LLSD args; - args["MESSAGE"] = desc; - LLNotificationsUtil::add("SystemMessage", args); - } -} - -static std::string reason_from_transaction_type(S32 transaction_type, - const std::string& item_desc) -{ - // *NOTE: The keys for the reason strings are unusual because - // an earlier version of the code used English language strings - // extracted from hard-coded server English descriptions. - // Keeping them so we don't have to re-localize them. - switch (transaction_type) - { - case TRANS_OBJECT_SALE: - { - LLStringUtil::format_map_t arg; - arg["ITEM"] = item_desc; - return LLTrans::getString("for item", arg); - } - case TRANS_LAND_SALE: - return LLTrans::getString("for a parcel of land"); - - case TRANS_LAND_PASS_SALE: - return LLTrans::getString("for a land access pass"); - - case TRANS_GROUP_LAND_DEED: - return LLTrans::getString("for deeding land"); - - case TRANS_GROUP_CREATE: - return LLTrans::getString("to create a group"); - - case TRANS_GROUP_JOIN: - return LLTrans::getString("to join a group"); - - case TRANS_UPLOAD_CHARGE: - return LLTrans::getString("to upload"); - - case TRANS_CLASSIFIED_CHARGE: - return LLTrans::getString("to publish a classified ad"); - - // These have no reason to display, but are expected and should not - // generate warnings - case TRANS_GIFT: - case TRANS_PAY_OBJECT: - case TRANS_OBJECT_PAYS: - return std::string(); - - default: - llwarns << "Unknown transaction type " - << transaction_type << llendl; - return std::string(); - } -} - -static void money_balance_group_notify(const LLUUID& group_id, - const std::string& name, - bool is_group, - std::string notification, - LLSD args, - LLSD payload) -{ - // Message uses name SLURLs, don't actually have to substitute in - // the name. We're just making sure it's available. - // Notification is either PaymentReceived or PaymentSent - LLNotificationsUtil::add(notification, args, payload); -} - -static void money_balance_avatar_notify(const LLUUID& agent_id, - const LLAvatarName& av_name, - std::string notification, - LLSD args, - LLSD payload) -{ - // Message uses name SLURLs, don't actually have to substitute in - // the name. We're just making sure it's available. - // Notification is either PaymentReceived or PaymentSent - LLNotificationsUtil::add(notification, args, payload); -} - -static void process_money_balance_reply_extended(LLMessageSystem* msg) -{ - // Added in server 1.40 and viewer 2.1, support for localization - // and agent ids for name lookup. - S32 transaction_type = 0; - LLUUID source_id; - BOOL is_source_group = FALSE; - LLUUID dest_id; - BOOL is_dest_group = FALSE; - S32 amount = 0; - std::string item_description; - - msg->getS32("TransactionInfo", "TransactionType", transaction_type); - msg->getUUID("TransactionInfo", "SourceID", source_id); - msg->getBOOL("TransactionInfo", "IsSourceGroup", is_source_group); - msg->getUUID("TransactionInfo", "DestID", dest_id); - msg->getBOOL("TransactionInfo", "IsDestGroup", is_dest_group); - msg->getS32("TransactionInfo", "Amount", amount); - msg->getString("TransactionInfo", "ItemDescription", item_description); - LL_INFOS("Money") << "MoneyBalanceReply source " << source_id - << " dest " << dest_id - << " type " << transaction_type - << " item " << item_description << LL_ENDL; - - if (source_id.isNull() && dest_id.isNull()) - { - // this is a pure balance update, no notification required - return; - } - - std::string source_slurl; - if (is_source_group) - { - source_slurl = - LLSLURL( "group", source_id, "inspect").getSLURLString(); - } - else - { - source_slurl = - LLSLURL( "agent", source_id, "completename").getSLURLString(); - } - - std::string dest_slurl; - if (is_dest_group) - { - dest_slurl = - LLSLURL( "group", dest_id, "inspect").getSLURLString(); - } - else - { - dest_slurl = - LLSLURL( "agent", dest_id, "completename").getSLURLString(); - } - - std::string reason = - reason_from_transaction_type(transaction_type, item_description); - - LLStringUtil::format_map_t args; - args["REASON"] = reason; // could be empty - args["AMOUNT"] = llformat("%d", amount); - - // Need to delay until name looked up, so need to know whether or not - // is group - bool is_name_group = false; - LLUUID name_id; - std::string message; - std::string notification; - LLSD final_args; - LLSD payload; - - bool you_paid_someone = (source_id == gAgentID); - if (you_paid_someone) - { - args["NAME"] = dest_slurl; - is_name_group = is_dest_group; - name_id = dest_id; - if (!reason.empty()) - { - if (dest_id.notNull()) - { - message = LLTrans::getString("you_paid_ldollars", args); - } - else - { - // transaction fee to the system, eg, to create a group - message = LLTrans::getString("you_paid_ldollars_no_name", args); - } - } - else - { - if (dest_id.notNull()) - { - message = LLTrans::getString("you_paid_ldollars_no_reason", args); - } - else - { - // no target, no reason, you just paid money - message = LLTrans::getString("you_paid_ldollars_no_info", args); - } - } - final_args["MESSAGE"] = message; - notification = "PaymentSent"; - } - else { - // ...someone paid you - args["NAME"] = source_slurl; - is_name_group = is_source_group; - name_id = source_id; - if (!reason.empty()) - { - message = LLTrans::getString("paid_you_ldollars", args); - } - else { - message = LLTrans::getString("paid_you_ldollars_no_reason", args); - } - final_args["MESSAGE"] = message; - - // make notification loggable - payload["from_id"] = source_id; - notification = "PaymentReceived"; - } - - // Despite using SLURLs, wait until the name is available before - // showing the notification, otherwise the UI layout is strange and - // the user sees a "Loading..." message - if (is_name_group) - { - gCacheName->getGroup(name_id, - boost::bind(&money_balance_group_notify, - _1, _2, _3, - notification, final_args, payload)); - } - else { - LLAvatarNameCache::get(name_id, - boost::bind(&money_balance_avatar_notify, - _1, _2, - notification, final_args, payload)); - } -} - - - -bool handle_special_notification_callback(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - - if (0 == option) - { - // set the preference to the maturity of the region we're calling - int preferredMaturity = notification["payload"]["_region_access"].asInteger(); - gSavedSettings.setU32("PreferredMaturity", preferredMaturity); - gAgent.sendMaturityPreferenceToServer(preferredMaturity); - - // notify user that the maturity preference has been changed - LLSD args; - args["RATING"] = LLViewerRegion::accessToString(preferredMaturity); - LLNotificationsUtil::add("PreferredMaturityChanged", args); - } - - return false; -} - -// some of the server notifications need special handling. This is where we do that. -bool handle_special_notification(std::string notificationID, LLSD& llsdBlock) -{ - int regionAccess = llsdBlock["_region_access"].asInteger(); - llsdBlock["REGIONMATURITY"] = LLViewerRegion::accessToString(regionAccess); - - // we're going to throw the LLSD in there in case anyone ever wants to use it - LLNotificationsUtil::add(notificationID+"_Notify", llsdBlock); - - if (regionAccess == SIM_ACCESS_MATURE) - { - if (gAgent.isTeen()) - { - LLNotificationsUtil::add(notificationID+"_KB", llsdBlock); - return true; - } - else if (gAgent.prefersPG()) - { - LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_special_notification_callback); - return true; - } - } - else if (regionAccess == SIM_ACCESS_ADULT) - { - if (!gAgent.isAdult()) - { - LLNotificationsUtil::add(notificationID+"_KB", llsdBlock); - return true; - } - else if (gAgent.prefersPG() || gAgent.prefersMature()) - { - LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_special_notification_callback); - return true; - } - } - return false; -} - -bool attempt_standard_notification(LLMessageSystem* msgsystem) -{ - // if we have additional alert data - if (msgsystem->has(_PREHASH_AlertInfo) && msgsystem->getNumberOfBlocksFast(_PREHASH_AlertInfo) > 0) - { - // notification was specified using the new mechanism, so we can just handle it here - std::string notificationID; - std::string llsdRaw; - LLSD llsdBlock; - msgsystem->getStringFast(_PREHASH_AlertInfo, _PREHASH_Message, notificationID); - msgsystem->getStringFast(_PREHASH_AlertInfo, _PREHASH_ExtraParams, llsdRaw); - if (llsdRaw.length()) - { - std::istringstream llsdData(llsdRaw); - if (!LLSDSerialize::deserialize(llsdBlock, llsdData, llsdRaw.length())) - { - llwarns << "attempt_standard_notification: Attempted to read notification parameter data into LLSD but failed:" << llsdRaw << llendl; - } - } - - if ( - (notificationID == "RegionEntryAccessBlocked") || - (notificationID == "LandClaimAccessBlocked") || - (notificationID == "LandBuyAccessBlocked") - ) - { - /*--------------------------------------------------------------------- - (Commented so a grep will find the notification strings, since - we construct them on the fly; if you add additional notifications, - please update the comment.) - - Could throw any of the following notifications: - - RegionEntryAccessBlocked - RegionEntryAccessBlocked_Notify - RegionEntryAccessBlocked_Change - RegionEntryAccessBlocked_KB - LandClaimAccessBlocked - LandClaimAccessBlocked_Notify - LandClaimAccessBlocked_Change - LandClaimAccessBlocked_KB - LandBuyAccessBlocked - LandBuyAccessBlocked_Notify - LandBuyAccessBlocked_Change - LandBuyAccessBlocked_KB - - -----------------------------------------------------------------------*/ - if (handle_special_notification(notificationID, llsdBlock)) - { - return true; - } - } - - LLNotificationsUtil::add(notificationID, llsdBlock); - return true; - } - return false; -} - - -void process_agent_alert_message(LLMessageSystem* msgsystem, void** user_data) -{ - // make sure the cursor is back to the usual default since the - // alert is probably due to some kind of error. - gViewerWindow->getWindow()->resetBusyCount(); - - if (!attempt_standard_notification(msgsystem)) - { - BOOL modal = FALSE; - msgsystem->getBOOL("AlertData", "Modal", modal); - std::string buffer; - msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, buffer); - process_alert_core(buffer, modal); - } -} - -// The only difference between this routine and the previous is the fact that -// for this routine, the modal parameter is always false. Sadly, for the message -// handled by this routine, there is no "Modal" parameter on the message, and -// there's no API to tell if a message has the given parameter or not. -// So we can't handle the messages with the same handler. -void process_alert_message(LLMessageSystem *msgsystem, void **user_data) -{ - // make sure the cursor is back to the usual default since the - // alert is probably due to some kind of error. - gViewerWindow->getWindow()->resetBusyCount(); - - if (!attempt_standard_notification(msgsystem)) - { - BOOL modal = FALSE; - std::string buffer; - msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, buffer); - process_alert_core(buffer, modal); - } -} - -void process_alert_core(const std::string& message, BOOL modal) -{ - // HACK -- handle callbacks for specific alerts. It also is localized in notifications.xml - if ( message == "You died and have been teleported to your home location") - { - LLViewerStats::getInstance()->incStat(LLViewerStats::ST_KILLED_COUNT); - } - else if( message == "Home position set." ) - { - // save the home location image to disk - std::string snap_filename = gDirUtilp->getLindenUserDir(); - snap_filename += gDirUtilp->getDirDelimiter(); - snap_filename += SCREEN_HOME_FILENAME; - gViewerWindow->saveSnapshot(snap_filename, gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw(), FALSE, FALSE); - } - - const std::string ALERT_PREFIX("ALERT: "); - const std::string NOTIFY_PREFIX("NOTIFY: "); - if (message.find(ALERT_PREFIX) == 0) - { - // Allow the server to spawn a named alert so that server alerts can be - // translated out of English. - std::string alert_name(message.substr(ALERT_PREFIX.length())); - LLNotificationsUtil::add(alert_name); - } - else if (message.find(NOTIFY_PREFIX) == 0) - { - // Allow the server to spawn a named notification so that server notifications can be - // translated out of English. - std::string notify_name(message.substr(NOTIFY_PREFIX.length())); - LLNotificationsUtil::add(notify_name); - } - else if (message[0] == '/') - { - // System message is important, show in upper-right box not tip - std::string text(message.substr(1)); - LLSD args; - if (text.substr(0,17) == "RESTART_X_MINUTES") - { - S32 mins = 0; - LLStringUtil::convertToS32(text.substr(18), mins); - args["MINUTES"] = llformat("%d",mins); - LLNotificationsUtil::add("RegionRestartMinutes", args); - } - else if (text.substr(0,17) == "RESTART_X_SECONDS") - { - S32 secs = 0; - LLStringUtil::convertToS32(text.substr(18), secs); - args["SECONDS"] = llformat("%d",secs); - LLNotificationsUtil::add("RegionRestartSeconds", args); - } - else - { - std::string new_msg =LLNotifications::instance().getGlobalString(text); - args["MESSAGE"] = new_msg; - LLNotificationsUtil::add("SystemMessage", args); - } - } - else if (modal) - { - LLSD args; - std::string new_msg =LLNotifications::instance().getGlobalString(message); - args["ERROR_MESSAGE"] = new_msg; - LLNotificationsUtil::add("ErrorMessage", args); - } - else - { - LLSD args; - std::string new_msg =LLNotifications::instance().getGlobalString(message); - args["MESSAGE"] = new_msg; - LLNotificationsUtil::add("SystemMessageTip", args); - } -} - -mean_collision_list_t gMeanCollisionList; -time_t gLastDisplayedTime = 0; - -void handle_show_mean_events(void *) -{ - if (gNoRender) - { - return; - } - LLFloaterReg::showInstance("bumps"); - //LLFloaterBump::showInstance(); -} - -void mean_name_callback(const LLUUID &id, const std::string& full_name, bool is_group) -{ - if (gNoRender) - { - return; - } - - static const U32 max_collision_list_size = 20; - if (gMeanCollisionList.size() > max_collision_list_size) - { - mean_collision_list_t::iterator iter = gMeanCollisionList.begin(); - for (U32 i=0; imPerp == id) - { - mcd->mFullName = full_name; - } - } -} - -void process_mean_collision_alert_message(LLMessageSystem *msgsystem, void **user_data) -{ - if (gAgent.inPrelude()) - { - // In prelude, bumping is OK. This dialog is rather confusing to - // newbies, so we don't show it. Drop the packet on the floor. - return; - } - - // make sure the cursor is back to the usual default since the - // alert is probably due to some kind of error. - gViewerWindow->getWindow()->resetBusyCount(); - - LLUUID perp; - U32 time; - U8 u8type; - EMeanCollisionType type; - F32 mag; - - S32 i, num = msgsystem->getNumberOfBlocks(_PREHASH_MeanCollision); - - for (i = 0; i < num; i++) - { - msgsystem->getUUIDFast(_PREHASH_MeanCollision, _PREHASH_Perp, perp); - msgsystem->getU32Fast(_PREHASH_MeanCollision, _PREHASH_Time, time); - msgsystem->getF32Fast(_PREHASH_MeanCollision, _PREHASH_Mag, mag); - msgsystem->getU8Fast(_PREHASH_MeanCollision, _PREHASH_Type, u8type); - - type = (EMeanCollisionType)u8type; - - BOOL b_found = FALSE; - - for (mean_collision_list_t::iterator iter = gMeanCollisionList.begin(); - iter != gMeanCollisionList.end(); ++iter) - { - LLMeanCollisionData *mcd = *iter; - if ((mcd->mPerp == perp) && (mcd->mType == type)) - { - mcd->mTime = time; - mcd->mMag = mag; - b_found = TRUE; - break; - } - } - - if (!b_found) - { - LLMeanCollisionData *mcd = new LLMeanCollisionData(gAgentID, perp, time, type, mag); - gMeanCollisionList.push_front(mcd); - gCacheName->get(perp, false, boost::bind(&mean_name_callback, _1, _2, _3)); - } - } -} - -void process_frozen_message(LLMessageSystem *msgsystem, void **user_data) -{ - // make sure the cursor is back to the usual default since the - // alert is probably due to some kind of error. - gViewerWindow->getWindow()->resetBusyCount(); - BOOL b_frozen; - - msgsystem->getBOOL("FrozenData", "Data", b_frozen); - - // TODO: make being frozen change view - if (b_frozen) - { - } - else - { - } -} - -// do some extra stuff once we get our economy data -void process_economy_data(LLMessageSystem *msg, void** /*user_data*/) -{ - LLGlobalEconomy::processEconomyData(msg, LLGlobalEconomy::Singleton::getInstance()); - - S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); - - LL_INFOS_ONCE("Messaging") << "EconomyData message arrived; upload cost is L$" << upload_cost << LL_ENDL; - - gMenuHolder->getChild("Upload Image")->setLabelArg("[COST]", llformat("%d", upload_cost)); - gMenuHolder->getChild("Upload Sound")->setLabelArg("[COST]", llformat("%d", upload_cost)); - gMenuHolder->getChild("Upload Animation")->setLabelArg("[COST]", llformat("%d", upload_cost)); - gMenuHolder->getChild("Bulk Upload")->setLabelArg("[COST]", llformat("%d", upload_cost)); -} - -void notify_cautioned_script_question(const LLSD& notification, const LLSD& response, S32 orig_questions, BOOL granted) -{ - // only continue if at least some permissions were requested - if (orig_questions) - { - // check to see if the person we are asking - - // "'[OBJECTNAME]', an object owned by '[OWNERNAME]', - // located in [REGIONNAME] at [REGIONPOS], - // has been permission to: [PERMISSIONS]." - - LLUIString notice(LLTrans::getString(granted ? "ScriptQuestionCautionChatGranted" : "ScriptQuestionCautionChatDenied")); - - // always include the object name and owner name - notice.setArg("[OBJECTNAME]", notification["payload"]["object_name"].asString()); - notice.setArg("[OWNERNAME]", notification["payload"]["owner_name"].asString()); - - // try to lookup viewerobject that corresponds to the object that - // requested permissions (here, taskid->requesting object id) - BOOL foundpos = FALSE; - LLViewerObject* viewobj = gObjectList.findObject(notification["payload"]["task_id"].asUUID()); - if (viewobj) - { - // found the viewerobject, get it's position in its region - LLVector3 objpos(viewobj->getPosition()); - - // try to lookup the name of the region the object is in - LLViewerRegion* viewregion = viewobj->getRegion(); - if (viewregion) - { - // got the region, so include the region and 3d coordinates of the object - notice.setArg("[REGIONNAME]", viewregion->getName()); - std::string formatpos = llformat("%.1f, %.1f,%.1f", objpos[VX], objpos[VY], objpos[VZ]); - notice.setArg("[REGIONPOS]", formatpos); - - foundpos = TRUE; - } - } - - if (!foundpos) - { - // unable to determine location of the object - notice.setArg("[REGIONNAME]", "(unknown region)"); - notice.setArg("[REGIONPOS]", "(unknown position)"); - } - - // check each permission that was requested, and list each - // permission that has been flagged as a caution permission - BOOL caution = FALSE; - S32 count = 0; - std::string perms; - for (S32 i = 0; i < SCRIPT_PERMISSION_EOF; i++) - { - if ((orig_questions & LSCRIPTRunTimePermissionBits[i]) && SCRIPT_QUESTION_IS_CAUTION[i]) - { - count++; - caution = TRUE; - - // add a comma before the permission description if it is not the first permission - // added to the list or the last permission to check - if ((count > 1) && (i < SCRIPT_PERMISSION_EOF)) - { - perms.append(", "); - } - - perms.append(LLTrans::getString(SCRIPT_QUESTIONS[i])); - } - } - - notice.setArg("[PERMISSIONS]", perms); - - // log a chat message as long as at least one requested permission - // is a caution permission - if (caution) - { - LLChat chat(notice.getString()); - // LLFloaterChat::addChat(chat, FALSE, FALSE); - } - } -} - -bool script_question_cb(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - LLMessageSystem *msg = gMessageSystem; - S32 orig = notification["payload"]["questions"].asInteger(); - S32 new_questions = orig; - - // check whether permissions were granted or denied - BOOL allowed = TRUE; - // the "yes/accept" button is the first button in the template, making it button 0 - // if any other button was clicked, the permissions were denied - if (option != 0) - { - new_questions = 0; - allowed = FALSE; - } - - LLUUID task_id = notification["payload"]["task_id"].asUUID(); - LLUUID item_id = notification["payload"]["item_id"].asUUID(); - - // reply with the permissions granted or denied - msg->newMessageFast(_PREHASH_ScriptAnswerYes); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_Data); - msg->addUUIDFast(_PREHASH_TaskID, task_id); - msg->addUUIDFast(_PREHASH_ItemID, item_id); - msg->addS32Fast(_PREHASH_Questions, new_questions); - msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); - - // only log a chat message if caution prompts are enabled - if (gSavedSettings.getBOOL("PermissionsCautionEnabled")) - { - // log a chat message, if appropriate - notify_cautioned_script_question(notification, response, orig, allowed); - } - - if ( response["Mute"] ) // mute - { - LLMuteList::getInstance()->add(LLMute(item_id, notification["payload"]["object_name"].asString(), LLMute::OBJECT)); - - // purge the message queue of any previously queued requests from the same source. DEV-4879 - class OfferMatcher : public LLNotificationsUI::LLScreenChannel::Matcher - { - public: - OfferMatcher(const LLUUID& to_block) : blocked_id(to_block) {} - bool matches(const LLNotificationPtr notification) const - { - if (notification->getName() == "ScriptQuestionCaution" - || notification->getName() == "ScriptQuestion") - { - return (notification->getPayload()["item_id"].asUUID() == blocked_id); - } - return false; - } - private: - const LLUUID& blocked_id; - }; - - LLNotificationsUI::LLChannelManager::getInstance()->killToastsFromChannel(LLUUID( - gSavedSettings.getString("NotificationChannelUUID")), OfferMatcher(item_id)); - } - - if (response["Details"]) - { - // respawn notification... - LLNotificationsUtil::add(notification["name"], notification["substitutions"], notification["payload"]); - - // ...with description on top - LLNotificationsUtil::add("DebitPermissionDetails"); - } - return false; -} -static LLNotificationFunctorRegistration script_question_cb_reg_1("ScriptQuestion", script_question_cb); -static LLNotificationFunctorRegistration script_question_cb_reg_2("ScriptQuestionCaution", script_question_cb); - -void process_script_question(LLMessageSystem *msg, void **user_data) -{ - // *TODO: Translate owner name -> [FIRST] [LAST] - - LLHost sender = msg->getSender(); - - LLUUID taskid; - LLUUID itemid; - S32 questions; - std::string object_name; - std::string owner_name; - - // taskid -> object key of object requesting permissions - msg->getUUIDFast(_PREHASH_Data, _PREHASH_TaskID, taskid ); - // itemid -> script asset key of script requesting permissions - msg->getUUIDFast(_PREHASH_Data, _PREHASH_ItemID, itemid ); - msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectName, object_name); - msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectOwner, owner_name); - msg->getS32Fast(_PREHASH_Data, _PREHASH_Questions, questions ); - - // Special case. If the objects are owned by this agent, throttle per-object instead - // of per-owner. It's common for residents to reset a ton of scripts that re-request - // permissions, as with tier boxes. UUIDs can't be valid agent names and vice-versa, - // so we'll reuse the same namespace for both throttle types. - std::string throttle_name = owner_name; - std::string self_name; - LLAgentUI::buildFullname( self_name ); - if( owner_name == self_name ) - { - throttle_name = taskid.getString(); - } - - // don't display permission requests if this object is muted - if (LLMuteList::getInstance()->isMuted(taskid)) return; - - // throttle excessive requests from any specific user's scripts - typedef LLKeyThrottle LLStringThrottle; - static LLStringThrottle question_throttle( LLREQUEST_PERMISSION_THROTTLE_LIMIT, LLREQUEST_PERMISSION_THROTTLE_INTERVAL ); - - switch (question_throttle.noteAction(throttle_name)) - { - case LLStringThrottle::THROTTLE_NEWLY_BLOCKED: - LL_INFOS("Messaging") << "process_script_question throttled" - << " owner_name:" << owner_name - << LL_ENDL; - // Fall through - - case LLStringThrottle::THROTTLE_BLOCKED: - // Escape altogether until we recover - return; - - case LLStringThrottle::THROTTLE_OK: - break; - } - - std::string script_question; - if (questions) - { - BOOL caution = FALSE; - S32 count = 0; - LLSD args; - args["OBJECTNAME"] = object_name; - args["NAME"] = LLCacheName::cleanFullName(owner_name); - - // check the received permission flags against each permission - for (S32 i = 0; i < SCRIPT_PERMISSION_EOF; i++) - { - if (questions & LSCRIPTRunTimePermissionBits[i]) - { - count++; - script_question += " " + LLTrans::getString(SCRIPT_QUESTIONS[i]) + "\n"; - - // check whether permission question should cause special caution dialog - caution |= (SCRIPT_QUESTION_IS_CAUTION[i]); - } - } - args["QUESTIONS"] = script_question; - - LLSD payload; - payload["task_id"] = taskid; - payload["item_id"] = itemid; - payload["sender"] = sender.getIPandPort(); - payload["questions"] = questions; - payload["object_name"] = object_name; - payload["owner_name"] = owner_name; - - // check whether cautions are even enabled or not - if (gSavedSettings.getBOOL("PermissionsCautionEnabled")) - { - // display the caution permissions prompt - LLNotificationsUtil::add(caution ? "ScriptQuestionCaution" : "ScriptQuestion", args, payload); - } - else - { - // fall back to default behavior if cautions are entirely disabled - LLNotificationsUtil::add("ScriptQuestion", args, payload); - } - - } -} - - -void process_derez_container(LLMessageSystem *msg, void**) -{ - LL_WARNS("Messaging") << "call to deprecated process_derez_container" << LL_ENDL; -} - -void container_inventory_arrived(LLViewerObject* object, - LLInventoryObject::object_list_t* inventory, - S32 serial_num, - void* data) -{ - LL_DEBUGS("Messaging") << "container_inventory_arrived()" << LL_ENDL; - if( gAgentCamera.cameraMouselook() ) - { - gAgentCamera.changeCameraToDefault(); - } - - LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); - - if (inventory->size() > 2) - { - // create a new inventory category to put this in - LLUUID cat_id; - cat_id = gInventory.createNewCategory(gInventory.getRootFolderID(), - LLFolderType::FT_NONE, - LLTrans::getString("AcquiredItems")); - - LLInventoryObject::object_list_t::const_iterator it = inventory->begin(); - LLInventoryObject::object_list_t::const_iterator end = inventory->end(); - for ( ; it != end; ++it) - { - if ((*it)->getType() != LLAssetType::AT_CATEGORY) - { - LLInventoryObject* obj = (LLInventoryObject*)(*it); - LLInventoryItem* item = (LLInventoryItem*)(obj); - LLUUID item_id; - item_id.generate(); - time_t creation_date_utc = time_corrected(); - LLPointer new_item - = new LLViewerInventoryItem(item_id, - cat_id, - item->getPermissions(), - item->getAssetUUID(), - item->getType(), - item->getInventoryType(), - item->getName(), - item->getDescription(), - LLSaleInfo::DEFAULT, - item->getFlags(), - creation_date_utc); - new_item->updateServer(TRUE); - gInventory.updateItem(new_item); - } - } - gInventory.notifyObservers(); - if(active_panel) - { - active_panel->setSelection(cat_id, TAKE_FOCUS_NO); - } - } - else if (inventory->size() == 2) - { - // we're going to get one fake root category as well as the - // one actual object - LLInventoryObject::object_list_t::iterator it = inventory->begin(); - - if ((*it)->getType() == LLAssetType::AT_CATEGORY) - { - ++it; - } - - LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it)); - const LLUUID category = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(item->getType())); - - LLUUID item_id; - item_id.generate(); - time_t creation_date_utc = time_corrected(); - LLPointer new_item - = new LLViewerInventoryItem(item_id, category, - item->getPermissions(), - item->getAssetUUID(), - item->getType(), - item->getInventoryType(), - item->getName(), - item->getDescription(), - LLSaleInfo::DEFAULT, - item->getFlags(), - creation_date_utc); - new_item->updateServer(TRUE); - gInventory.updateItem(new_item); - gInventory.notifyObservers(); - if(active_panel) - { - active_panel->setSelection(item_id, TAKE_FOCUS_NO); - } - } - - // we've got the inventory, now delete this object if this was a take - BOOL delete_object = (BOOL)(intptr_t)data; - LLViewerRegion *region = gAgent.getRegion(); - if (delete_object && region) - { - gMessageSystem->newMessageFast(_PREHASH_ObjectDelete); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - const U8 NO_FORCE = 0; - gMessageSystem->addU8Fast(_PREHASH_Force, NO_FORCE); - gMessageSystem->nextBlockFast(_PREHASH_ObjectData); - gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID()); - gMessageSystem->sendReliable(region->getHost()); - } -} - -// method to format the time. -std::string formatted_time(const time_t& the_time) -{ - std::string dateStr = "["+LLTrans::getString("LTimeWeek")+"] [" - +LLTrans::getString("LTimeMonth")+"] [" - +LLTrans::getString("LTimeDay")+"] [" - +LLTrans::getString("LTimeHour")+"]:[" - +LLTrans::getString("LTimeMin")+"]:[" - +LLTrans::getString("LTimeSec")+"] [" - +LLTrans::getString("LTimeYear")+"]"; - - LLSD substitution; - substitution["datetime"] = (S32) the_time; - LLStringUtil::format (dateStr, substitution); - return dateStr; -} - - -void process_teleport_failed(LLMessageSystem *msg, void**) -{ - std::string reason; - std::string big_reason; - LLSD args; - - // if we have additional alert data - if (msg->has(_PREHASH_AlertInfo) && msg->getSizeFast(_PREHASH_AlertInfo, _PREHASH_Message) > 0) - { - // Get the message ID - msg->getStringFast(_PREHASH_AlertInfo, _PREHASH_Message, reason); - big_reason = LLAgent::sTeleportErrorMessages[reason]; - if ( big_reason.size() > 0 ) - { // Substitute verbose reason from the local map - args["REASON"] = big_reason; - } - else - { // Nothing found in the map - use what the server returned in the original message block - msg->getStringFast(_PREHASH_Info, _PREHASH_Reason, reason); - args["REASON"] = reason; - } - - LLSD llsd_block; - std::string llsd_raw; - msg->getStringFast(_PREHASH_AlertInfo, _PREHASH_ExtraParams, llsd_raw); - if (llsd_raw.length()) - { - std::istringstream llsd_data(llsd_raw); - if (!LLSDSerialize::deserialize(llsd_block, llsd_data, llsd_raw.length())) - { - llwarns << "process_teleport_failed: Attempted to read alert parameter data into LLSD but failed:" << llsd_raw << llendl; - } - else - { - // change notification name in this special case - if (handle_special_notification("RegionEntryAccessBlocked", llsd_block)) - { - if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) - { - gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); - } - return; - } - } - } - - } - else - { - msg->getStringFast(_PREHASH_Info, _PREHASH_Reason, reason); - - big_reason = LLAgent::sTeleportErrorMessages[reason]; - if ( big_reason.size() > 0 ) - { // Substitute verbose reason from the local map - args["REASON"] = big_reason; - } - else - { // Nothing found in the map - use what the server returned - args["REASON"] = reason; - } - } - - LLNotificationsUtil::add("CouldNotTeleportReason", args); - - // Let the interested parties know that teleport failed. - LLViewerParcelMgr::getInstance()->onTeleportFailed(); - - if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) - { - gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); - } -} - -void process_teleport_local(LLMessageSystem *msg,void**) -{ - LLUUID agent_id; - msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id); - if (agent_id != gAgent.getID()) - { - LL_WARNS("Messaging") << "Got teleport notification for wrong agent!" << LL_ENDL; - return; - } - - U32 location_id; - LLVector3 pos, look_at; - U32 teleport_flags; - msg->getU32Fast(_PREHASH_Info, _PREHASH_LocationID, location_id); - msg->getVector3Fast(_PREHASH_Info, _PREHASH_Position, pos); - msg->getVector3Fast(_PREHASH_Info, _PREHASH_LookAt, look_at); - msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags); - - if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) - { - if( gAgent.getTeleportState() == LLAgent::TELEPORT_LOCAL ) - { - // To prevent TeleportStart messages re-activating the progress screen right - // after tp, keep the teleport state and let progress screen clear it after a short delay - // (progress screen is active but not visible) *TODO: remove when SVC-5290 is fixed - gTeleportDisplayTimer.reset(); - gTeleportDisplay = TRUE; - } - else - { - gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); - } - } - - // Sim tells us whether the new position is off the ground - if (teleport_flags & TELEPORT_FLAGS_IS_FLYING) - { - gAgent.setFlying(TRUE); - } - else - { - gAgent.setFlying(FALSE); - } - - gAgent.setPositionAgent(pos); - gAgentCamera.slamLookAt(look_at); - - if ( !(gAgent.getTeleportKeepsLookAt() && LLViewerJoystick::getInstance()->getOverrideCamera()) ) - { - gAgentCamera.resetView(TRUE, TRUE); - } - - // send camera update to new region - gAgentCamera.updateCamera(); - - send_agent_update(TRUE, TRUE); - - // Let the interested parties know we've teleported. - // Vadim *HACK: Agent position seems to get reset (to render position?) - // on each frame, so we have to pass the new position manually. - LLViewerParcelMgr::getInstance()->onTeleportFinished(true, gAgent.getPosGlobalFromAgent(pos)); -} - -void send_simple_im(const LLUUID& to_id, - const std::string& message, - EInstantMessage dialog, - const LLUUID& id) -{ - std::string my_name; - LLAgentUI::buildFullname(my_name); - send_improved_im(to_id, - my_name, - message, - IM_ONLINE, - dialog, - id, - NO_TIMESTAMP, - (U8*)EMPTY_BINARY_BUCKET, - EMPTY_BINARY_BUCKET_SIZE); -} - -void send_group_notice(const LLUUID& group_id, - const std::string& subject, - const std::string& message, - const LLInventoryItem* item) -{ - // Put this notice into an instant message form. - // This will mean converting the item to a binary bucket, - // and the subject/message into a single field. - std::string my_name; - LLAgentUI::buildFullname(my_name); - - // Combine subject + message into a single string. - std::ostringstream subject_and_message; - // TODO: turn all existing |'s into ||'s in subject and message. - subject_and_message << subject << "|" << message; - - // Create an empty binary bucket. - U8 bin_bucket[MAX_INVENTORY_BUFFER_SIZE]; - U8* bucket_to_send = bin_bucket; - bin_bucket[0] = '\0'; - S32 bin_bucket_size = EMPTY_BINARY_BUCKET_SIZE; - // If there is an item being sent, pack it into the binary bucket. - if (item) - { - LLSD item_def; - item_def["item_id"] = item->getUUID(); - item_def["owner_id"] = item->getPermissions().getOwner(); - std::ostringstream ostr; - LLSDSerialize::serialize(item_def, ostr, LLSDSerialize::LLSD_XML); - bin_bucket_size = ostr.str().copy( - (char*)bin_bucket, ostr.str().size()); - bin_bucket[bin_bucket_size] = '\0'; - } - else - { - bucket_to_send = (U8*) EMPTY_BINARY_BUCKET; - } - - - send_improved_im( - group_id, - my_name, - subject_and_message.str(), - IM_ONLINE, - IM_GROUP_NOTICE, - LLUUID::null, - NO_TIMESTAMP, - bucket_to_send, - bin_bucket_size); -} - -bool handle_lure_callback(const LLSD& notification, const LLSD& response) -{ - std::string text = response["message"].asString(); - LLSLURL slurl; - LLAgentUI::buildSLURL(slurl); - text.append("\r\n").append(slurl.getSLURLString()); - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - - if(0 == option) - { - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_StartLure); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_Info); - msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in. - msg->addStringFast(_PREHASH_Message, text); - for(LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray(); - it != notification["payload"]["ids"].endArray(); - ++it) - { - LLUUID target_id = it->asUUID(); - - msg->nextBlockFast(_PREHASH_TargetData); - msg->addUUIDFast(_PREHASH_TargetID, target_id); - - // Record the offer. - { - std::string target_name; - gCacheName->getFullName(target_id, target_name); // for im log filenames - LLSD args; - args["TO_NAME"] = LLSLURL("agent", target_id, "displayname").getSLURLString();; - - LLSD payload; - - //*TODO please rewrite all keys to the same case, lower or upper - payload["from_id"] = target_id; - payload["SUPPRESS_TOAST"] = true; - LLNotificationsUtil::add("TeleportOfferSent", args, payload); - - // Add the recepient to the recent people list. - LLRecentPeople::instance().add(target_id); - } - } - gAgent.sendReliableMessage(); - } - - return false; -} - -void handle_lure(const LLUUID& invitee) -{ - LLDynamicArray ids; - ids.push_back(invitee); - handle_lure(ids); -} - -// Prompt for a message to the invited user. -void handle_lure(const uuid_vec_t& ids) -{ - if (ids.empty()) return; - - if (!gAgent.getRegion()) return; - - LLSD edit_args; - edit_args["REGION"] = gAgent.getRegion()->getName(); - - LLSD payload; - for (LLDynamicArray::const_iterator it = ids.begin(); - it != ids.end(); - ++it) - { - payload["ids"].append(*it); - } - if (gAgent.isGodlike()) - { - LLNotificationsUtil::add("OfferTeleportFromGod", edit_args, payload, handle_lure_callback); - } - else - { - LLNotificationsUtil::add("OfferTeleport", edit_args, payload, handle_lure_callback); - } -} - - -void send_improved_im(const LLUUID& to_id, - const std::string& name, - const std::string& message, - U8 offline, - EInstantMessage dialog, - const LLUUID& id, - U32 timestamp, - const U8* binary_bucket, - S32 binary_bucket_size) -{ - pack_instant_message( - gMessageSystem, - gAgent.getID(), - FALSE, - gAgent.getSessionID(), - to_id, - name, - message, - offline, - dialog, - id, - 0, - LLUUID::null, - gAgent.getPositionAgent(), - timestamp, - binary_bucket, - binary_bucket_size); - gAgent.sendReliableMessage(); -} - - -void send_places_query(const LLUUID& query_id, - const LLUUID& trans_id, - const std::string& query_text, - U32 query_flags, - S32 category, - const std::string& sim_name) -{ - LLMessageSystem* msg = gMessageSystem; - - msg->newMessage("PlacesQuery"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->addUUID("QueryID", query_id); - msg->nextBlock("TransactionData"); - msg->addUUID("TransactionID", trans_id); - msg->nextBlock("QueryData"); - msg->addString("QueryText", query_text); - msg->addU32("QueryFlags", query_flags); - msg->addS8("Category", (S8)category); - msg->addString("SimName", sim_name); - gAgent.sendReliableMessage(); -} - - -void process_user_info_reply(LLMessageSystem* msg, void**) -{ - LLUUID agent_id; - msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); - if(agent_id != gAgent.getID()) - { - LL_WARNS("Messaging") << "process_user_info_reply - " - << "wrong agent id." << LL_ENDL; - } - - BOOL im_via_email; - msg->getBOOLFast(_PREHASH_UserData, _PREHASH_IMViaEMail, im_via_email); - std::string email; - msg->getStringFast(_PREHASH_UserData, _PREHASH_EMail, email); - std::string dir_visibility; - msg->getString( "UserData", "DirectoryVisibility", dir_visibility); - - LLFloaterPreference::updateUserInfo(dir_visibility, im_via_email, email); - LLFloaterPostcard::updateUserInfo(email); -} - - -//--------------------------------------------------------------------------- -// Script Dialog -//--------------------------------------------------------------------------- - -const S32 SCRIPT_DIALOG_MAX_BUTTONS = 12; -const S32 SCRIPT_DIALOG_BUTTON_STR_SIZE = 24; -const S32 SCRIPT_DIALOG_MAX_MESSAGE_SIZE = 512; -const char* SCRIPT_DIALOG_HEADER = "Script Dialog:\n"; - -bool callback_script_dialog(const LLSD& notification, const LLSD& response) -{ - LLNotificationForm form(notification["form"]); - - std::string rtn_text; - S32 button_idx; - button_idx = LLNotification::getSelectedOption(notification, response); - if (response[TEXTBOX_MAGIC_TOKEN].isDefined()) - { - if (response[TEXTBOX_MAGIC_TOKEN].isString()) - rtn_text = response[TEXTBOX_MAGIC_TOKEN].asString(); - else - rtn_text.clear(); // bool marks empty string - } - else - { - rtn_text = LLNotification::getSelectedOptionName(response); - } - - // Didn't click "Ignore" - if (button_idx != -1) - { - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("ScriptDialogReply"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->nextBlock("Data"); - msg->addUUID("ObjectID", notification["payload"]["object_id"].asUUID()); - msg->addS32("ChatChannel", notification["payload"]["chat_channel"].asInteger()); - msg->addS32("ButtonIndex", button_idx); - msg->addString("ButtonLabel", rtn_text); - msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); - } - - return false; -} -static LLNotificationFunctorRegistration callback_script_dialog_reg_1("ScriptDialog", callback_script_dialog); -static LLNotificationFunctorRegistration callback_script_dialog_reg_2("ScriptDialogGroup", callback_script_dialog); - -void process_script_dialog(LLMessageSystem* msg, void**) -{ - S32 i; - LLSD payload; - - LLUUID object_id; - msg->getUUID("Data", "ObjectID", object_id); - - if (LLMuteList::getInstance()->isMuted(object_id)) - { - return; - } - - std::string message; - std::string first_name; - std::string last_name; - std::string title; - - S32 chat_channel; - msg->getString("Data", "FirstName", first_name); - msg->getString("Data", "LastName", last_name); - msg->getString("Data", "ObjectName", title); - msg->getString("Data", "Message", message); - msg->getS32("Data", "ChatChannel", chat_channel); - - // unused for now - LLUUID image_id; - msg->getUUID("Data", "ImageID", image_id); - - payload["sender"] = msg->getSender().getIPandPort(); - payload["object_id"] = object_id; - payload["chat_channel"] = chat_channel; - - // build up custom form - S32 button_count = msg->getNumberOfBlocks("Buttons"); - if (button_count > SCRIPT_DIALOG_MAX_BUTTONS) - { - llwarns << "Too many script dialog buttons - omitting some" << llendl; - button_count = SCRIPT_DIALOG_MAX_BUTTONS; - } - - LLNotificationForm form; - for (i = 0; i < button_count; i++) - { - std::string tdesc; - msg->getString("Buttons", "ButtonLabel", tdesc, i); - form.addElement("button", std::string(tdesc)); - } - - LLSD args; - args["TITLE"] = title; - args["MESSAGE"] = message; - LLNotificationPtr notification; - if (!first_name.empty()) - { - args["NAME"] = LLCacheName::buildFullName(first_name, last_name); - notification = LLNotifications::instance().add( - LLNotification::Params("ScriptDialog").substitutions(args).payload(payload).form_elements(form.asLLSD())); - } - else - { - args["GROUPNAME"] = last_name; - notification = LLNotifications::instance().add( - LLNotification::Params("ScriptDialogGroup").substitutions(args).payload(payload).form_elements(form.asLLSD())); - } -} - -//--------------------------------------------------------------------------- - - -std::vector gLoadUrlList; - -bool callback_load_url(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - - if (0 == option) - { - LLWeb::loadURL(notification["payload"]["url"].asString()); - } - - return false; -} -static LLNotificationFunctorRegistration callback_load_url_reg("LoadWebPage", callback_load_url); - - -// We've got the name of the person who owns the object hurling the url. -// Display confirmation dialog. -void callback_load_url_name(const LLUUID& id, const std::string& full_name, bool is_group) -{ - std::vector::iterator it; - for (it = gLoadUrlList.begin(); it != gLoadUrlList.end(); ) - { - LLSD load_url_info = *it; - if (load_url_info["owner_id"].asUUID() == id) - { - it = gLoadUrlList.erase(it); - - std::string owner_name; - if (is_group) - { - owner_name = full_name + LLTrans::getString("Group"); - } - else - { - owner_name = full_name; - } - - // For legacy name-only mutes. - if (LLMuteList::getInstance()->isMuted(LLUUID::null, owner_name)) - { - continue; - } - LLSD args; - args["URL"] = load_url_info["url"].asString(); - args["MESSAGE"] = load_url_info["message"].asString();; - args["OBJECTNAME"] = load_url_info["object_name"].asString(); - args["NAME"] = owner_name; - - LLNotificationsUtil::add("LoadWebPage", args, load_url_info); - } - else - { - ++it; - } - } -} - -void process_load_url(LLMessageSystem* msg, void**) -{ - LLUUID object_id; - LLUUID owner_id; - BOOL owner_is_group; - char object_name[256]; /* Flawfinder: ignore */ - char message[256]; /* Flawfinder: ignore */ - char url[256]; /* Flawfinder: ignore */ - - msg->getString("Data", "ObjectName", 256, object_name); - msg->getUUID( "Data", "ObjectID", object_id); - msg->getUUID( "Data", "OwnerID", owner_id); - msg->getBOOL( "Data", "OwnerIsGroup", owner_is_group); - msg->getString("Data", "Message", 256, message); - msg->getString("Data", "URL", 256, url); - - LLSD payload; - payload["object_id"] = object_id; - payload["owner_id"] = owner_id; - payload["owner_is_group"] = owner_is_group; - payload["object_name"] = object_name; - payload["message"] = message; - payload["url"] = url; - - // URL is safety checked in load_url above - - // Check if object or owner is muted - if (LLMuteList::getInstance()->isMuted(object_id, object_name) || - LLMuteList::getInstance()->isMuted(owner_id)) - { - LL_INFOS("Messaging")<<"Ignoring load_url from muted object/owner."<get(owner_id, owner_is_group, - boost::bind(&callback_load_url_name, _1, _2, _3)); -} - - -void callback_download_complete(void** data, S32 result, LLExtStat ext_status) -{ - std::string* filepath = (std::string*)data; - LLSD args; - args["DOWNLOAD_PATH"] = *filepath; - LLNotificationsUtil::add("FinishedRawDownload", args); - delete filepath; -} - - -void process_initiate_download(LLMessageSystem* msg, void**) -{ - LLUUID agent_id; - msg->getUUID("AgentData", "AgentID", agent_id); - if (agent_id != gAgent.getID()) - { - LL_WARNS("Messaging") << "Initiate download for wrong agent" << LL_ENDL; - return; - } - - std::string sim_filename; - std::string viewer_filename; - msg->getString("FileData", "SimFilename", sim_filename); - msg->getString("FileData", "ViewerFilename", viewer_filename); - - if (!gXferManager->validateFileForRequest(viewer_filename)) - { - llwarns << "SECURITY: Unauthorized download to local file " << viewer_filename << llendl; - return; - } - gXferManager->requestFile(viewer_filename, - sim_filename, - LL_PATH_NONE, - msg->getSender(), - FALSE, // don't delete remote - callback_download_complete, - (void**)new std::string(viewer_filename)); -} - - -void process_script_teleport_request(LLMessageSystem* msg, void**) -{ - if (!gSavedSettings.getBOOL("ScriptsCanShowUI")) return; - - std::string object_name; - std::string sim_name; - LLVector3 pos; - LLVector3 look_at; - - msg->getString("Data", "ObjectName", object_name); - msg->getString("Data", "SimName", sim_name); - msg->getVector3("Data", "SimPosition", pos); - msg->getVector3("Data", "LookAt", look_at); - - LLFloaterWorldMap* instance = LLFloaterWorldMap::getInstance(); - if(instance) - { - instance->trackURL( - sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]); - LLFloaterReg::showInstance("world_map", "center"); - } - - // remove above two lines and replace with below line - // to re-enable parcel browser for llMapDestination() - // LLURLDispatcher::dispatch(LLSLURL::buildSLURL(sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]), FALSE); - -} - -void process_covenant_reply(LLMessageSystem* msg, void**) -{ - LLUUID covenant_id, estate_owner_id; - std::string estate_name; - U32 covenant_timestamp; - msg->getUUID("Data", "CovenantID", covenant_id); - msg->getU32("Data", "CovenantTimestamp", covenant_timestamp); - msg->getString("Data", "EstateName", estate_name); - msg->getUUID("Data", "EstateOwnerID", estate_owner_id); - - LLPanelEstateCovenant::updateEstateName(estate_name); - LLPanelLandCovenant::updateEstateName(estate_name); - LLFloaterBuyLand::updateEstateName(estate_name); - - std::string owner_name = - LLSLURL("agent", estate_owner_id, "inspect").getSLURLString(); - LLPanelEstateCovenant::updateEstateOwnerName(owner_name); - LLPanelLandCovenant::updateEstateOwnerName(owner_name); - LLFloaterBuyLand::updateEstateOwnerName(owner_name); - - LLPanelPlaceProfile* panel = LLSideTray::getInstance()->getPanel("panel_place_profile"); - if (panel) - { - panel->updateEstateName(estate_name); - panel->updateEstateOwnerName(owner_name); - } - - // standard message, not from system - std::string last_modified; - if (covenant_timestamp == 0) - { - last_modified = LLTrans::getString("covenant_last_modified")+LLTrans::getString("never_text"); - } - else - { - last_modified = LLTrans::getString("covenant_last_modified")+"[" - +LLTrans::getString("LTimeWeek")+"] [" - +LLTrans::getString("LTimeMonth")+"] [" - +LLTrans::getString("LTimeDay")+"] [" - +LLTrans::getString("LTimeHour")+"]:[" - +LLTrans::getString("LTimeMin")+"]:[" - +LLTrans::getString("LTimeSec")+"] [" - +LLTrans::getString("LTimeYear")+"]"; - LLSD substitution; - substitution["datetime"] = (S32) covenant_timestamp; - LLStringUtil::format (last_modified, substitution); - } - - LLPanelEstateCovenant::updateLastModified(last_modified); - LLPanelLandCovenant::updateLastModified(last_modified); - LLFloaterBuyLand::updateLastModified(last_modified); - - // load the actual covenant asset data - const BOOL high_priority = TRUE; - if (covenant_id.notNull()) - { - gAssetStorage->getEstateAsset(gAgent.getRegionHost(), - gAgent.getID(), - gAgent.getSessionID(), - covenant_id, - LLAssetType::AT_NOTECARD, - ET_Covenant, - onCovenantLoadComplete, - NULL, - high_priority); - } - else - { - std::string covenant_text; - if (estate_owner_id.isNull()) - { - // mainland - covenant_text = LLTrans::getString("RegionNoCovenant"); - } - else - { - covenant_text = LLTrans::getString("RegionNoCovenantOtherOwner"); - } - LLPanelEstateCovenant::updateCovenantText(covenant_text, covenant_id); - LLPanelLandCovenant::updateCovenantText(covenant_text); - LLFloaterBuyLand::updateCovenantText(covenant_text, covenant_id); - if (panel) - { - panel->updateCovenantText(covenant_text); - } - } -} - -void onCovenantLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status) -{ - LL_DEBUGS("Messaging") << "onCovenantLoadComplete()" << LL_ENDL; - std::string covenant_text; - if(0 == status) - { - LLVFile file(vfs, asset_uuid, type, LLVFile::READ); - - S32 file_length = file.getSize(); - - std::vector buffer(file_length+1); - file.read((U8*)&buffer[0], file_length); - // put a EOS at the end - buffer[file_length] = '\0'; - - if( (file_length > 19) && !strncmp( &buffer[0], "Linden text version", 19 ) ) - { - LLViewerTextEditor::Params params; - params.name("temp"); - params.max_text_length(file_length+1); - LLViewerTextEditor * editor = LLUICtrlFactory::create (params); - if( !editor->importBuffer( &buffer[0], file_length+1 ) ) - { - LL_WARNS("Messaging") << "Problem importing estate covenant." << LL_ENDL; - covenant_text = "Problem importing estate covenant."; - } - else - { - // Version 0 (just text, doesn't include version number) - covenant_text = editor->getText(); - } - delete editor; - } - else - { - LL_WARNS("Messaging") << "Problem importing estate covenant: Covenant file format error." << LL_ENDL; - covenant_text = "Problem importing estate covenant: Covenant file format error."; - } - } - else - { - LLViewerStats::getInstance()->incStat( LLViewerStats::ST_DOWNLOAD_FAILED ); - - if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status || - LL_ERR_FILE_EMPTY == status) - { - covenant_text = "Estate covenant notecard is missing from database."; - } - else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status) - { - covenant_text = "Insufficient permissions to view estate covenant."; - } - else - { - covenant_text = "Unable to load estate covenant at this time."; - } - - LL_WARNS("Messaging") << "Problem loading notecard: " << status << LL_ENDL; - } - LLPanelEstateCovenant::updateCovenantText(covenant_text, asset_uuid); - LLPanelLandCovenant::updateCovenantText(covenant_text); - LLFloaterBuyLand::updateCovenantText(covenant_text, asset_uuid); - - LLPanelPlaceProfile* panel = LLSideTray::getInstance()->getPanel("panel_place_profile"); - if (panel) - { - panel->updateCovenantText(covenant_text); - } -} - - -void process_feature_disabled_message(LLMessageSystem* msg, void**) -{ - // Handle Blacklisted feature simulator response... - LLUUID agentID; - LLUUID transactionID; - std::string messageText; - msg->getStringFast(_PREHASH_FailureInfo,_PREHASH_ErrorMessage, messageText,0); - msg->getUUIDFast(_PREHASH_FailureInfo,_PREHASH_AgentID,agentID); - msg->getUUIDFast(_PREHASH_FailureInfo,_PREHASH_TransactionID,transactionID); - - LL_WARNS("Messaging") << "Blacklisted Feature Response:" << messageText << LL_ENDL; -} - -// ------------------------------------------------------------ -// Message system exception callbacks -// ------------------------------------------------------------ - -void invalid_message_callback(LLMessageSystem* msg, - void*, - EMessageException exception) -{ - LLAppViewer::instance()->badNetworkHandler(); -} - -// Please do not add more message handlers here. This file is huge. -// Put them in a file related to the functionality you are implementing. - -void LLOfferInfo::forceResponse(InventoryOfferResponse response) -{ - LLNotification::Params params("UserGiveItem"); - params.functor.function(boost::bind(&LLOfferInfo::inventory_offer_callback, this, _1, _2)); - LLNotifications::instance().forceResponse(params, response); -} - +/** + * @file llviewermessage.cpp + * @brief Dumping ground for viewer-side message system callbacks. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llviewermessage.h" +#include "boost/lexical_cast.hpp" + +// Linden libraries +#include "llanimationstates.h" +#include "llaudioengine.h" +#include "llavataractions.h" +#include "llavatarnamecache.h" // IDEVO HACK +#include "lscript_byteformat.h" +#include "lleconomy.h" +#include "lleventtimer.h" +#include "llfloaterreg.h" +#include "llfollowcamparams.h" +#include "llinventorydefines.h" +#include "lllslconstants.h" +#include "llregionhandle.h" +#include "llsdserialize.h" +#include "llteleportflags.h" +#include "lltransactionflags.h" +#include "llvfile.h" +#include "llvfs.h" +#include "llxfermanager.h" +#include "mean_collision_data.h" + +#include "llagent.h" +#include "llagentcamera.h" +#include "llcallingcard.h" +#include "llbuycurrencyhtml.h" +#include "llfirstuse.h" +#include "llfloaterbuyland.h" +#include "llfloaterland.h" +#include "llfloaterregioninfo.h" +#include "llfloaterlandholdings.h" +#include "llfloaterpostcard.h" +#include "llfloaterpreference.h" +#include "llhudeffecttrail.h" +#include "llhudmanager.h" +#include "llinventoryfunctions.h" +#include "llinventoryobserver.h" +#include "llinventorypanel.h" +#include "llnearbychat.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" +#include "llpanelgrouplandmoney.h" +#include "llrecentpeople.h" +#include "llscriptfloater.h" +#include "llselectmgr.h" +#include "llsidetray.h" +#include "llstartup.h" +#include "llsky.h" +#include "llslurl.h" +#include "llstatenums.h" +#include "llstatusbar.h" +#include "llimview.h" +#include "llspeakers.h" +#include "lltrans.h" +#include "lltranslate.h" +#include "llviewerfoldertype.h" +#include "llvoavatar.h" // IDEVO HACK +#include "lluri.h" +#include "llviewergenericmessage.h" +#include "llviewermenu.h" +#include "llviewerjoystick.h" +#include "llviewerobjectlist.h" +#include "llviewerparcelmgr.h" +#include "llviewerstats.h" +#include "llviewertexteditor.h" +#include "llviewerthrottle.h" +#include "llviewerwindow.h" +#include "llvlmanager.h" +#include "llvoavatarself.h" +#include "llvotextbubble.h" +#include "llworld.h" +#include "pipeline.h" +#include "llfloaterworldmap.h" +#include "llviewerdisplay.h" +#include "llkeythrottle.h" +#include "llgroupactions.h" +#include "llagentui.h" +#include "llpanelblockedlist.h" +#include "llpanelplaceprofile.h" + +#include // +#include + +#include "llnotificationmanager.h" // + +#if LL_MSVC +// disable boost::lexical_cast warning +#pragma warning (disable:4702) +#endif + +// +// Constants +// +const F32 BIRD_AUDIBLE_RADIUS = 32.0f; +const F32 SIT_DISTANCE_FROM_TARGET = 0.25f; +static const F32 LOGOUT_REPLY_TIME = 3.f; // Wait this long after LogoutReply before quitting. + +// Determine how quickly residents' scripts can issue question dialogs +// Allow bursts of up to 5 dialogs in 10 seconds. 10*2=20 seconds recovery if throttle kicks in +static const U32 LLREQUEST_PERMISSION_THROTTLE_LIMIT = 5; // requests +static const F32 LLREQUEST_PERMISSION_THROTTLE_INTERVAL = 10.0f; // seconds + +extern BOOL gDebugClicks; + +// function prototypes +bool check_offer_throttle(const std::string& from_name, bool check_only); +static void process_money_balance_reply_extended(LLMessageSystem* msg); + +//inventory offer throttle globals +LLFrameTimer gThrottleTimer; +const U32 OFFER_THROTTLE_MAX_COUNT=5; //number of items per time period +const F32 OFFER_THROTTLE_TIME=10.f; //time period in seconds + +//script permissions +const std::string SCRIPT_QUESTIONS[SCRIPT_PERMISSION_EOF] = + { + "ScriptTakeMoney", + "ActOnControlInputs", + "RemapControlInputs", + "AnimateYourAvatar", + "AttachToYourAvatar", + "ReleaseOwnership", + "LinkAndDelink", + "AddAndRemoveJoints", + "ChangePermissions", + "TrackYourCamera", + "ControlYourCamera" + }; + +const BOOL SCRIPT_QUESTION_IS_CAUTION[SCRIPT_PERMISSION_EOF] = +{ + TRUE, // ScriptTakeMoney, + FALSE, // ActOnControlInputs + FALSE, // RemapControlInputs + FALSE, // AnimateYourAvatar + FALSE, // AttachToYourAvatar + FALSE, // ReleaseOwnership, + FALSE, // LinkAndDelink, + FALSE, // AddAndRemoveJoints + FALSE, // ChangePermissions + FALSE, // TrackYourCamera, + FALSE // ControlYourCamera +}; + +bool friendship_offer_callback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + LLMessageSystem* msg = gMessageSystem; + const LLSD& payload = notification["payload"]; + + // add friend to recent people list + LLRecentPeople::instance().add(payload["from_id"]); + + switch(option) + { + case 0: + { + // accept + LLAvatarTracker::formFriendship(payload["from_id"]); + + const LLUUID fid = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); + + // This will also trigger an onlinenotification if the user is online + msg->newMessageFast(_PREHASH_AcceptFriendship); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_TransactionBlock); + msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]); + msg->nextBlockFast(_PREHASH_FolderData); + msg->addUUIDFast(_PREHASH_FolderID, fid); + msg->sendReliable(LLHost(payload["sender"].asString())); + + LLSD payload = notification["payload"]; + payload["SUPPRESS_TOAST"] = true; + LLNotificationsUtil::add("FriendshipAcceptedByMe", + notification["substitutions"], payload); + break; + } + case 1: // Decline + { + LLSD payload = notification["payload"]; + payload["SUPPRESS_TOAST"] = true; + LLNotificationsUtil::add("FriendshipDeclinedByMe", + notification["substitutions"], payload); + } + // fall-through + case 2: // Send IM - decline and start IM session + { + // decline + // We no longer notify other viewers, but we DO still send + // the rejection to the simulator to delete the pending userop. + msg->newMessageFast(_PREHASH_DeclineFriendship); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_TransactionBlock); + msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]); + msg->sendReliable(LLHost(payload["sender"].asString())); + + // start IM session + if(2 == option) + { + LLAvatarActions::startIM(payload["from_id"].asUUID()); + } + } + default: + // close button probably, possibly timed out + break; + } + + return false; +} +static LLNotificationFunctorRegistration friendship_offer_callback_reg("OfferFriendship", friendship_offer_callback); +static LLNotificationFunctorRegistration friendship_offer_callback_reg_nm("OfferFriendshipNoMessage", friendship_offer_callback); + +//const char BUSY_AUTO_RESPONSE[] = "The Resident you messaged is in 'busy mode' which means they have " +// "requested not to be disturbed. Your message will still be shown in their IM " +// "panel for later viewing."; + +// +// Functions +// + +void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_group, + S32 trx_type, const std::string& desc) +{ + if(0 == amount || !region) return; + amount = abs(amount); + LL_INFOS("Messaging") << "give_money(" << uuid << "," << amount << ")"<< LL_ENDL; + if(can_afford_transaction(amount)) + { +// gStatusBar->debitBalance(amount); + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_MoneyTransferRequest); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_MoneyData); + msg->addUUIDFast(_PREHASH_SourceID, gAgent.getID() ); + msg->addUUIDFast(_PREHASH_DestID, uuid); + msg->addU8Fast(_PREHASH_Flags, pack_transaction_flags(FALSE, is_group)); + msg->addS32Fast(_PREHASH_Amount, amount); + msg->addU8Fast(_PREHASH_AggregatePermNextOwner, (U8)LLAggregatePermissions::AP_EMPTY); + msg->addU8Fast(_PREHASH_AggregatePermInventory, (U8)LLAggregatePermissions::AP_EMPTY); + msg->addS32Fast(_PREHASH_TransactionType, trx_type ); + msg->addStringFast(_PREHASH_Description, desc); + msg->sendReliable(region->getHost()); + } + else + { + LLStringUtil::format_map_t args; + args["AMOUNT"] = llformat("%d", amount); + LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("giving", args), amount ); + } +} + +void send_complete_agent_movement(const LLHost& sim_host) +{ + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_CompleteAgentMovement); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addU32Fast(_PREHASH_CircuitCode, msg->mOurCircuitCode); + msg->sendReliable(sim_host); +} + +void process_logout_reply(LLMessageSystem* msg, void**) +{ + // The server has told us it's ok to quit. + LL_DEBUGS("Messaging") << "process_logout_reply" << LL_ENDL; + + LLUUID agent_id; + msg->getUUID("AgentData", "AgentID", agent_id); + LLUUID session_id; + msg->getUUID("AgentData", "SessionID", session_id); + if((agent_id != gAgent.getID()) || (session_id != gAgent.getSessionID())) + { + LL_WARNS("Messaging") << "Bogus Logout Reply" << LL_ENDL; + } + + LLInventoryModel::update_map_t parents; + S32 count = msg->getNumberOfBlocksFast( _PREHASH_InventoryData ); + for(S32 i = 0; i < count; ++i) + { + LLUUID item_id; + msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i); + + if( (1 == count) && item_id.isNull() ) + { + // Detect dummy item. Indicates an empty list. + break; + } + + // We do not need to track the asset ids, just account for an + // updated inventory version. + LL_INFOS("Messaging") << "process_logout_reply itemID=" << item_id << LL_ENDL; + LLInventoryItem* item = gInventory.getItem( item_id ); + if( item ) + { + parents[item->getParentUUID()] = 0; + gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item_id); + } + else + { + LL_INFOS("Messaging") << "process_logout_reply item not found: " << item_id << LL_ENDL; + } + } + LLAppViewer::instance()->forceQuit(); +} + +void process_layer_data(LLMessageSystem *mesgsys, void **user_data) +{ + LLViewerRegion *regionp = LLWorld::getInstance()->getRegion(mesgsys->getSender()); + + if (!regionp || gNoRender) + { + return; + } + + + S32 size; + S8 type; + + mesgsys->getS8Fast(_PREHASH_LayerID, _PREHASH_Type, type); + size = mesgsys->getSizeFast(_PREHASH_LayerData, _PREHASH_Data); + if (0 == size) + { + LL_WARNS("Messaging") << "Layer data has zero size." << LL_ENDL; + return; + } + if (size < 0) + { + // getSizeFast() is probably trying to tell us about an error + LL_WARNS("Messaging") << "getSizeFast() returned negative result: " + << size + << LL_ENDL; + return; + } + U8 *datap = new U8[size]; + mesgsys->getBinaryDataFast(_PREHASH_LayerData, _PREHASH_Data, datap, size); + LLVLData *vl_datap = new LLVLData(regionp, type, datap, size); + if (mesgsys->getReceiveCompressedSize()) + { + gVLManager.addLayerData(vl_datap, mesgsys->getReceiveCompressedSize()); + } + else + { + gVLManager.addLayerData(vl_datap, mesgsys->getReceiveSize()); + } +} + +// S32 exported_object_count = 0; +// S32 exported_image_count = 0; +// S32 current_object_count = 0; +// S32 current_image_count = 0; + +// extern LLNotifyBox *gExporterNotify; +// extern LLUUID gExporterRequestID; +// extern std::string gExportDirectory; + +// extern LLUploadDialog *gExportDialog; + +// std::string gExportedFile; + +// std::map gImageChecksums; + +// void export_complete() +// { +// LLUploadDialog::modalUploadFinished(); +// gExporterRequestID.setNull(); +// gExportDirectory = ""; + +// LLFILE* fXML = LLFile::fopen(gExportedFile, "rb"); /* Flawfinder: ignore */ +// fseek(fXML, 0, SEEK_END); +// long length = ftell(fXML); +// fseek(fXML, 0, SEEK_SET); +// U8 *buffer = new U8[length + 1]; +// size_t nread = fread(buffer, 1, length, fXML); +// if (nread < (size_t) length) +// { +// LL_WARNS("Messaging") << "Short read" << LL_ENDL; +// } +// buffer[nread] = '\0'; +// fclose(fXML); + +// char *pos = (char *)buffer; +// while ((pos = strstr(pos+1, ""); + +// if (pos_uuid) +// { +// char image_uuid_str[UUID_STR_SIZE]; /* Flawfinder: ignore */ +// memcpy(image_uuid_str, pos_uuid+2, UUID_STR_SIZE-1); /* Flawfinder: ignore */ +// image_uuid_str[UUID_STR_SIZE-1] = 0; + +// LLUUID image_uuid(image_uuid_str); + +// LL_INFOS("Messaging") << "Found UUID: " << image_uuid << LL_ENDL; + +// std::map::iterator itor = gImageChecksums.find(image_uuid); +// if (itor != gImageChecksums.end()) +// { +// LL_INFOS("Messaging") << "Replacing with checksum: " << itor->second << LL_ENDL; +// if (!itor->second.empty()) +// { +// memcpy(&pos_check[10], itor->second.c_str(), 32); /* Flawfinder: ignore */ +// } +// } +// } +// } +// } + +// LLFILE* fXMLOut = LLFile::fopen(gExportedFile, "wb"); /* Flawfinder: ignore */ +// if (fwrite(buffer, 1, length, fXMLOut) != length) +// { +// LL_WARNS("Messaging") << "Short write" << LL_ENDL; +// } +// fclose(fXMLOut); + +// delete [] buffer; +// } + + +// void exported_item_complete(const LLTSCode status, void *user_data) +// { +// //std::string *filename = (std::string *)user_data; + +// if (status < LLTS_OK) +// { +// LL_WARNS("Messaging") << "Export failed!" << LL_ENDL; +// } +// else +// { +// ++current_object_count; +// if (current_image_count == exported_image_count && current_object_count == exported_object_count) +// { +// LL_INFOS("Messaging") << "*** Export complete ***" << LL_ENDL; + +// export_complete(); +// } +// else +// { +// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count)); +// } +// } +// } + +// struct exported_image_info +// { +// LLUUID image_id; +// std::string filename; +// U32 image_num; +// }; + +// void exported_j2c_complete(const LLTSCode status, void *user_data) +// { +// exported_image_info *info = (exported_image_info *)user_data; +// LLUUID image_id = info->image_id; +// U32 image_num = info->image_num; +// std::string filename = info->filename; +// delete info; + +// if (status < LLTS_OK) +// { +// LL_WARNS("Messaging") << "Image download failed!" << LL_ENDL; +// } +// else +// { +// LLFILE* fIn = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */ +// if (fIn) +// { +// LLPointer ImageUtility = new LLImageJ2C; +// LLPointer TargaUtility = new LLImageTGA; + +// fseek(fIn, 0, SEEK_END); +// S32 length = ftell(fIn); +// fseek(fIn, 0, SEEK_SET); +// U8 *buffer = ImageUtility->allocateData(length); +// if (fread(buffer, 1, length, fIn) != length) +// { +// LL_WARNS("Messaging") << "Short read" << LL_ENDL; +// } +// fclose(fIn); +// LLFile::remove(filename); + +// // Convert to TGA +// LLPointer image = new LLImageRaw(); + +// ImageUtility->updateData(); +// ImageUtility->decode(image, 100000.0f); + +// TargaUtility->encode(image); +// U8 *data = TargaUtility->getData(); +// S32 data_size = TargaUtility->getDataSize(); + +// std::string file_path = gDirUtilp->getDirName(filename); + +// std::string output_file = llformat("%s/image-%03d.tga", file_path.c_str(), image_num);//filename; +// //S32 name_len = output_file.length(); +// //strcpy(&output_file[name_len-3], "tga"); +// LLFILE* fOut = LLFile::fopen(output_file, "wb"); /* Flawfinder: ignore */ +// char md5_hash_string[33]; /* Flawfinder: ignore */ +// strcpy(md5_hash_string, "00000000000000000000000000000000"); /* Flawfinder: ignore */ +// if (fOut) +// { +// if (fwrite(data, 1, data_size, fOut) != data_size) +// { +// LL_WARNS("Messaging") << "Short write" << LL_ENDL; +// } +// fseek(fOut, 0, SEEK_SET); +// fclose(fOut); +// fOut = LLFile::fopen(output_file, "rb"); /* Flawfinder: ignore */ +// LLMD5 my_md5_hash(fOut); +// my_md5_hash.hex_digest(md5_hash_string); +// } + +// gImageChecksums.insert(std::pair(image_id, md5_hash_string)); +// } +// } + +// ++current_image_count; +// if (current_image_count == exported_image_count && current_object_count == exported_object_count) +// { +// LL_INFOS("Messaging") << "*** Export textures complete ***" << LL_ENDL; +// export_complete(); +// } +// else +// { +// gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count)); +// } +//} + +void process_derez_ack(LLMessageSystem*, void**) +{ + if(gViewerWindow) gViewerWindow->getWindow()->decBusyCount(); +} + +void process_places_reply(LLMessageSystem* msg, void** data) +{ + LLUUID query_id; + + msg->getUUID("AgentData", "QueryID", query_id); + if (query_id.isNull()) + { + LLFloaterLandHoldings::processPlacesReply(msg, data); + } + else if(gAgent.isInGroup(query_id)) + { + LLPanelGroupLandMoney::processPlacesReply(msg, data); + } + else + { + LL_WARNS("Messaging") << "Got invalid PlacesReply message" << LL_ENDL; + } +} + +void send_sound_trigger(const LLUUID& sound_id, F32 gain) +{ + if (sound_id.isNull() || gAgent.getRegion() == NULL) + { + // disconnected agent or zero guids don't get sent (no sound) + return; + } + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_SoundTrigger); + msg->nextBlockFast(_PREHASH_SoundData); + msg->addUUIDFast(_PREHASH_SoundID, sound_id); + // Client untrusted, ids set on sim + msg->addUUIDFast(_PREHASH_OwnerID, LLUUID::null ); + msg->addUUIDFast(_PREHASH_ObjectID, LLUUID::null ); + msg->addUUIDFast(_PREHASH_ParentID, LLUUID::null ); + + msg->addU64Fast(_PREHASH_Handle, gAgent.getRegion()->getHandle()); + + LLVector3 position = gAgent.getPositionAgent(); + msg->addVector3Fast(_PREHASH_Position, position); + msg->addF32Fast(_PREHASH_Gain, gain); + + gAgent.sendMessage(); +} + +bool join_group_response(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + BOOL delete_context_data = TRUE; + bool accept_invite = false; + + LLUUID group_id = notification["payload"]["group_id"].asUUID(); + LLUUID transaction_id = notification["payload"]["transaction_id"].asUUID(); + std::string name = notification["payload"]["name"].asString(); + std::string message = notification["payload"]["message"].asString(); + S32 fee = notification["payload"]["fee"].asInteger(); + + if (option == 2 && !group_id.isNull()) + { + LLGroupActions::show(group_id); + LLSD args; + args["MESSAGE"] = message; + LLNotificationsUtil::add("JoinGroup", args, notification["payload"]); + return false; + } + if(option == 0 && !group_id.isNull()) + { + // check for promotion or demotion. + S32 max_groups = gMaxAgentGroups; + if(gAgent.isInGroup(group_id)) ++max_groups; + + if(gAgent.mGroups.count() < max_groups) + { + accept_invite = true; + } + else + { + delete_context_data = FALSE; + LLSD args; + args["NAME"] = name; + LLNotificationsUtil::add("JoinedTooManyGroupsMember", args, notification["payload"]); + } + } + + if (accept_invite) + { + // If there is a fee to join this group, make + // sure the user is sure they want to join. + if (fee > 0) + { + delete_context_data = FALSE; + LLSD args; + args["COST"] = llformat("%d", fee); + // Set the fee for next time to 0, so that we don't keep + // asking about a fee. + LLSD next_payload = notification["payload"]; + next_payload["fee"] = 0; + LLNotificationsUtil::add("JoinGroupCanAfford", + args, + next_payload); + } + else + { + send_improved_im(group_id, + std::string("name"), + std::string("message"), + IM_ONLINE, + IM_GROUP_INVITATION_ACCEPT, + transaction_id); + } + } + else + { + send_improved_im(group_id, + std::string("name"), + std::string("message"), + IM_ONLINE, + IM_GROUP_INVITATION_DECLINE, + transaction_id); + } + + return false; +} + +static void highlight_inventory_items_in_panel(const std::vector& items, LLInventoryPanel *inventory_panel) +{ + if (NULL == inventory_panel) return; + + for (std::vector::const_iterator item_iter = items.begin(); + item_iter != items.end(); + ++item_iter) + { + const LLUUID& item_id = (*item_iter); + if(!highlight_offered_object(item_id)) + { + continue; + } + + LLInventoryItem* item = gInventory.getItem(item_id); + llassert(item); + if (!item) { + continue; + } + + LL_DEBUGS("Inventory_Move") << "Highlighting inventory item: " << item->getName() << ", " << item_id << LL_ENDL; + LLFolderView* fv = inventory_panel->getRootFolder(); + if (fv) + { + LLFolderViewItem* fv_item = fv->getItemByID(item_id); + if (fv_item) + { + LLFolderViewItem* fv_folder = fv_item->getParentFolder(); + if (fv_folder) + { + // Parent folders can be different in case of 2 consecutive drag and drop + // operations when the second one is started before the first one completes. + LL_DEBUGS("Inventory_Move") << "Open folder: " << fv_folder->getName() << LL_ENDL; + fv_folder->setOpen(TRUE); + if (fv_folder->isSelected()) + { + fv->changeSelection(fv_folder, FALSE); + } + } + fv->changeSelection(fv_item, TRUE); + } + } + } +} + +static LLNotificationFunctorRegistration jgr_1("JoinGroup", join_group_response); +static LLNotificationFunctorRegistration jgr_2("JoinedTooManyGroupsMember", join_group_response); +static LLNotificationFunctorRegistration jgr_3("JoinGroupCanAfford", join_group_response); + + +//----------------------------------------------------------------------------- +// Instant Message +//----------------------------------------------------------------------------- +class LLOpenAgentOffer : public LLInventoryFetchItemsObserver +{ +public: + LLOpenAgentOffer(const LLUUID& object_id, + const std::string& from_name) : + LLInventoryFetchItemsObserver(object_id), + mFromName(from_name) {} + /*virtual*/ void startFetch() + { + for (uuid_vec_t::const_iterator it = mIDs.begin(); it < mIDs.end(); ++it) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(*it); + if (cat) + { + mComplete.push_back((*it)); + } + } + LLInventoryFetchItemsObserver::startFetch(); + } + /*virtual*/ void done() + { + open_inventory_offer(mComplete, mFromName); + gInventory.removeObserver(this); + delete this; + } +private: + std::string mFromName; +}; + +/** + * Class to observe adding of new items moved from the world to user's inventory to select them in inventory. + * + * We can't create it each time items are moved because "drop" event is sent separately for each + * element even while multi-dragging. We have to have the only instance of the observer. See EXT-4347. + */ +class LLViewerInventoryMoveFromWorldObserver : public LLInventoryAddItemByAssetObserver +{ +public: + LLViewerInventoryMoveFromWorldObserver() + : LLInventoryAddItemByAssetObserver() + , mActivePanel(NULL) + { + + } + + void setMoveIntoFolderID(const LLUUID& into_folder_uuid) {mMoveIntoFolderID = into_folder_uuid; } + +private: + /*virtual */void onAssetAdded(const LLUUID& asset_id) + { + // Store active Inventory panel. + mActivePanel = LLInventoryPanel::getActiveInventoryPanel(); + + // Store selected items (without destination folder) + mSelectedItems.clear(); + if (mActivePanel) + { + mSelectedItems = mActivePanel->getRootFolder()->getSelectionList(); + } + mSelectedItems.erase(mMoveIntoFolderID); + } + + /** + * Selects added inventory items watched by their Asset UUIDs if selection was not changed since + * all items were started to watch (dropped into a folder). + */ + void done() + { + // if selection is not changed since watch started lets hightlight new items. + if (mActivePanel && !isSelectionChanged()) + { + LL_DEBUGS("Inventory_Move") << "Selecting new items..." << LL_ENDL; + mActivePanel->clearSelection(); + highlight_inventory_items_in_panel(mAddedItems, mActivePanel); + } + } + + /** + * Returns true if selected inventory items were changed since moved inventory items were started to watch. + */ + bool isSelectionChanged() + { + const LLInventoryPanel * const current_active_panel = LLInventoryPanel::getActiveInventoryPanel(); + + if (NULL == mActivePanel || current_active_panel != mActivePanel) + { + return true; + } + + // get selected items (without destination folder) + selected_items_t selected_items = mActivePanel->getRootFolder()->getSelectionList(); + selected_items.erase(mMoveIntoFolderID); + + // compare stored & current sets of selected items + selected_items_t different_items; + std::set_symmetric_difference(mSelectedItems.begin(), mSelectedItems.end(), + selected_items.begin(), selected_items.end(), std::inserter(different_items, different_items.begin())); + + LL_DEBUGS("Inventory_Move") << "Selected firstly: " << mSelectedItems.size() + << ", now: " << selected_items.size() << ", difference: " << different_items.size() << LL_ENDL; + + return different_items.size() > 0; + } + + LLInventoryPanel *mActivePanel; + typedef std::set selected_items_t; + selected_items_t mSelectedItems; + + /** + * UUID of FolderViewFolder into which watched items are moved. + * + * Destination FolderViewFolder becomes selected while mouse hovering (when dragged items are dropped). + * + * If mouse is moved out it set unselected and number of selected items is changed + * even if selected items in Inventory stay the same. + * So, it is used to update stored selection list. + * + * @see onAssetAdded() + * @see isSelectionChanged() + */ + LLUUID mMoveIntoFolderID; +}; + +LLViewerInventoryMoveFromWorldObserver* gInventoryMoveObserver = NULL; + +void set_dad_inventory_item(LLInventoryItem* inv_item, const LLUUID& into_folder_uuid) +{ + start_new_inventory_observer(); + + gInventoryMoveObserver->setMoveIntoFolderID(into_folder_uuid); + gInventoryMoveObserver->watchAsset(inv_item->getAssetUUID()); +} + +//unlike the FetchObserver for AgentOffer, we only make one +//instance of the AddedObserver for TaskOffers +//and it never dies. We do this because we don't know the UUID of +//task offers until they are accepted, so we don't wouldn't +//know what to watch for, so instead we just watch for all additions. +class LLOpenTaskOffer : public LLInventoryAddedObserver +{ +protected: + /*virtual*/ void done() + { + for (uuid_vec_t::iterator it = mAdded.begin(); it != mAdded.end();) + { + const LLUUID& item_uuid = *it; + bool was_moved = false; + LLInventoryObject* added_object = gInventory.getObject(item_uuid); + if (added_object) + { + // cast to item to get Asset UUID + LLInventoryItem* added_item = dynamic_cast(added_object); + if (added_item) + { + const LLUUID& asset_uuid = added_item->getAssetUUID(); + if (gInventoryMoveObserver->isAssetWatched(asset_uuid)) + { + LL_DEBUGS("Inventory_Move") << "Found asset UUID: " << asset_uuid << LL_ENDL; + was_moved = true; + } + } + } + + if (was_moved) + { + it = mAdded.erase(it); + } + else ++it; + } + + open_inventory_offer(mAdded, ""); + mAdded.clear(); + } + }; + +class LLOpenTaskGroupOffer : public LLInventoryAddedObserver +{ +protected: + /*virtual*/ void done() + { + open_inventory_offer(mAdded, "group_offer"); + mAdded.clear(); + gInventory.removeObserver(this); + delete this; + } +}; + +//one global instance to bind them +LLOpenTaskOffer* gNewInventoryObserver=NULL; + +class LLNewInventoryHintObserver : public LLInventoryAddedObserver +{ +protected: + /*virtual*/ void done() + { + LLFirstUse::newInventory(); + } +}; + +void start_new_inventory_observer() +{ + if (!gNewInventoryObserver) //task offer observer + { + // Observer is deleted by gInventory + gNewInventoryObserver = new LLOpenTaskOffer; + gInventory.addObserver(gNewInventoryObserver); + } + + if (!gInventoryMoveObserver) //inventory move from the world observer + { + // Observer is deleted by gInventory + gInventoryMoveObserver = new LLViewerInventoryMoveFromWorldObserver; + gInventory.addObserver(gInventoryMoveObserver); + } + + gInventory.addObserver(new LLNewInventoryHintObserver()); +} + +class LLDiscardAgentOffer : public LLInventoryFetchItemsObserver +{ + LOG_CLASS(LLDiscardAgentOffer); +public: + LLDiscardAgentOffer(const LLUUID& folder_id, const LLUUID& object_id) : + LLInventoryFetchItemsObserver(object_id), + mFolderID(folder_id), + mObjectID(object_id) {} + virtual ~LLDiscardAgentOffer() {} + virtual void done() + { + LL_DEBUGS("Messaging") << "LLDiscardAgentOffer::done()" << LL_ENDL; + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + bool notify = false; + if(trash_id.notNull() && mObjectID.notNull()) + { + LLInventoryModel::update_list_t update; + LLInventoryModel::LLCategoryUpdate old_folder(mFolderID, -1); + update.push_back(old_folder); + LLInventoryModel::LLCategoryUpdate new_folder(trash_id, 1); + update.push_back(new_folder); + gInventory.accountForUpdate(update); + gInventory.moveObject(mObjectID, trash_id); + LLInventoryObject* obj = gInventory.getObject(mObjectID); + if(obj) + { + // no need to restamp since this is already a freshly + // stamped item. + obj->updateParentOnServer(FALSE); + notify = true; + } + } + else + { + LL_WARNS("Messaging") << "DiscardAgentOffer unable to find: " + << (trash_id.isNull() ? "trash " : "") + << (mObjectID.isNull() ? "object" : "") << LL_ENDL; + } + gInventory.removeObserver(this); + if(notify) + { + gInventory.notifyObservers(); + } + delete this; + } +protected: + LLUUID mFolderID; + LLUUID mObjectID; +}; + + +//Returns TRUE if we are OK, FALSE if we are throttled +//Set check_only true if you want to know the throttle status +//without registering a hit +bool check_offer_throttle(const std::string& from_name, bool check_only) +{ + static U32 throttle_count; + static bool throttle_logged; + LLChat chat; + std::string log_message; + + if (!gSavedSettings.getBOOL("ShowNewInventory")) + return false; + + if (check_only) + { + return gThrottleTimer.hasExpired(); + } + + if(gThrottleTimer.checkExpirationAndReset(OFFER_THROTTLE_TIME)) + { + LL_DEBUGS("Messaging") << "Throttle Expired" << LL_ENDL; + throttle_count=1; + throttle_logged=false; + return true; + } + else //has not expired + { + LL_DEBUGS("Messaging") << "Throttle Not Expired, Count: " << throttle_count << LL_ENDL; + // When downloading the initial inventory we get a lot of new items + // coming in and can't tell that from spam. + if (LLStartUp::getStartupState() >= STATE_STARTED + && throttle_count >= OFFER_THROTTLE_MAX_COUNT) + { + if (!throttle_logged) + { + // Use the name of the last item giver, who is probably the person + // spamming you. + + LLStringUtil::format_map_t arg; + std::string log_msg; + std::ostringstream time ; + time<getSecondLifeTitle(); + arg["TIME"] = time.str(); + + if (!from_name.empty()) + { + arg["FROM_NAME"] = from_name; + log_msg = LLTrans::getString("ItemsComingInTooFastFrom", arg); + } + else + { + log_msg = LLTrans::getString("ItemsComingInTooFast", arg); + } + + //this is kinda important, so actually put it on screen + LLSD args; + args["MESSAGE"] = log_msg; + LLNotificationsUtil::add("SystemMessage", args); + + throttle_logged=true; + } + return false; + } + else + { + throttle_count++; + return true; + } + } +} + +void open_inventory_offer(const uuid_vec_t& objects, const std::string& from_name) +{ + for (uuid_vec_t::const_iterator obj_iter = objects.begin(); + obj_iter != objects.end(); + ++obj_iter) + { + const LLUUID& obj_id = (*obj_iter); + if(!highlight_offered_object(obj_id)) + { + continue; + } + + const LLInventoryObject *obj = gInventory.getObject(obj_id); + if (!obj) + { + llwarns << "Cannot find object [ itemID:" << obj_id << " ] to open." << llendl; + continue; + } + + const LLAssetType::EType asset_type = obj->getActualType(); + + // Either an inventory item or a category. + const LLInventoryItem* item = dynamic_cast(obj); + if (item) + { + //////////////////////////////////////////////////////////////////////////////// + // Special handling for various types. + if (check_offer_throttle(from_name, false)) // If we are throttled, don't display + { + LL_DEBUGS("Messaging") << "Highlighting inventory item: " << item->getUUID() << LL_ENDL; + // If we opened this ourselves, focus it + const BOOL take_focus = from_name.empty() ? TAKE_FOCUS_YES : TAKE_FOCUS_NO; + switch(asset_type) + { + case LLAssetType::AT_NOTECARD: + { + LLFloaterReg::showInstance("preview_notecard", LLSD(obj_id), take_focus); + break; + } + case LLAssetType::AT_LANDMARK: + { + LLInventoryCategory* parent_folder = gInventory.getCategory(item->getParentUUID()); + if ("inventory_handler" == from_name) + { + //we have to filter inventory_handler messages to avoid notification displaying + LLSideTray::getInstance()->showPanel("panel_places", + LLSD().with("type", "landmark").with("id", item->getUUID())); + } + else if("group_offer" == from_name) + { + // "group_offer" is passed by LLOpenTaskGroupOffer + // Notification about added landmark will be generated under the "from_name.empty()" called from LLOpenTaskOffer::done(). + LLSD args; + args["type"] = "landmark"; + args["id"] = obj_id; + LLSideTray::getInstance()->showPanel("panel_places", args); + + continue; + } + else if(from_name.empty()) + { + std::string folder_name; + if (parent_folder) + { + // Localize folder name. + // *TODO: share this code? + folder_name = parent_folder->getName(); + if (LLFolderType::lookupIsProtectedType(parent_folder->getPreferredType())) + { + LLTrans::findString(folder_name, "InvFolder " + folder_name); + } + } + else + { + folder_name = LLTrans::getString("Unknown"); + } + + // we receive a message from LLOpenTaskOffer, it mean that new landmark has been added. + LLSD args; + args["LANDMARK_NAME"] = item->getName(); + args["FOLDER_NAME"] = folder_name; + LLNotificationsUtil::add("LandmarkCreated", args); + } + } + break; + case LLAssetType::AT_TEXTURE: + { + LLFloaterReg::showInstance("preview_texture", LLSD(obj_id), take_focus); + break; + } + case LLAssetType::AT_ANIMATION: + LLFloaterReg::showInstance("preview_anim", LLSD(obj_id), take_focus); + break; + case LLAssetType::AT_SCRIPT: + LLFloaterReg::showInstance("preview_script", LLSD(obj_id), take_focus); + break; + case LLAssetType::AT_SOUND: + LLFloaterReg::showInstance("preview_sound", LLSD(obj_id), take_focus); + break; + default: + break; + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + // Highlight item + const BOOL auto_open = + gSavedSettings.getBOOL("ShowInInventory") && // don't open if showininventory is false + !(asset_type == LLAssetType::AT_CALLINGCARD) && // don't open if it's a calling card + !from_name.empty(); // don't open if it's not from anyone. + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(auto_open); + if(active_panel) + { + LL_DEBUGS("Messaging") << "Highlighting" << obj_id << LL_ENDL; + LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus(); + active_panel->setSelection(obj_id, TAKE_FOCUS_NO); + gFocusMgr.setKeyboardFocus(focus_ctrl); + } + } +} + +bool highlight_offered_object(const LLUUID& obj_id) +{ + const LLInventoryObject* obj = gInventory.getObject(obj_id); + if(!obj) + { + LL_WARNS("Messaging") << "Unable to show inventory item: " << obj_id << LL_ENDL; + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + // Don't highlight if it's in certain "quiet" folders which don't need UI + // notification (e.g. trash, cof, lost-and-found). + if(!gAgent.getAFK()) + { + const LLViewerInventoryCategory *parent = gInventory.getFirstNondefaultParent(obj_id); + if (parent) + { + const LLFolderType::EType parent_type = parent->getPreferredType(); + if (LLViewerFolderType::lookupIsQuietType(parent_type)) + { + return false; + } + } + } + + return true; +} + +void inventory_offer_mute_callback(const LLUUID& blocked_id, + const std::string& full_name, + bool is_group, + boost::shared_ptr offer_ptr) +{ + LLOfferInfo* offer = dynamic_cast(offer_ptr.get()); + + std::string from_name = full_name; + LLMute::EType type; + if (is_group) + { + type = LLMute::GROUP; + } + else if(offer && offer->mFromObject) + { + //we have to block object by name because blocked_id is an id of owner + type = LLMute::BY_NAME; + } + else + { + type = LLMute::AGENT; + } + + // id should be null for BY_NAME mute, see LLMuteList::add for details + LLMute mute(type == LLMute::BY_NAME ? LLUUID::null : blocked_id, from_name, type); + if (LLMuteList::getInstance()->add(mute)) + { + LLPanelBlockedList::showPanelAndSelect(blocked_id); + } + + // purge the message queue of any previously queued inventory offers from the same source. + class OfferMatcher : public LLNotificationsUI::LLScreenChannel::Matcher + { + public: + OfferMatcher(const LLUUID& to_block) : blocked_id(to_block) {} + bool matches(const LLNotificationPtr notification) const + { + if(notification->getName() == "ObjectGiveItem" + || notification->getName() == "UserGiveItem") + { + return (notification->getPayload()["from_id"].asUUID() == blocked_id); + } + return FALSE; + } + private: + const LLUUID& blocked_id; + }; + + LLNotificationsUI::LLChannelManager::getInstance()->killToastsFromChannel(LLUUID( + gSavedSettings.getString("NotificationChannelUUID")), OfferMatcher(blocked_id)); +} + +LLOfferInfo::LLOfferInfo() + : LLNotificationResponderInterface() + , mFromGroup(FALSE) + , mFromObject(FALSE) + , mIM(IM_NOTHING_SPECIAL) + , mType(LLAssetType::AT_NONE) + , mPersist(false) +{ +} + +LLOfferInfo::LLOfferInfo(const LLSD& sd) +{ + mIM = (EInstantMessage)sd["im_type"].asInteger(); + mFromID = sd["from_id"].asUUID(); + mFromGroup = sd["from_group"].asBoolean(); + mFromObject = sd["from_object"].asBoolean(); + mTransactionID = sd["transaction_id"].asUUID(); + mFolderID = sd["folder_id"].asUUID(); + mObjectID = sd["object_id"].asUUID(); + mType = LLAssetType::lookup(sd["type"].asString().c_str()); + mFromName = sd["from_name"].asString(); + mDesc = sd["description"].asString(); + mHost = LLHost(sd["sender"].asString()); + mPersist = sd["persist"].asBoolean(); +} + +LLOfferInfo::LLOfferInfo(const LLOfferInfo& info) +{ + mIM = info.mIM; + mFromID = info.mFromID; + mFromGroup = info.mFromGroup; + mFromObject = info.mFromObject; + mTransactionID = info.mTransactionID; + mFolderID = info.mFolderID; + mObjectID = info.mObjectID; + mType = info.mType; + mFromName = info.mFromName; + mDesc = info.mDesc; + mHost = info.mHost; + mPersist = info.mPersist; +} + +LLSD LLOfferInfo::asLLSD() +{ + LLSD sd; + sd["im_type"] = mIM; + sd["from_id"] = mFromID; + sd["from_group"] = mFromGroup; + sd["from_object"] = mFromObject; + sd["transaction_id"] = mTransactionID; + sd["folder_id"] = mFolderID; + sd["object_id"] = mObjectID; + sd["type"] = LLAssetType::lookup(mType); + sd["from_name"] = mFromName; + sd["description"] = mDesc; + sd["sender"] = mHost.getIPandPort(); + sd["persist"] = mPersist; + return sd; +} + +void LLOfferInfo::fromLLSD(const LLSD& params) +{ + *this = params; +} + +void LLOfferInfo::send_auto_receive_response(void) +{ + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_ImprovedInstantMessage); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_MessageBlock); + msg->addBOOLFast(_PREHASH_FromGroup, FALSE); + msg->addUUIDFast(_PREHASH_ToAgentID, mFromID); + msg->addU8Fast(_PREHASH_Offline, IM_ONLINE); + msg->addUUIDFast(_PREHASH_ID, mTransactionID); + msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary + std::string name; + LLAgentUI::buildFullname(name); + msg->addStringFast(_PREHASH_FromAgentName, name); + msg->addStringFast(_PREHASH_Message, ""); + msg->addU32Fast(_PREHASH_ParentEstateID, 0); + msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null); + msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent()); + + // Auto Receive Message. The math for the dialog works, because the accept + // for inventory_offered, task_inventory_offer or + // group_notice_inventory is 1 greater than the offer integer value. + // Generates IM_INVENTORY_ACCEPTED, IM_TASK_INVENTORY_ACCEPTED, + // or IM_GROUP_NOTICE_INVENTORY_ACCEPTED + msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 1)); + msg->addBinaryDataFast(_PREHASH_BinaryBucket, &(mFolderID.mData), + sizeof(mFolderID.mData)); + // send the message + msg->sendReliable(mHost); + + if(IM_INVENTORY_OFFERED == mIM) + { + // add buddy to recent people list + LLRecentPeople::instance().add(mFromID); + } +} + +void LLOfferInfo::handleRespond(const LLSD& notification, const LLSD& response) +{ + initRespondFunctionMap(); + + const std::string name = notification["name"].asString(); + if(mRespondFunctions.find(name) == mRespondFunctions.end()) + { + llwarns << "Unexpected notification name : " << name << llendl; + llassert(!"Unexpected notification name"); + return; + } + + mRespondFunctions[name](notification, response); +} + +bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& response) +{ + LLChat chat; + std::string log_message; + S32 button = LLNotificationsUtil::getSelectedOption(notification, response); + + LLInventoryObserver* opener = NULL; + LLViewerInventoryCategory* catp = NULL; + catp = (LLViewerInventoryCategory*)gInventory.getCategory(mObjectID); + LLViewerInventoryItem* itemp = NULL; + if(!catp) + { + itemp = (LLViewerInventoryItem*)gInventory.getItem(mObjectID); + } + + // For muting, we need to add the mute, then decline the offer. + // This must be done here because: + // * callback may be called immediately, + // * adding the mute sends a message, + // * we can't build two messages at once. + if (2 == button) // Block + { + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); + + llassert(notification_ptr != NULL); + if (notification_ptr != NULL) + { + gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback,_1,_2,_3,notification_ptr->getResponderPtr())); + } + } + + std::string from_string; // Used in the pop-up. + std::string chatHistory_string; // Used in chat history. + + // TODO: when task inventory offers can also be handled the new way, migrate the code that sets these strings here: + from_string = chatHistory_string = mFromName; + + bool busy=FALSE; + + switch(button) + { + case IOR_SHOW: + // we will want to open this item when it comes back. + LL_DEBUGS("Messaging") << "Initializing an opener for tid: " << mTransactionID + << LL_ENDL; + switch (mIM) + { + case IM_INVENTORY_OFFERED: + { + // This is an offer from an agent. In this case, the back + // end has already copied the items into your inventory, + // so we can fetch it out of our inventory. + LLOpenAgentOffer* open_agent_offer = new LLOpenAgentOffer(mObjectID, from_string); + open_agent_offer->startFetch(); + if(catp || (itemp && itemp->isFinished())) + { + open_agent_offer->done(); + } + else + { + opener = open_agent_offer; + } + } + break; + case IM_GROUP_NOTICE: + opener = new LLOpenTaskGroupOffer; + send_auto_receive_response(); + break; + case IM_TASK_INVENTORY_OFFERED: + case IM_GROUP_NOTICE_REQUESTED: + // This is an offer from a task or group. + // We don't use a new instance of an opener + // We instead use the singular observer gOpenTaskOffer + // Since it already exists, we don't need to actually do anything + break; + default: + LL_WARNS("Messaging") << "inventory_offer_callback: unknown offer type" << LL_ENDL; + break; + } + break; + // end switch (mIM) + + case IOR_ACCEPT: + //don't spam them if they are getting flooded + if (check_offer_throttle(mFromName, true)) + { + log_message = chatHistory_string + " " + LLTrans::getString("InvOfferGaveYou") + " " + mDesc + LLTrans::getString("."); + LLSD args; + args["MESSAGE"] = log_message; + LLNotificationsUtil::add("SystemMessage", args); + } + break; + + case IOR_BUSY: + //Busy falls through to decline. Says to make busy message. + busy=TRUE; + case IOR_MUTE: + // MUTE falls through to decline + case IOR_DECLINE: + { + { + LLStringUtil::format_map_t log_message_args; + log_message_args["DESC"] = mDesc; + log_message_args["NAME"] = mFromName; + log_message = LLTrans::getString("InvOfferDecline", log_message_args); + } + chat.mText = log_message; + if( LLMuteList::getInstance()->isMuted(mFromID ) && ! LLMuteList::getInstance()->isLinden(mFromName) ) // muting for SL-42269 + { + chat.mMuted = TRUE; + } + + // *NOTE dzaporozhan + // Disabled logging to old chat floater to fix crash in group notices - EXT-4149 + // LLFloaterChat::addChatHistory(chat); + + LLDiscardAgentOffer* discard_agent_offer = new LLDiscardAgentOffer(mFolderID, mObjectID); + discard_agent_offer->startFetch(); + if (catp || (itemp && itemp->isFinished())) + { + discard_agent_offer->done(); + } + else + { + opener = discard_agent_offer; + } + + + if (busy && (!mFromGroup && !mFromObject)) + { + busy_message(gMessageSystem, mFromID); + } + break; + } + default: + // close button probably + // The item has already been fetched and is in your inventory, we simply won't highlight it + // OR delete it if the notification gets killed, since we don't want that to be a vector for + // losing inventory offers. + break; + } + + if(opener) + { + gInventory.addObserver(opener); + } + + if(!mPersist) + { + delete this; + } + return false; +} + +bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const LLSD& response) +{ + LLChat chat; + std::string log_message; + S32 button = LLNotification::getSelectedOption(notification, response); + + // For muting, we need to add the mute, then decline the offer. + // This must be done here because: + // * callback may be called immediately, + // * adding the mute sends a message, + // * we can't build two messages at once. + if (2 == button) + { + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); + + llassert(notification_ptr != NULL); + if (notification_ptr != NULL) + { + gCacheName->get(mFromID, mFromGroup, boost::bind(&inventory_offer_mute_callback,_1,_2,_3,notification_ptr->getResponderPtr())); + } + } + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_ImprovedInstantMessage); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_MessageBlock); + msg->addBOOLFast(_PREHASH_FromGroup, FALSE); + msg->addUUIDFast(_PREHASH_ToAgentID, mFromID); + msg->addU8Fast(_PREHASH_Offline, IM_ONLINE); + msg->addUUIDFast(_PREHASH_ID, mTransactionID); + msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary + std::string name; + LLAgentUI::buildFullname(name); + msg->addStringFast(_PREHASH_FromAgentName, name); + msg->addStringFast(_PREHASH_Message, ""); + msg->addU32Fast(_PREHASH_ParentEstateID, 0); + msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null); + msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent()); + LLInventoryObserver* opener = NULL; + + std::string from_string; // Used in the pop-up. + std::string chatHistory_string; // Used in chat history. + if (mFromObject == TRUE) + { + if (mFromGroup) + { + std::string group_name; + if (gCacheName->getGroupName(mFromID, group_name)) + { + from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+"'" + + mFromName + LLTrans::getString("'") +" " + LLTrans::getString("InvOfferOwnedByGroup") + + " "+ "'" + group_name + "'"; + + chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedByGroup") + + " " + group_name + "'"; + } + else + { + from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+"'" + + mFromName +"'"+ " " + LLTrans::getString("InvOfferOwnedByUnknownGroup"); + chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedByUnknownGroup"); + } + } + else + { + std::string full_name; + if (gCacheName->getFullName(mFromID, full_name)) + { + from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+ LLTrans::getString("'") + mFromName + + LLTrans::getString("'")+" " + LLTrans::getString("InvOfferOwnedBy") + full_name; + chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedBy") + " " + full_name; + } + else + { + from_string = LLTrans::getString("InvOfferAnObjectNamed") + " "+LLTrans::getString("'") + + mFromName + LLTrans::getString("'")+" " + LLTrans::getString("InvOfferOwnedByUnknownUser"); + chatHistory_string = mFromName + " " + LLTrans::getString("InvOfferOwnedByUnknownUser"); + } + } + } + else + { + from_string = chatHistory_string = mFromName; + } + + bool busy=FALSE; + + switch(button) + { + case IOR_ACCEPT: + // ACCEPT. The math for the dialog works, because the accept + // for inventory_offered, task_inventory_offer or + // group_notice_inventory is 1 greater than the offer integer value. + // Generates IM_INVENTORY_ACCEPTED, IM_TASK_INVENTORY_ACCEPTED, + // or IM_GROUP_NOTICE_INVENTORY_ACCEPTED + msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 1)); + msg->addBinaryDataFast(_PREHASH_BinaryBucket, &(mFolderID.mData), + sizeof(mFolderID.mData)); + // send the message + msg->sendReliable(mHost); + + //don't spam them if they are getting flooded + if (check_offer_throttle(mFromName, true)) + { + log_message = chatHistory_string + " " + LLTrans::getString("InvOfferGaveYou") + " " + mDesc + LLTrans::getString("."); + LLSD args; + args["MESSAGE"] = log_message; + LLNotificationsUtil::add("SystemMessage", args); + } + + // we will want to open this item when it comes back. + LL_DEBUGS("Messaging") << "Initializing an opener for tid: " << mTransactionID + << LL_ENDL; + switch (mIM) + { + case IM_TASK_INVENTORY_OFFERED: + case IM_GROUP_NOTICE: + case IM_GROUP_NOTICE_REQUESTED: + { + // This is an offer from a task or group. + // We don't use a new instance of an opener + // We instead use the singular observer gOpenTaskOffer + // Since it already exists, we don't need to actually do anything + } + break; + default: + LL_WARNS("Messaging") << "inventory_offer_callback: unknown offer type" << LL_ENDL; + break; + } // end switch (mIM) + break; + + case IOR_BUSY: + //Busy falls through to decline. Says to make busy message. + busy=TRUE; + case IOR_MUTE: + // MUTE falls through to decline + case IOR_DECLINE: + // DECLINE. The math for the dialog works, because the decline + // for inventory_offered, task_inventory_offer or + // group_notice_inventory is 2 greater than the offer integer value. + // Generates IM_INVENTORY_DECLINED, IM_TASK_INVENTORY_DECLINED, + // or IM_GROUP_NOTICE_INVENTORY_DECLINED + default: + // close button probably (or any of the fall-throughs from above) + msg->addU8Fast(_PREHASH_Dialog, (U8)(mIM + 2)); + msg->addBinaryDataFast(_PREHASH_BinaryBucket, EMPTY_BINARY_BUCKET, EMPTY_BINARY_BUCKET_SIZE); + // send the message + msg->sendReliable(mHost); + + if (gSavedSettings.getBOOL("LogInventoryDecline")) + { + LLStringUtil::format_map_t log_message_args; + log_message_args["DESC"] = mDesc; + log_message_args["NAME"] = mFromName; + log_message = LLTrans::getString("InvOfferDecline", log_message_args); + + LLSD args; + args["MESSAGE"] = log_message; + LLNotificationsUtil::add("SystemMessage", args); + } + + if (busy && (!mFromGroup && !mFromObject)) + { + busy_message(msg,mFromID); + } + break; + } + + if(opener) + { + gInventory.addObserver(opener); + } + + if(!mPersist) + { + delete this; + } + return false; +} + +class LLPostponedOfferNotification: public LLPostponedNotification +{ +protected: + /* virtual */ + void modifyNotificationParams() + { + LLSD substitutions = mParams.substitutions; + substitutions["NAME"] = mName; + mParams.substitutions = substitutions; + } +}; + +void LLOfferInfo::initRespondFunctionMap() +{ + if(mRespondFunctions.empty()) + { + mRespondFunctions["ObjectGiveItem"] = boost::bind(&LLOfferInfo::inventory_task_offer_callback, this, _1, _2); + mRespondFunctions["UserGiveItem"] = boost::bind(&LLOfferInfo::inventory_offer_callback, this, _1, _2); + } +} + +void inventory_offer_handler(LLOfferInfo* info) +{ + //Until throttling is implmented, busy mode should reject inventory instead of silently + //accepting it. SEE SL-39554 + if (gAgent.getBusy()) + { + info->forceResponse(IOR_BUSY); + return; + } + + //If muted, don't even go through the messaging stuff. Just curtail the offer here. + if (LLMuteList::getInstance()->isMuted(info->mFromID, info->mFromName)) + { + info->forceResponse(IOR_MUTE); + return; + } + + // Avoid the Accept/Discard dialog if the user so desires. JC + if (gSavedSettings.getBOOL("AutoAcceptNewInventory") + && (info->mType == LLAssetType::AT_NOTECARD + || info->mType == LLAssetType::AT_LANDMARK + || info->mType == LLAssetType::AT_TEXTURE)) + { + // For certain types, just accept the items into the inventory, + // and possibly open them on receipt depending upon "ShowNewInventory". + info->forceResponse(IOR_ACCEPT); + return; + } + + // Strip any SLURL from the message display. (DEV-2754) + std::string msg = info->mDesc; + int indx = msg.find(" ( http://slurl.com/secondlife/"); + if(indx == std::string::npos) + { + // try to find new slurl host + indx = msg.find(" ( http://maps.secondlife.com/secondlife/"); + } + if(indx >= 0) + { + LLStringUtil::truncate(msg, indx); + } + + LLSD args; + args["[OBJECTNAME]"] = msg; + + LLSD payload; + + // must protect against a NULL return from lookupHumanReadable() + std::string typestr = ll_safe_string(LLAssetType::lookupHumanReadable(info->mType)); + if (!typestr.empty()) + { + // human readable matches string name from strings.xml + // lets get asset type localized name + args["OBJECTTYPE"] = LLTrans::getString(typestr); + } + else + { + LL_WARNS("Messaging") << "LLAssetType::lookupHumanReadable() returned NULL - probably bad asset type: " << info->mType << LL_ENDL; + args["OBJECTTYPE"] = ""; + + // This seems safest, rather than propagating bogosity + LL_WARNS("Messaging") << "Forcing an inventory-decline for probably-bad asset type." << LL_ENDL; + info->forceResponse(IOR_DECLINE); + return; + } + + // If mObjectID is null then generate the object_id based on msg to prevent + // multiple creation of chiclets for same object. + LLUUID object_id = info->mObjectID; + if (object_id.isNull()) + object_id.generate(msg); + + payload["from_id"] = info->mFromID; + // Needed by LLScriptFloaterManager to bind original notification with + // faked for toast one. + payload["object_id"] = object_id; + // Flag indicating that this notification is faked for toast. + payload["give_inventory_notification"] = FALSE; + args["OBJECTFROMNAME"] = info->mFromName; + args["NAME"] = info->mFromName; + if (info->mFromGroup) + { + args["NAME_SLURL"] = LLSLURL("group", info->mFromID, "about").getSLURLString(); + } + else + { + args["NAME_SLURL"] = LLSLURL("agent", info->mFromID, "about").getSLURLString(); + } + std::string verb = "select?name=" + LLURI::escape(msg); + args["ITEM_SLURL"] = LLSLURL("inventory", info->mObjectID, verb.c_str()).getSLURLString(); + + LLNotification::Params p("ObjectGiveItem"); + + // Object -> Agent Inventory Offer + if (info->mFromObject) + { + // Inventory Slurls don't currently work for non agent transfers, so only display the object name. + args["ITEM_SLURL"] = msg; + // Note: sets inventory_task_offer_callback as the callback + p.substitutions(args).payload(payload).functor.responder(LLNotificationResponderPtr(info)); + info->mPersist = true; + p.name = "ObjectGiveItem"; + // Pop up inv offer chiclet and let the user accept (keep), or reject (and silently delete) the inventory. + LLPostponedNotification::add(p, info->mFromID, info->mFromGroup == TRUE); + } + else // Agent -> Agent Inventory Offer + { + p.responder = info; + // Note: sets inventory_offer_callback as the callback + // *TODO fix memory leak + // inventory_offer_callback() is not invoked if user received notification and + // closes viewer(without responding the notification) + p.substitutions(args).payload(payload).functor.responder(LLNotificationResponderPtr(info)); + info->mPersist = true; + p.name = "UserGiveItem"; + + // Prefetch the item into your local inventory. + LLInventoryFetchItemsObserver* fetch_item = new LLInventoryFetchItemsObserver(info->mObjectID); + fetch_item->startFetch(); + if(fetch_item->isFinished()) + { + fetch_item->done(); + } + else + { + gInventory.addObserver(fetch_item); + } + + // In viewer 2 we're now auto receiving inventory offers and messaging as such (not sending reject messages). + info->send_auto_receive_response(); + + // Inform user that there is a script floater via toast system + { + payload["give_inventory_notification"] = TRUE; + p.payload = payload; + LLPostponedNotification::add(p, info->mFromID, false); + } + } + + LLFirstUse::newInventory(); +} + +bool lure_callback(const LLSD& notification, const LLSD& response) +{ + S32 option = 0; + if (response.isInteger()) + { + option = response.asInteger(); + } + else + { + option = LLNotificationsUtil::getSelectedOption(notification, response); + } + + LLUUID from_id = notification["payload"]["from_id"].asUUID(); + LLUUID lure_id = notification["payload"]["lure_id"].asUUID(); + BOOL godlike = notification["payload"]["godlike"].asBoolean(); + + switch(option) + { + case 0: + { + // accept + gAgent.teleportViaLure(lure_id, godlike); + } + break; + case 1: + default: + // decline + send_simple_im(from_id, + LLStringUtil::null, + IM_LURE_DECLINED, + lure_id); + break; + } + return false; +} +static LLNotificationFunctorRegistration lure_callback_reg("TeleportOffered", lure_callback); + +bool goto_url_callback(const LLSD& notification, const LLSD& response) +{ + std::string url = notification["payload"]["url"].asString(); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if(1 == option) + { + LLWeb::loadURL(url); + } + return false; +} +static LLNotificationFunctorRegistration goto_url_callback_reg("GotoURL", goto_url_callback); + +bool inspect_remote_object_callback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 == option) + { + LLFloaterReg::showInstance("inspect_remote_object", notification["payload"]); + } + return false; +} +static LLNotificationFunctorRegistration inspect_remote_object_callback_reg("ServerObjectMessage", inspect_remote_object_callback); + +class LLPostponedServerObjectNotification: public LLPostponedNotification +{ +protected: + /* virtual */ + void modifyNotificationParams() + { + LLSD payload = mParams.payload; + mParams.payload = payload; + } +}; + +static bool parse_lure_bucket(const std::string& bucket, + U64& region_handle, + LLVector3& pos, + LLVector3& look_at, + U8& region_access) +{ + // tokenize the bucket + typedef boost::tokenizer > tokenizer; + boost::char_separator sep("|", "", boost::keep_empty_tokens); + tokenizer tokens(bucket, sep); + tokenizer::iterator iter = tokens.begin(); + + S32 gx,gy,rx,ry,rz,lx,ly,lz; + try + { + gx = boost::lexical_cast((*(iter)).c_str()); + gy = boost::lexical_cast((*(++iter)).c_str()); + rx = boost::lexical_cast((*(++iter)).c_str()); + ry = boost::lexical_cast((*(++iter)).c_str()); + rz = boost::lexical_cast((*(++iter)).c_str()); + lx = boost::lexical_cast((*(++iter)).c_str()); + ly = boost::lexical_cast((*(++iter)).c_str()); + lz = boost::lexical_cast((*(++iter)).c_str()); + } + catch( boost::bad_lexical_cast& ) + { + LL_WARNS("parse_lure_bucket") + << "Couldn't parse lure bucket." + << LL_ENDL; + return false; + } + // Grab region access + region_access = SIM_ACCESS_MIN; + if (++iter != tokens.end()) + { + std::string access_str((*iter).c_str()); + LLStringUtil::trim(access_str); + if ( access_str == "A" ) + { + region_access = SIM_ACCESS_ADULT; + } + else if ( access_str == "M" ) + { + region_access = SIM_ACCESS_MATURE; + } + else if ( access_str == "PG" ) + { + region_access = SIM_ACCESS_PG; + } + } + + pos.setVec((F32)rx, (F32)ry, (F32)rz); + look_at.setVec((F32)lx, (F32)ly, (F32)lz); + + region_handle = to_region_handle(gx, gy); + return true; +} + +// Strip out "Resident" for display, but only if the message came from a user +// (rather than a script) +static std::string clean_name_from_im(const std::string& name, EInstantMessage type) +{ + switch(type) + { + case IM_NOTHING_SPECIAL: + case IM_MESSAGEBOX: + case IM_GROUP_INVITATION: + case IM_INVENTORY_OFFERED: + case IM_INVENTORY_ACCEPTED: + case IM_INVENTORY_DECLINED: + case IM_GROUP_VOTE: + case IM_GROUP_MESSAGE_DEPRECATED: + //IM_TASK_INVENTORY_OFFERED + //IM_TASK_INVENTORY_ACCEPTED + //IM_TASK_INVENTORY_DECLINED + case IM_NEW_USER_DEFAULT: + case IM_SESSION_INVITE: + case IM_SESSION_P2P_INVITE: + case IM_SESSION_GROUP_START: + case IM_SESSION_CONFERENCE_START: + case IM_SESSION_SEND: + case IM_SESSION_LEAVE: + //IM_FROM_TASK + case IM_BUSY_AUTO_RESPONSE: + case IM_CONSOLE_AND_CHAT_HISTORY: + case IM_LURE_USER: + case IM_LURE_ACCEPTED: + case IM_LURE_DECLINED: + case IM_GODLIKE_LURE_USER: + case IM_YET_TO_BE_USED: + case IM_GROUP_ELECTION_DEPRECATED: + //IM_GOTO_URL + //IM_FROM_TASK_AS_ALERT + case IM_GROUP_NOTICE: + case IM_GROUP_NOTICE_INVENTORY_ACCEPTED: + case IM_GROUP_NOTICE_INVENTORY_DECLINED: + case IM_GROUP_INVITATION_ACCEPT: + case IM_GROUP_INVITATION_DECLINE: + case IM_GROUP_NOTICE_REQUESTED: + case IM_FRIENDSHIP_OFFERED: + case IM_FRIENDSHIP_ACCEPTED: + case IM_FRIENDSHIP_DECLINED_DEPRECATED: + //IM_TYPING_START + //IM_TYPING_STOP + return LLCacheName::cleanFullName(name); + default: + return name; + } +} + +static std::string clean_name_from_task_im(const std::string& msg, + BOOL from_group) +{ + boost::smatch match; + static const boost::regex returned_exp( + "(.*been returned to your inventory lost and found folder by )(.+)( (from|near).*)"); + if (boost::regex_match(msg, match, returned_exp)) + { + // match objects are 1-based for groups + std::string final = match[1].str(); + std::string name = match[2].str(); + // Don't try to clean up group names + if (!from_group) + { + if (LLAvatarNameCache::useDisplayNames()) + { + // ...just convert to username + final += LLCacheName::buildUsername(name); + } + else + { + // ...strip out legacy "Resident" name + final += LLCacheName::cleanFullName(name); + } + } + final += match[3].str(); + return final; + } + return msg; +} + +void notification_display_name_callback(const LLUUID& id, + const LLAvatarName& av_name, + const std::string& name, + LLSD& substitutions, + const LLSD& payload) +{ + substitutions["NAME"] = av_name.mDisplayName; + LLNotificationsUtil::add(name, substitutions, payload); +} + +class LLPostponedIMSystemTipNotification: public LLPostponedNotification +{ +protected: + /* virtual */ + void modifyNotificationParams() + { + LLSD payload = mParams.payload; + payload["SESSION_NAME"] = mName; + mParams.payload = payload; + } + +}; + +// Callback for name resolution of a god/estate message +void god_message_name_cb(const LLAvatarName& av_name, LLChat chat, std::string message) +{ + LLSD args; + args["NAME"] = av_name.getCompleteName(); + args["MESSAGE"] = message; + LLNotificationsUtil::add("GodMessage", args); + + // Treat like a system message and put in chat history. + chat.mText = av_name.getCompleteName() + ": " + message; + + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat", LLSD()); + if(nearby_chat) + { + nearby_chat->addMessage(chat); + } + +} + +void process_improved_im(LLMessageSystem *msg, void **user_data) +{ + if (gNoRender) + { + return; + } + LLUUID from_id; + BOOL from_group; + LLUUID to_id; + U8 offline; + U8 d = 0; + LLUUID session_id; + U32 timestamp; + std::string name; + std::string message; + U32 parent_estate_id = 0; + LLUUID region_id; + LLVector3 position; + U8 binary_bucket[MTUBYTES]; + S32 binary_bucket_size; + LLChat chat; + std::string buffer; + + // *TODO: Translate - need to fix the full name to first/last (maybe) + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, from_id); + msg->getBOOLFast(_PREHASH_MessageBlock, _PREHASH_FromGroup, from_group); + msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ToAgentID, to_id); + msg->getU8Fast( _PREHASH_MessageBlock, _PREHASH_Offline, offline); + msg->getU8Fast( _PREHASH_MessageBlock, _PREHASH_Dialog, d); + msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ID, session_id); + msg->getU32Fast( _PREHASH_MessageBlock, _PREHASH_Timestamp, timestamp); + //msg->getData("MessageBlock", "Count", &count); + msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_FromAgentName, name); + msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_Message, message); + msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_ParentEstateID, parent_estate_id); + msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_RegionID, region_id); + msg->getVector3Fast(_PREHASH_MessageBlock, _PREHASH_Position, position); + msg->getBinaryDataFast( _PREHASH_MessageBlock, _PREHASH_BinaryBucket, binary_bucket, 0, 0, MTUBYTES); + binary_bucket_size = msg->getSizeFast(_PREHASH_MessageBlock, _PREHASH_BinaryBucket); + EInstantMessage dialog = (EInstantMessage)d; + + // make sure that we don't have an empty or all-whitespace name + LLStringUtil::trim(name); + if (name.empty()) + { + name = LLTrans::getString("Unnamed"); + } + // IDEVO convert new-style "Resident" names for display + name = clean_name_from_im(name, dialog); + + BOOL is_busy = gAgent.getBusy(); + BOOL is_muted = LLMuteList::getInstance()->isMuted(from_id, name, LLMute::flagTextChat); + BOOL is_linden = LLMuteList::getInstance()->isLinden(name); + BOOL is_owned_by_me = FALSE; + BOOL is_friend = (LLAvatarTracker::instance().getBuddyInfo(from_id) == NULL) ? false : true; + BOOL accept_im_from_only_friend = gSavedSettings.getBOOL("VoiceCallsFriendsOnly"); + + chat.mMuted = is_muted && !is_linden; + chat.mFromID = from_id; + chat.mFromName = name; + chat.mSourceType = (from_id.isNull() || (name == std::string(SYSTEM_FROM))) ? CHAT_SOURCE_SYSTEM : CHAT_SOURCE_AGENT; + + LLViewerObject *source = gObjectList.findObject(session_id); //Session ID is probably the wrong thing. + if (source) + { + is_owned_by_me = source->permYouOwner(); + } + + std::string separator_string(": "); + + LLSD args; + LLSD payload; + LLNotification::Params params; + + switch(dialog) + { + case IM_CONSOLE_AND_CHAT_HISTORY: + args["MESSAGE"] = message; + payload["from_id"] = from_id; + + params.name = "IMSystemMessageTip"; + params.substitutions = args; + params.payload = payload; + LLPostponedNotification::add(params, from_id, false); + break; + + case IM_NOTHING_SPECIAL: + // Don't show dialog, just do IM + if (!gAgent.isGodlike() + && gAgent.getRegion()->isPrelude() + && to_id.isNull() ) + { + // do nothing -- don't distract newbies in + // Prelude with global IMs + } + else if (offline == IM_ONLINE && !is_linden && is_busy && name != SYSTEM_FROM) + { + // return a standard "busy" message, but only do it to online IM + // (i.e. not other auto responses and not store-and-forward IM) + if (!gIMMgr->hasSession(session_id)) + { + // if there is not a panel for this conversation (i.e. it is a new IM conversation + // initiated by the other party) then... + std::string my_name; + LLAgentUI::buildFullname(my_name); + std::string response = gSavedPerAccountSettings.getString("BusyModeResponse"); + pack_instant_message( + gMessageSystem, + gAgent.getID(), + FALSE, + gAgent.getSessionID(), + from_id, + my_name, + response, + IM_ONLINE, + IM_BUSY_AUTO_RESPONSE, + session_id); + gAgent.sendReliableMessage(); + } + + // now store incoming IM in chat history + + buffer = message; + + LL_INFOS("Messaging") << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << LL_ENDL; + + // add to IM panel, but do not bother the user + gIMMgr->addMessage( + session_id, + from_id, + name, + buffer, + LLStringUtil::null, + dialog, + parent_estate_id, + region_id, + position, + true); + } + else if (from_id.isNull()) + { + LLSD args; + args["MESSAGE"] = message; + LLNotificationsUtil::add("SystemMessage", args); + } + else if (to_id.isNull()) + { + // Message to everyone from GOD, look up the fullname since + // server always slams name to legacy names + LLAvatarNameCache::get(from_id, boost::bind(god_message_name_cb, _2, chat, message)); + } + else + { + // standard message, not from system + std::string saved; + if(offline == IM_OFFLINE) + { + LLStringUtil::format_map_t args; + args["[LONG_TIMESTAMP]"] = formatted_time(timestamp); + saved = LLTrans::getString("Saved_message", args); + } + buffer = saved + message; + + LL_INFOS("Messaging") << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << LL_ENDL; + + bool mute_im = is_muted; + if(accept_im_from_only_friend&&!is_friend) + { + mute_im = true; + } + if (!mute_im || is_linden) + { + gIMMgr->addMessage( + session_id, + from_id, + name, + buffer, + LLStringUtil::null, + dialog, + parent_estate_id, + region_id, + position, + true); + } + else + { + /* + EXT-5099 + currently there is no way to store in history only... + using LLNotificationsUtil::add will add message to Nearby Chat + + // muted user, so don't start an IM session, just record line in chat + // history. Pretend the chat is from a local agent, + // so it will go into the history but not be shown on screen. + + LLSD args; + args["MESSAGE"] = buffer; + LLNotificationsUtil::add("SystemMessageTip", args); + */ + } + } + break; + + case IM_TYPING_START: + { + LLPointer im_info = new LLIMInfo(gMessageSystem); + gIMMgr->processIMTypingStart(im_info); + } + break; + + case IM_TYPING_STOP: + { + LLPointer im_info = new LLIMInfo(gMessageSystem); + gIMMgr->processIMTypingStop(im_info); + } + break; + + case IM_MESSAGEBOX: + { + // This is a block, modeless dialog. + //*TODO: Translate + args["MESSAGE"] = message; + LLNotificationsUtil::add("SystemMessageTip", args); + } + break; + case IM_GROUP_NOTICE: + case IM_GROUP_NOTICE_REQUESTED: + { + LL_INFOS("Messaging") << "Received IM_GROUP_NOTICE message." << LL_ENDL; + // Read the binary bucket for more information. + struct notice_bucket_header_t + { + U8 has_inventory; + U8 asset_type; + LLUUID group_id; + }; + struct notice_bucket_full_t + { + struct notice_bucket_header_t header; + U8 item_name[DB_INV_ITEM_NAME_BUF_SIZE]; + }* notice_bin_bucket; + + // Make sure the binary bucket is big enough to hold the header + // and a null terminated item name. + if ( (binary_bucket_size < (S32)((sizeof(notice_bucket_header_t) + sizeof(U8)))) + || (binary_bucket[binary_bucket_size - 1] != '\0') ) + { + LL_WARNS("Messaging") << "Malformed group notice binary bucket" << LL_ENDL; + break; + } + + notice_bin_bucket = (struct notice_bucket_full_t*) &binary_bucket[0]; + U8 has_inventory = notice_bin_bucket->header.has_inventory; + U8 asset_type = notice_bin_bucket->header.asset_type; + LLUUID group_id = notice_bin_bucket->header.group_id; + std::string item_name = ll_safe_string((const char*) notice_bin_bucket->item_name); + + // If there is inventory, give the user the inventory offer. + LLOfferInfo* info = NULL; + + if (has_inventory) + { + info = new LLOfferInfo(); + + info->mIM = IM_GROUP_NOTICE; + info->mFromID = from_id; + info->mFromGroup = from_group; + info->mTransactionID = session_id; + info->mType = (LLAssetType::EType) asset_type; + info->mFolderID = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(info->mType)); + std::string from_name; + + from_name += "A group member named "; + from_name += name; + + info->mFromName = from_name; + info->mDesc = item_name; + info->mHost = msg->getSender(); + } + + std::string str(message); + + // Tokenize the string. + // TODO: Support escaped tokens ("||" -> "|") + typedef boost::tokenizer > tokenizer; + boost::char_separator sep("|","",boost::keep_empty_tokens); + tokenizer tokens(str, sep); + tokenizer::iterator iter = tokens.begin(); + + std::string subj(*iter++); + std::string mes(*iter++); + + // Send the notification down the new path. + // For requested notices, we don't want to send the popups. + if (dialog != IM_GROUP_NOTICE_REQUESTED) + { + payload["subject"] = subj; + payload["message"] = mes; + payload["sender_name"] = name; + payload["group_id"] = group_id; + payload["inventory_name"] = item_name; + payload["inventory_offer"] = info ? info->asLLSD() : LLSD(); + + LLSD args; + args["SUBJECT"] = subj; + args["MESSAGE"] = mes; + LLNotifications::instance().add(LLNotification::Params("GroupNotice").substitutions(args).payload(payload).time_stamp(timestamp)); + } + + // Also send down the old path for now. + if (IM_GROUP_NOTICE_REQUESTED == dialog) + { + + LLPanelGroup::showNotice(subj,mes,group_id,has_inventory,item_name,info); + } + else + { + delete info; + } + } + break; + case IM_GROUP_INVITATION: + { + //if (!is_linden && (is_busy || is_muted)) + if ((is_busy || is_muted)) + { + LLMessageSystem *msg = gMessageSystem; + busy_message(msg,from_id); + } + else + { + LL_INFOS("Messaging") << "Received IM_GROUP_INVITATION message." << LL_ENDL; + // Read the binary bucket for more information. + struct invite_bucket_t + { + S32 membership_fee; + LLUUID role_id; + }* invite_bucket; + + // Make sure the binary bucket is the correct size. + if (binary_bucket_size != sizeof(invite_bucket_t)) + { + LL_WARNS("Messaging") << "Malformed group invite binary bucket" << LL_ENDL; + break; + } + + invite_bucket = (struct invite_bucket_t*) &binary_bucket[0]; + S32 membership_fee = ntohl(invite_bucket->membership_fee); + + LLSD payload; + payload["transaction_id"] = session_id; + payload["group_id"] = from_id; + payload["name"] = name; + payload["message"] = message; + payload["fee"] = membership_fee; + + LLSD args; + args["MESSAGE"] = message; + // we shouldn't pass callback functor since it is registered in LLFunctorRegistration + LLNotificationsUtil::add("JoinGroup", args, payload); + } + } + break; + + case IM_INVENTORY_OFFERED: + case IM_TASK_INVENTORY_OFFERED: + // Someone has offered us some inventory. + { + LLOfferInfo* info = new LLOfferInfo; + if (IM_INVENTORY_OFFERED == dialog) + { + struct offer_agent_bucket_t + { + S8 asset_type; + LLUUID object_id; + }* bucketp; + + if (sizeof(offer_agent_bucket_t) != binary_bucket_size) + { + LL_WARNS("Messaging") << "Malformed inventory offer from agent" << LL_ENDL; + delete info; + break; + } + bucketp = (struct offer_agent_bucket_t*) &binary_bucket[0]; + info->mType = (LLAssetType::EType) bucketp->asset_type; + info->mObjectID = bucketp->object_id; + } + else + { + if (sizeof(S8) != binary_bucket_size) + { + LL_WARNS("Messaging") << "Malformed inventory offer from object" << LL_ENDL; + delete info; + break; + } + info->mType = (LLAssetType::EType) binary_bucket[0]; + info->mObjectID = LLUUID::null; + } + + info->mIM = dialog; + info->mFromID = from_id; + info->mFromGroup = from_group; + info->mTransactionID = session_id; + info->mFolderID = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(info->mType)); + + if (dialog == IM_TASK_INVENTORY_OFFERED) + { + info->mFromObject = TRUE; + } + else + { + info->mFromObject = FALSE; + } + info->mFromName = name; + info->mDesc = message; + info->mHost = msg->getSender(); + //if (((is_busy && !is_owned_by_me) || is_muted)) + if (is_muted) + { + // Prefetch the offered item so that it can be discarded by the appropriate observer. (EXT-4331) + LLInventoryFetchItemsObserver* fetch_item = new LLInventoryFetchItemsObserver(info->mObjectID); + fetch_item->startFetch(); + delete fetch_item; + + // Same as closing window + info->forceResponse(IOR_DECLINE); + } + else + { + inventory_offer_handler(info); + } + } + break; + + case IM_INVENTORY_ACCEPTED: + { + args["NAME"] = LLSLURL("agent", from_id, "completename").getSLURLString();; + LLSD payload; + payload["from_id"] = from_id; + LLNotificationsUtil::add("InventoryAccepted", args, payload); + break; + } + case IM_INVENTORY_DECLINED: + { + args["NAME"] = LLSLURL("agent", from_id, "completename").getSLURLString();; + LLSD payload; + payload["from_id"] = from_id; + LLNotificationsUtil::add("InventoryDeclined", args, payload); + break; + } + // TODO: _DEPRECATED suffix as part of vote removal - DEV-24856 + case IM_GROUP_VOTE: + { + LL_WARNS("Messaging") << "Received IM: IM_GROUP_VOTE_DEPRECATED" << LL_ENDL; + } + break; + + case IM_GROUP_ELECTION_DEPRECATED: + { + LL_WARNS("Messaging") << "Received IM: IM_GROUP_ELECTION_DEPRECATED" << LL_ENDL; + } + break; + + case IM_SESSION_SEND: + { + if (!is_linden && is_busy) + { + return; + } + + // Only show messages if we have a session open (which + // should happen after you get an "invitation" + if ( !gIMMgr->hasSession(session_id) ) + { + return; + } + + // standard message, not from system + std::string saved; + if(offline == IM_OFFLINE) + { + saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); + } + buffer = saved + message; + BOOL is_this_agent = FALSE; + if(from_id == gAgentID) + { + is_this_agent = TRUE; + } + gIMMgr->addMessage( + session_id, + from_id, + name, + buffer, + ll_safe_string((char*)binary_bucket), + IM_SESSION_INVITE, + parent_estate_id, + region_id, + position, + true); + } + break; + + case IM_FROM_TASK: + { + if (is_busy && !is_owned_by_me) + { + return; + } + + // Build a link to open the object IM info window. + std::string location = ll_safe_string((char*)binary_bucket, binary_bucket_size-1); + + if (session_id.notNull()) + { + chat.mFromID = session_id; + } + else + { + // This message originated on a region without the updated code for task id and slurl information. + // We just need a unique ID for this object that isn't the owner ID. + // If it is the owner ID it will overwrite the style that contains the link to that owner's profile. + // This isn't ideal - it will make 1 style for all objects owned by the the same person/group. + // This works because the only thing we can really do in this case is show the owner name and link to their profile. + chat.mFromID = from_id ^ gAgent.getSessionID(); + } + + chat.mSourceType = CHAT_SOURCE_OBJECT; + + if(SYSTEM_FROM == name) + { + // System's UUID is NULL (fixes EXT-4766) + chat.mFromID = LLUUID::null; + chat.mSourceType = CHAT_SOURCE_SYSTEM; + } + + // IDEVO Some messages have embedded resident names + message = clean_name_from_task_im(message, from_group); + + LLSD query_string; + query_string["owner"] = from_id; + query_string["slurl"] = location; + query_string["name"] = name; + if (from_group) + { + query_string["groupowned"] = "true"; + } + + chat.mURL = LLSLURL("objectim", session_id, "").getSLURLString(); + chat.mText = message; + + // Note: lie to Nearby Chat, pretending that this is NOT an IM, because + // IMs from obejcts don't open IM sessions. + LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat", LLSD()); + if(SYSTEM_FROM != name && nearby_chat) + { + chat.mOwnerID = from_id; + LLSD args; + args["slurl"] = location; + args["type"] = LLNotificationsUI::NT_NEARBYCHAT; + + // Look for IRC-style emotes here so object name formatting is correct + std::string prefix = message.substr(0, 4); + if (prefix == "/me " || prefix == "/me'") + { + chat.mChatStyle = CHAT_STYLE_IRC; + } + + LLNotificationsUI::LLNotificationManager::instance().onChat(chat, args); + } + + + //Object IMs send with from name: 'Second Life' need to be displayed also in notification toasts (EXT-1590) + if (SYSTEM_FROM != name) break; + + LLSD substitutions; + substitutions["NAME"] = name; + substitutions["MSG"] = message; + + LLSD payload; + payload["object_id"] = session_id; + payload["owner_id"] = from_id; + payload["from_id"] = from_id; + payload["slurl"] = location; + payload["name"] = name; + std::string session_name; + if (from_group) + { + payload["group_owned"] = "true"; + } + + LLNotification::Params params("ServerObjectMessage"); + params.substitutions = substitutions; + params.payload = payload; + + LLPostponedNotification::add(params, from_id, from_group); + } + break; + case IM_FROM_TASK_AS_ALERT: + if (is_busy && !is_owned_by_me) + { + return; + } + { + // Construct a viewer alert for this message. + args["NAME"] = name; + args["MESSAGE"] = message; + LLNotificationsUtil::add("ObjectMessage", args); + } + break; + case IM_BUSY_AUTO_RESPONSE: + if (is_muted) + { + LL_DEBUGS("Messaging") << "Ignoring busy response from " << from_id << LL_ENDL; + return; + } + else + { + // TODO: after LLTrans hits release, get "busy response" into translatable file + buffer = llformat("%s (%s): %s", name.c_str(), "busy response", message.c_str()); + gIMMgr->addMessage(session_id, from_id, name, buffer); + } + break; + + case IM_LURE_USER: + { + if (is_muted) + { + return; + } + else if (is_busy) + { + busy_message(msg,from_id); + } + else + { + LLVector3 pos, look_at; + U64 region_handle; + U8 region_access = SIM_ACCESS_MIN; + std::string region_info = ll_safe_string((char*)binary_bucket, binary_bucket_size); + std::string region_access_str = LLStringUtil::null; + std::string region_access_icn = LLStringUtil::null; + + if (parse_lure_bucket(region_info, region_handle, pos, look_at, region_access)) + { + region_access_str = LLViewerRegion::accessToString(region_access); + region_access_icn = LLViewerRegion::getAccessIcon(region_access); + } + + LLSD args; + // *TODO: Translate -> [FIRST] [LAST] (maybe) + args["NAME_SLURL"] = LLSLURL("agent", from_id, "about").getSLURLString(); + args["MESSAGE"] = message; + args["MATURITY_STR"] = region_access_str; + args["MATURITY_ICON"] = region_access_icn; + LLSD payload; + payload["from_id"] = from_id; + payload["lure_id"] = session_id; + payload["godlike"] = FALSE; + + LLNotification::Params params("TeleportOffered"); + params.substitutions = args; + params.payload = payload; + LLPostponedNotification::add( params, from_id, false); + } + } + break; + + case IM_GODLIKE_LURE_USER: + { + LLSD payload; + payload["from_id"] = from_id; + payload["lure_id"] = session_id; + payload["godlike"] = TRUE; + // do not show a message box, because you're about to be + // teleported. + LLNotifications::instance().forceResponse(LLNotification::Params("TeleportOffered").payload(payload), 0); + } + break; + + case IM_GOTO_URL: + { + LLSD args; + // n.b. this is for URLs sent by the system, not for + // URLs sent by scripts (i.e. llLoadURL) + if (binary_bucket_size <= 0) + { + LL_WARNS("Messaging") << "bad binary_bucket_size: " + << binary_bucket_size + << " - aborting function." << LL_ENDL; + return; + } + + std::string url; + + url.assign((char*)binary_bucket, binary_bucket_size-1); + args["MESSAGE"] = message; + args["URL"] = url; + LLSD payload; + payload["url"] = url; + LLNotificationsUtil::add("GotoURL", args, payload ); + } + break; + + case IM_FRIENDSHIP_OFFERED: + { + LLSD payload; + payload["from_id"] = from_id; + payload["session_id"] = session_id;; + payload["online"] = (offline == IM_ONLINE); + payload["sender"] = msg->getSender().getIPandPort(); + + if (is_busy) + { + busy_message(msg, from_id); + LLNotifications::instance().forceResponse(LLNotification::Params("OfferFriendship").payload(payload), 1); + } + else if (is_muted) + { + LLNotifications::instance().forceResponse(LLNotification::Params("OfferFriendship").payload(payload), 1); + } + else + { + args["NAME_SLURL"] = LLSLURL("agent", from_id, "about").getSLURLString(); + if(message.empty()) + { + //support for frienship offers from clients before July 2008 + LLNotificationsUtil::add("OfferFriendshipNoMessage", args, payload); + } + else + { + args["[MESSAGE]"] = message; + LLNotification::Params params("OfferFriendship"); + params.substitutions = args; + params.payload = payload; + LLPostponedNotification::add( params, from_id, false); + } + } + } + break; + + case IM_FRIENDSHIP_ACCEPTED: + { + // In the case of an offline IM, the formFriendship() may be extraneous + // as the database should already include the relationship. But it + // doesn't hurt for dupes. + LLAvatarTracker::formFriendship(from_id); + + std::vector strings; + strings.push_back(from_id.asString()); + send_generic_message("requestonlinenotification", strings); + + args["NAME"] = name; + LLSD payload; + payload["from_id"] = from_id; + LLAvatarNameCache::get(from_id, boost::bind(¬ification_display_name_callback, + _1, + _2, + "FriendshipAccepted", + args, + payload)); + } + break; + + case IM_FRIENDSHIP_DECLINED_DEPRECATED: + default: + LL_WARNS("Messaging") << "Instant message calling for unknown dialog " + << (S32)dialog << LL_ENDL; + break; + } + + LLWindow* viewer_window = gViewerWindow->getWindow(); + if (viewer_window && viewer_window->getMinimized()) + { + viewer_window->flashIcon(5.f); + } +} + +void busy_message (LLMessageSystem* msg, LLUUID from_id) +{ + if (gAgent.getBusy()) + { + std::string my_name; + LLAgentUI::buildFullname(my_name); + std::string response = gSavedPerAccountSettings.getString("BusyModeResponse"); + pack_instant_message( + gMessageSystem, + gAgent.getID(), + FALSE, + gAgent.getSessionID(), + from_id, + my_name, + response, + IM_ONLINE, + IM_BUSY_AUTO_RESPONSE); + gAgent.sendReliableMessage(); + } +} + +bool callingcard_offer_callback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + LLUUID fid; + LLUUID from_id; + LLMessageSystem* msg = gMessageSystem; + switch(option) + { + case 0: + // accept + msg->newMessageFast(_PREHASH_AcceptCallingCard); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_TransactionBlock); + msg->addUUIDFast(_PREHASH_TransactionID, notification["payload"]["transaction_id"].asUUID()); + fid = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); + msg->nextBlockFast(_PREHASH_FolderData); + msg->addUUIDFast(_PREHASH_FolderID, fid); + msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); + break; + case 1: + // decline + msg->newMessageFast(_PREHASH_DeclineCallingCard); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_TransactionBlock); + msg->addUUIDFast(_PREHASH_TransactionID, notification["payload"]["transaction_id"].asUUID()); + msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); + busy_message(msg, notification["payload"]["source_id"].asUUID()); + break; + default: + // close button probably, possibly timed out + break; + } + + return false; +} +static LLNotificationFunctorRegistration callingcard_offer_cb_reg("OfferCallingCard", callingcard_offer_callback); + +void process_offer_callingcard(LLMessageSystem* msg, void**) +{ + // someone has offered to form a friendship + LL_DEBUGS("Messaging") << "callingcard offer" << LL_ENDL; + + LLUUID source_id; + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, source_id); + LLUUID tid; + msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_TransactionID, tid); + + LLSD payload; + payload["transaction_id"] = tid; + payload["source_id"] = source_id; + payload["sender"] = msg->getSender().getIPandPort(); + + LLViewerObject* source = gObjectList.findObject(source_id); + LLSD args; + std::string source_name; + if(source && source->isAvatar()) + { + LLNameValue* nvfirst = source->getNVPair("FirstName"); + LLNameValue* nvlast = source->getNVPair("LastName"); + if (nvfirst && nvlast) + { + source_name = LLCacheName::buildFullName( + nvfirst->getString(), nvlast->getString()); + } + } + + if(!source_name.empty()) + { + if (gAgent.getBusy() + || LLMuteList::getInstance()->isMuted(source_id, source_name, LLMute::flagTextChat)) + { + // automatically decline offer + LLNotifications::instance().forceResponse(LLNotification::Params("OfferCallingCard").payload(payload), 1); + } + else + { + args["NAME"] = source_name; + LLNotificationsUtil::add("OfferCallingCard", args, payload); + } + } + else + { + LL_WARNS("Messaging") << "Calling card offer from an unknown source." << LL_ENDL; + } +} + +void process_accept_callingcard(LLMessageSystem* msg, void**) +{ + LLNotificationsUtil::add("CallingCardAccepted"); +} + +void process_decline_callingcard(LLMessageSystem* msg, void**) +{ + LLNotificationsUtil::add("CallingCardDeclined"); +} + +class ChatTranslationReceiver : public LLTranslate::TranslationReceiver +{ +public : + ChatTranslationReceiver(const std::string &from_lang, const std::string &to_lang, const std::string &mesg, + const LLChat &chat, const LLSD &toast_args) + : LLTranslate::TranslationReceiver(from_lang, to_lang), + m_chat(chat), + m_toastArgs(toast_args), + m_origMesg(mesg) + { + } + + static boost::intrusive_ptr build(const std::string &from_lang, const std::string &to_lang, const std::string &mesg, const LLChat &chat, const LLSD &toast_args) + { + return boost::intrusive_ptr(new ChatTranslationReceiver(from_lang, to_lang, mesg, chat, toast_args)); + } + +protected: + void handleResponse(const std::string &translation, const std::string &detected_language) + { + // filter out non-interesting responeses + if ( !translation.empty() + && (m_toLang != detected_language) + && (LLStringUtil::compareInsensitive(translation, m_origMesg) != 0) ) + { + m_chat.mText += " (" + translation + ")"; + } + + LLNotificationsUI::LLNotificationManager::instance().onChat(m_chat, m_toastArgs); + } + + void handleFailure() + { + LLTranslate::TranslationReceiver::handleFailure(); + m_chat.mText += " (?)"; + + LLNotificationsUI::LLNotificationManager::instance().onChat(m_chat, m_toastArgs); + } + +private: + LLChat m_chat; + std::string m_origMesg; + LLSD m_toastArgs; +}; +void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) +{ + LLChat chat; + std::string mesg; + std::string from_name; + U8 source_temp; + U8 type_temp; + U8 audible_temp; + LLColor4 color(1.0f, 1.0f, 1.0f, 1.0f); + LLUUID from_id; + LLUUID owner_id; + BOOL is_owned_by_me = FALSE; + LLViewerObject* chatter; + + msg->getString("ChatData", "FromName", from_name); + + msg->getUUID("ChatData", "SourceID", from_id); + chat.mFromID = from_id; + + // Object owner for objects + msg->getUUID("ChatData", "OwnerID", owner_id); + + msg->getU8Fast(_PREHASH_ChatData, _PREHASH_SourceType, source_temp); + chat.mSourceType = (EChatSourceType)source_temp; + + msg->getU8("ChatData", "ChatType", type_temp); + chat.mChatType = (EChatType)type_temp; + + msg->getU8Fast(_PREHASH_ChatData, _PREHASH_Audible, audible_temp); + chat.mAudible = (EChatAudible)audible_temp; + + chat.mTime = LLFrameTimer::getElapsedSeconds(); + + // IDEVO Correct for new-style "Resident" names + if (chat.mSourceType == CHAT_SOURCE_AGENT) + { + // I don't know if it's OK to change this here, if + // anything downstream does lookups by name, for instance + + LLAvatarName av_name; + if (LLAvatarNameCache::get(from_id, &av_name)) + { + chat.mFromName = av_name.mDisplayName; + } + else + { + chat.mFromName = LLCacheName::cleanFullName(from_name); + } + } + else + { + chat.mFromName = from_name; + } + + BOOL is_busy = gAgent.getBusy(); + + BOOL is_muted = FALSE; + BOOL is_linden = FALSE; + is_muted = LLMuteList::getInstance()->isMuted( + from_id, + from_name, + LLMute::flagTextChat) + || LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagTextChat); + is_linden = chat.mSourceType != CHAT_SOURCE_OBJECT && + LLMuteList::getInstance()->isLinden(from_name); + + BOOL is_audible = (CHAT_AUDIBLE_FULLY == chat.mAudible); + chatter = gObjectList.findObject(from_id); + if (chatter) + { + chat.mPosAgent = chatter->getPositionAgent(); + + // Make swirly things only for talking objects. (not script debug messages, though) + if (chat.mSourceType == CHAT_SOURCE_OBJECT + && chat.mChatType != CHAT_TYPE_DEBUG_MSG + && gSavedSettings.getBOOL("EffectScriptChatParticles") ) + { + LLPointer psc = new LLViewerPartSourceChat(chatter->getPositionAgent()); + psc->setSourceObject(chatter); + psc->setColor(color); + //We set the particles to be owned by the object's owner, + //just in case they should be muted by the mute list + psc->setOwnerUUID(owner_id); + LLViewerPartSim::getInstance()->addPartSource(psc); + } + + // record last audible utterance + if (is_audible + && (is_linden || (!is_muted && !is_busy))) + { + if (chat.mChatType != CHAT_TYPE_START + && chat.mChatType != CHAT_TYPE_STOP) + { + gAgent.heardChat(chat.mFromID); + } + } + + is_owned_by_me = chatter->permYouOwner(); + } + + if (is_audible) + { + BOOL visible_in_chat_bubble = FALSE; + std::string verb; + + color.setVec(1.f,1.f,1.f,1.f); + msg->getStringFast(_PREHASH_ChatData, _PREHASH_Message, mesg); + + BOOL ircstyle = FALSE; + + // Look for IRC-style emotes here so chatbubbles work + std::string prefix = mesg.substr(0, 4); + if (prefix == "/me " || prefix == "/me'") + { + ircstyle = TRUE; + } + chat.mText = mesg; + + // Look for the start of typing so we can put "..." in the bubbles. + if (CHAT_TYPE_START == chat.mChatType) + { + LLLocalSpeakerMgr::getInstance()->setSpeakerTyping(from_id, TRUE); + + // Might not have the avatar constructed yet, eg on login. + if (chatter && chatter->isAvatar()) + { + ((LLVOAvatar*)chatter)->startTyping(); + } + return; + } + else if (CHAT_TYPE_STOP == chat.mChatType) + { + LLLocalSpeakerMgr::getInstance()->setSpeakerTyping(from_id, FALSE); + + // Might not have the avatar constructed yet, eg on login. + if (chatter && chatter->isAvatar()) + { + ((LLVOAvatar*)chatter)->stopTyping(); + } + return; + } + + // Look for IRC-style emotes + if (ircstyle) + { + // set CHAT_STYLE_IRC to avoid adding Avatar Name as author of message. See EXT-656 + chat.mChatStyle = CHAT_STYLE_IRC; + + // Do nothing, ircstyle is fixed above for chat bubbles + } + else + { + switch(chat.mChatType) + { + case CHAT_TYPE_WHISPER: + verb = LLTrans::getString("whisper") + " "; + break; + case CHAT_TYPE_DEBUG_MSG: + case CHAT_TYPE_OWNER: + case CHAT_TYPE_NORMAL: + verb = ""; + break; + case CHAT_TYPE_SHOUT: + verb = LLTrans::getString("shout") + " "; + break; + case CHAT_TYPE_START: + case CHAT_TYPE_STOP: + LL_WARNS("Messaging") << "Got chat type start/stop in main chat processing." << LL_ENDL; + break; + default: + LL_WARNS("Messaging") << "Unknown type " << chat.mChatType << " in chat!" << LL_ENDL; + verb = ""; + break; + } + + + chat.mText = ""; + chat.mText += verb; + chat.mText += mesg; + } + + // We have a real utterance now, so can stop showing "..." and proceed. + if (chatter && chatter->isAvatar()) + { + LLLocalSpeakerMgr::getInstance()->setSpeakerTyping(from_id, FALSE); + ((LLVOAvatar*)chatter)->stopTyping(); + + if (!is_muted && !is_busy) + { + visible_in_chat_bubble = gSavedSettings.getBOOL("UseChatBubbles"); + std::string formated_msg = ""; + LLViewerChat::formatChatMsg(chat, formated_msg); + LLChat chat_bubble = chat; + chat_bubble.mText = formated_msg; + ((LLVOAvatar*)chatter)->addChat(chat_bubble); + } + } + + if (chatter) + { + chat.mPosAgent = chatter->getPositionAgent(); + } + + // truth table: + // LINDEN BUSY MUTED OWNED_BY_YOU TASK DISPLAY STORE IN HISTORY + // F F F F * Yes Yes + // F F F T * Yes Yes + // F F T F * No No + // F F T T * No No + // F T F F * No Yes + // F T F T * Yes Yes + // F T T F * No No + // F T T T * No No + // T * * * F Yes Yes + + chat.mMuted = is_muted && !is_linden; + + // pass owner_id to chat so that we can display the remote + // object inspect for an object that is chatting with you + LLSD args; + args["type"] = LLNotificationsUI::NT_NEARBYCHAT; + chat.mOwnerID = owner_id; + + if (gSavedSettings.getBOOL("TranslateChat") && chat.mSourceType != CHAT_SOURCE_SYSTEM) + { + if (chat.mChatStyle == CHAT_STYLE_IRC) + { + mesg = mesg.substr(4, std::string::npos); + } + const std::string from_lang = ""; // leave empty to trigger autodetect + const std::string to_lang = LLTranslate::getTranslateLanguage(); + + LLHTTPClient::ResponderPtr result = ChatTranslationReceiver::build(from_lang, to_lang, mesg, chat, args); + LLTranslate::translateMessage(result, from_lang, to_lang, mesg); + } + else + { + LLNotificationsUI::LLNotificationManager::instance().onChat(chat, args); + } + } +} + + +// Simulator we're on is informing the viewer that the agent +// is starting to teleport (perhaps to another sim, perhaps to the +// same sim). If we initiated the teleport process by sending some kind +// of TeleportRequest, then this info is redundant, but if the sim +// initiated the teleport (via a script call, being killed, etc.) +// then this info is news to us. +void process_teleport_start(LLMessageSystem *msg, void**) +{ + // on teleport, don't tell them about destination guide anymore + LLFirstUse::notUsingDestinationGuide(false); + U32 teleport_flags = 0x0; + msg->getU32("Info", "TeleportFlags", teleport_flags); + + LL_DEBUGS("Messaging") << "Got TeleportStart with TeleportFlags=" << teleport_flags << ". gTeleportDisplay: " << gTeleportDisplay << ", gAgent.mTeleportState: " << gAgent.getTeleportState() << LL_ENDL; + + if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL) + { + gViewerWindow->setProgressCancelButtonVisible(FALSE); + } + else + { + gViewerWindow->setProgressCancelButtonVisible(TRUE, LLTrans::getString("Cancel")); + } + + // Freeze the UI and show progress bar + // Note: could add data here to differentiate between normal teleport and death. + + if( gAgent.getTeleportState() == LLAgent::TELEPORT_NONE ) + { + gTeleportDisplay = TRUE; + gAgent.setTeleportState( LLAgent::TELEPORT_START ); + make_ui_sound("UISndTeleportOut"); + + LL_INFOS("Messaging") << "Teleport initiated by remote TeleportStart message with TeleportFlags: " << teleport_flags << LL_ENDL; + // Don't call LLFirstUse::useTeleport here because this could be + // due to being killed, which would send you home, not to a Telehub + } +} + +void process_teleport_progress(LLMessageSystem* msg, void**) +{ + LLUUID agent_id; + msg->getUUID("AgentData", "AgentID", agent_id); + if((gAgent.getID() != agent_id) + || (gAgent.getTeleportState() == LLAgent::TELEPORT_NONE)) + { + LL_WARNS("Messaging") << "Unexpected teleport progress message." << LL_ENDL; + return; + } + U32 teleport_flags = 0x0; + msg->getU32("Info", "TeleportFlags", teleport_flags); + if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL) + { + gViewerWindow->setProgressCancelButtonVisible(FALSE); + } + else + { + gViewerWindow->setProgressCancelButtonVisible(TRUE, LLTrans::getString("Cancel")); + } + std::string buffer; + msg->getString("Info", "Message", buffer); + LL_DEBUGS("Messaging") << "teleport progress: " << buffer << LL_ENDL; + + //Sorta hacky...default to using simulator raw messages + //if we don't find the coresponding mapping in our progress mappings + std::string message = buffer; + + if (LLAgent::sTeleportProgressMessages.find(buffer) != + LLAgent::sTeleportProgressMessages.end() ) + { + message = LLAgent::sTeleportProgressMessages[buffer]; + } + + gAgent.setTeleportMessage(LLAgent::sTeleportProgressMessages[message]); +} + +class LLFetchInWelcomeArea : public LLInventoryFetchDescendentsObserver +{ +public: + LLFetchInWelcomeArea(const uuid_vec_t &ids) : + LLInventoryFetchDescendentsObserver(ids) + {} + virtual void done() + { + LLIsType is_landmark(LLAssetType::AT_LANDMARK); + LLIsType is_card(LLAssetType::AT_CALLINGCARD); + + LLInventoryModel::cat_array_t card_cats; + LLInventoryModel::item_array_t card_items; + LLInventoryModel::cat_array_t land_cats; + LLInventoryModel::item_array_t land_items; + + uuid_vec_t::iterator it = mComplete.begin(); + uuid_vec_t::iterator end = mComplete.end(); + for(; it != end; ++it) + { + gInventory.collectDescendentsIf( + (*it), + land_cats, + land_items, + LLInventoryModel::EXCLUDE_TRASH, + is_landmark); + gInventory.collectDescendentsIf( + (*it), + card_cats, + card_items, + LLInventoryModel::EXCLUDE_TRASH, + is_card); + } + LLSD args; + if ( land_items.count() > 0 ) + { // Show notification that they can now teleport to landmarks. Use a random landmark from the inventory + S32 random_land = ll_rand( land_items.count() - 1 ); + args["NAME"] = land_items[random_land]->getName(); + LLNotificationsUtil::add("TeleportToLandmark",args); + } + if ( card_items.count() > 0 ) + { // Show notification that they can now contact people. Use a random calling card from the inventory + S32 random_card = ll_rand( card_items.count() - 1 ); + args["NAME"] = card_items[random_card]->getName(); + LLNotificationsUtil::add("TeleportToPerson",args); + } + + gInventory.removeObserver(this); + delete this; + } +}; + + + +class LLPostTeleportNotifiers : public LLEventTimer +{ +public: + LLPostTeleportNotifiers(); + virtual ~LLPostTeleportNotifiers(); + + //function to be called at the supplied frequency + virtual BOOL tick(); +}; + +LLPostTeleportNotifiers::LLPostTeleportNotifiers() : LLEventTimer( 2.0 ) +{ +}; + +LLPostTeleportNotifiers::~LLPostTeleportNotifiers() +{ +} + +BOOL LLPostTeleportNotifiers::tick() +{ + BOOL all_done = FALSE; + if ( gAgent.getTeleportState() == LLAgent::TELEPORT_NONE ) + { + // get callingcards and landmarks available to the user arriving. + uuid_vec_t folders; + const LLUUID callingcard_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); + if(callingcard_id.notNull()) + folders.push_back(callingcard_id); + const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); + if(folder_id.notNull()) + folders.push_back(folder_id); + if(!folders.empty()) + { + LLFetchInWelcomeArea* fetcher = new LLFetchInWelcomeArea(folders); + fetcher->startFetch(); + if(fetcher->isFinished()) + { + fetcher->done(); + } + else + { + gInventory.addObserver(fetcher); + } + } + all_done = TRUE; + } + + return all_done; +} + + + +// Teleport notification from the simulator +// We're going to pretend to be a new agent +void process_teleport_finish(LLMessageSystem* msg, void**) +{ + LL_DEBUGS("Messaging") << "Got teleport location message" << LL_ENDL; + LLUUID agent_id; + msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id); + if (agent_id != gAgent.getID()) + { + LL_WARNS("Messaging") << "Got teleport notification for wrong agent!" << LL_ENDL; + return; + } + + // Teleport is finished; it can't be cancelled now. + gViewerWindow->setProgressCancelButtonVisible(FALSE); + + // Do teleport effect for where you're leaving + // VEFFECT: TeleportStart + LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); + effectp->setPositionGlobal(gAgent.getPositionGlobal()); + effectp->setColor(LLColor4U(gAgent.getEffectColor())); + LLHUDManager::getInstance()->sendEffects(); + + U32 location_id; + U32 sim_ip; + U16 sim_port; + LLVector3 pos, look_at; + U64 region_handle; + msg->getU32Fast(_PREHASH_Info, _PREHASH_LocationID, location_id); + msg->getIPAddrFast(_PREHASH_Info, _PREHASH_SimIP, sim_ip); + msg->getIPPortFast(_PREHASH_Info, _PREHASH_SimPort, sim_port); + //msg->getVector3Fast(_PREHASH_Info, _PREHASH_Position, pos); + //msg->getVector3Fast(_PREHASH_Info, _PREHASH_LookAt, look_at); + msg->getU64Fast(_PREHASH_Info, _PREHASH_RegionHandle, region_handle); + U32 teleport_flags; + msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags); + + + std::string seedCap; + msg->getStringFast(_PREHASH_Info, _PREHASH_SeedCapability, seedCap); + + // update home location if we are teleporting out of prelude - specific to teleporting to welcome area + if((teleport_flags & TELEPORT_FLAGS_SET_HOME_TO_TARGET) + && (!gAgent.isGodlike())) + { + gAgent.setHomePosRegion(region_handle, pos); + + // Create a timer that will send notices when teleporting is all finished. Since this is + // based on the LLEventTimer class, it will be managed by that class and not orphaned or leaked. + new LLPostTeleportNotifiers(); + } + + LLHost sim_host(sim_ip, sim_port); + + // Viewer trusts the simulator. + gMessageSystem->enableCircuit(sim_host, TRUE); + LLViewerRegion* regionp = LLWorld::getInstance()->addRegion(region_handle, sim_host); + +/* + // send camera update to new region + gAgentCamera.updateCamera(); + + // likewise make sure the camera is behind the avatar + gAgentCamera.resetView(TRUE); + LLVector3 shift_vector = regionp->getPosRegionFromGlobal(gAgent.getRegion()->getOriginGlobal()); + gAgent.setRegion(regionp); + gObjectList.shiftObjects(shift_vector); + + if (isAgentAvatarValid()) + { + gAgentAvatarp->clearChatText(); + gAgentCamera.slamLookAt(look_at); + } + gAgent.setPositionAgent(pos); + gAssetStorage->setUpstream(sim); + gCacheName->setUpstream(sim); +*/ + + // now, use the circuit info to tell simulator about us! + LL_INFOS("Messaging") << "process_teleport_finish() Enabling " + << sim_host << " with code " << msg->mOurCircuitCode << LL_ENDL; + msg->newMessageFast(_PREHASH_UseCircuitCode); + msg->nextBlockFast(_PREHASH_CircuitCode); + msg->addU32Fast(_PREHASH_Code, msg->getOurCircuitCode()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addUUIDFast(_PREHASH_ID, gAgent.getID()); + msg->sendReliable(sim_host); + + send_complete_agent_movement(sim_host); + gAgent.setTeleportState( LLAgent::TELEPORT_MOVING ); + gAgent.setTeleportMessage(LLAgent::sTeleportProgressMessages["contacting"]); + + regionp->setSeedCapability(seedCap); + + // Don't send camera updates to the new region until we're + // actually there... + + + // Now do teleport effect for where you're going. + // VEFFECT: TeleportEnd + effectp = (LLHUDEffectSpiral *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE); + effectp->setPositionGlobal(gAgent.getPositionGlobal()); + + effectp->setColor(LLColor4U(gAgent.getEffectColor())); + LLHUDManager::getInstance()->sendEffects(); + +// gTeleportDisplay = TRUE; +// gTeleportDisplayTimer.reset(); +// gViewerWindow->setShowProgress(TRUE); +} + +// stuff we have to do every time we get an AvatarInitComplete from a sim +/* +void process_avatar_init_complete(LLMessageSystem* msg, void**) +{ + LLVector3 agent_pos; + msg->getVector3Fast(_PREHASH_AvatarData, _PREHASH_Position, agent_pos); + agent_movement_complete(msg->getSender(), agent_pos); +} +*/ + +void process_agent_movement_complete(LLMessageSystem* msg, void**) +{ + gAgentMovementCompleted = true; + + LLUUID agent_id; + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); + LLUUID session_id; + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); + if((gAgent.getID() != agent_id) || (gAgent.getSessionID() != session_id)) + { + LL_WARNS("Messaging") << "Incorrect id in process_agent_movement_complete()" + << LL_ENDL; + return; + } + + LL_DEBUGS("Messaging") << "process_agent_movement_complete()" << LL_ENDL; + + // *TODO: check timestamp to make sure the movement compleation + // makes sense. + LLVector3 agent_pos; + msg->getVector3Fast(_PREHASH_Data, _PREHASH_Position, agent_pos); + LLVector3 look_at; + msg->getVector3Fast(_PREHASH_Data, _PREHASH_LookAt, look_at); + U64 region_handle; + msg->getU64Fast(_PREHASH_Data, _PREHASH_RegionHandle, region_handle); + + std::string version_channel; + msg->getString("SimData", "ChannelVersion", version_channel); + + if (!isAgentAvatarValid()) + { + // Could happen if you were immediately god-teleported away on login, + // maybe other cases. Continue, but warn. + LL_WARNS("Messaging") << "agent_movement_complete() with NULL avatarp." << LL_ENDL; + } + + F32 x, y; + from_region_handle(region_handle, &x, &y); + LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromHandle(region_handle); + if (!regionp) + { + if (gAgent.getRegion()) + { + LL_WARNS("Messaging") << "current region " << gAgent.getRegion()->getOriginGlobal() << LL_ENDL; + } + + LL_WARNS("Messaging") << "Agent being sent to invalid home region: " + << x << ":" << y + << " current pos " << gAgent.getPositionGlobal() + << LL_ENDL; + LLAppViewer::instance()->forceDisconnect(LLTrans::getString("SentToInvalidRegion")); + return; + + } + + LL_INFOS("Messaging") << "Changing home region to " << x << ":" << y << LL_ENDL; + + // set our upstream host the new simulator and shuffle things as + // appropriate. + LLVector3 shift_vector = regionp->getPosRegionFromGlobal( + gAgent.getRegion()->getOriginGlobal()); + gAgent.setRegion(regionp); + gObjectList.shiftObjects(shift_vector); + gAssetStorage->setUpstream(msg->getSender()); + gCacheName->setUpstream(msg->getSender()); + gViewerThrottle.sendToSim(); + gViewerWindow->sendShapeToSim(); + + bool is_teleport = gAgent.getTeleportState() == LLAgent::TELEPORT_MOVING; + + if( is_teleport ) + { + if (gAgent.getTeleportKeepsLookAt()) + { + // *NOTE: the LookAt data we get from the sim here doesn't + // seem to be useful, so get it from the camera instead + look_at = LLViewerCamera::getInstance()->getAtAxis(); + } + // Force the camera back onto the agent, don't animate. + gAgentCamera.setFocusOnAvatar(TRUE, FALSE); + gAgentCamera.slamLookAt(look_at); + gAgentCamera.updateCamera(); + + gAgent.setTeleportState( LLAgent::TELEPORT_START_ARRIVAL ); + + // set the appearance on teleport since the new sim does not + // know what you look like. + gAgent.sendAgentSetAppearance(); + + if (isAgentAvatarValid()) + { + // Chat the "back" SLURL. (DEV-4907) + + LLSLURL slurl; + gAgent.getTeleportSourceSLURL(slurl); + LLSD substitution = LLSD().with("[T_SLURL]", slurl.getSLURLString()); + std::string completed_from = LLAgent::sTeleportProgressMessages["completed_from"]; + LLStringUtil::format(completed_from, substitution); + + LLSD args; + args["MESSAGE"] = completed_from; + LLNotificationsUtil::add("SystemMessageTip", args); + + // Set the new position + gAgentAvatarp->setPositionAgent(agent_pos); + gAgentAvatarp->clearChat(); + gAgentAvatarp->slamPosition(); + } + } + else + { + // This is initial log-in or a region crossing + gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); + + if(LLStartUp::getStartupState() < STATE_STARTED) + { // This is initial log-in, not a region crossing: + // Set the camera looking ahead of the AV so send_agent_update() below + // will report the correct location to the server. + LLVector3 look_at_point = look_at; + look_at_point = agent_pos + look_at_point.rotVec(gAgent.getQuat()); + + static LLVector3 up_direction(0.0f, 0.0f, 1.0f); + LLViewerCamera::getInstance()->lookAt(agent_pos, look_at_point, up_direction); + } + } + + if ( LLTracker::isTracking(NULL) ) + { + // Check distance to beacon, if < 5m, remove beacon + LLVector3d beacon_pos = LLTracker::getTrackedPositionGlobal(); + LLVector3 beacon_dir(agent_pos.mV[VX] - (F32)fmod(beacon_pos.mdV[VX], 256.0), agent_pos.mV[VY] - (F32)fmod(beacon_pos.mdV[VY], 256.0), 0); + if (beacon_dir.magVecSquared() < 25.f) + { + LLTracker::stopTracking(NULL); + } + else if ( is_teleport && !gAgent.getTeleportKeepsLookAt() ) + { + //look at the beacon + LLVector3 global_agent_pos = agent_pos; + global_agent_pos[0] += x; + global_agent_pos[1] += y; + look_at = (LLVector3)beacon_pos - global_agent_pos; + look_at.normVec(); + gAgentCamera.slamLookAt(look_at); + } + } + + // TODO: Put back a check for flying status! DK 12/19/05 + // Sim tells us whether the new position is off the ground + /* + if (teleport_flags & TELEPORT_FLAGS_IS_FLYING) + { + gAgent.setFlying(TRUE); + } + else + { + gAgent.setFlying(FALSE); + } + */ + + send_agent_update(TRUE, TRUE); + + if (gAgent.getRegion()->getBlockFly()) + { + gAgent.setFlying(gAgent.canFly()); + } + + // force simulator to recognize busy state + if (gAgent.getBusy()) + { + gAgent.setBusy(); + } + else + { + gAgent.clearBusy(); + } + + if (isAgentAvatarValid()) + { + gAgentAvatarp->mFootPlane.clearVec(); + } + + // send walk-vs-run status + gAgent.sendWalkRun(gAgent.getRunning() || gAgent.getAlwaysRun()); + + // If the server version has changed, display an info box and offer + // to display the release notes, unless this is the initial log in. + if (gLastVersionChannel == version_channel) + { + return; + } + + gLastVersionChannel = version_channel; +} + +void process_crossed_region(LLMessageSystem* msg, void**) +{ + LLUUID agent_id; + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); + LLUUID session_id; + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id); + if((gAgent.getID() != agent_id) || (gAgent.getSessionID() != session_id)) + { + LL_WARNS("Messaging") << "Incorrect id in process_crossed_region()" + << LL_ENDL; + return; + } + LL_INFOS("Messaging") << "process_crossed_region()" << LL_ENDL; + gAgentAvatarp->resetRegionCrossingTimer(); + + U32 sim_ip; + msg->getIPAddrFast(_PREHASH_RegionData, _PREHASH_SimIP, sim_ip); + U16 sim_port; + msg->getIPPortFast(_PREHASH_RegionData, _PREHASH_SimPort, sim_port); + LLHost sim_host(sim_ip, sim_port); + U64 region_handle; + msg->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle); + + std::string seedCap; + msg->getStringFast(_PREHASH_RegionData, _PREHASH_SeedCapability, seedCap); + + send_complete_agent_movement(sim_host); + + LLViewerRegion* regionp = LLWorld::getInstance()->addRegion(region_handle, sim_host); + regionp->setSeedCapability(seedCap); +} + + + +// Sends avatar and camera information to simulator. +// Sent roughly once per frame, or 20 times per second, whichever is less often + +const F32 THRESHOLD_HEAD_ROT_QDOT = 0.9997f; // ~= 2.5 degrees -- if its less than this we need to update head_rot +const F32 MAX_HEAD_ROT_QDOT = 0.99999f; // ~= 0.5 degrees -- if its greater than this then no need to update head_rot + // between these values we delay the updates (but no more than one second) + +static LLFastTimer::DeclareTimer FTM_AGENT_UPDATE_SEND("Send Message"); + +void send_agent_update(BOOL force_send, BOOL send_reliable) +{ + if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE) + { + // We don't care if they want to send an agent update, they're not allowed to until the simulator + // that's the target is ready to receive them (after avatar_init_complete is received) + return; + } + + // We have already requested to log out. Don't send agent updates. + if(LLAppViewer::instance()->logoutRequestSent()) + { + return; + } + + // no region to send update to + if(gAgent.getRegion() == NULL) + { + return; + } + + const F32 TRANSLATE_THRESHOLD = 0.01f; + + // NOTA BENE: This is (intentionally?) using the small angle sine approximation to test for rotation + // Plus, there is an extra 0.5 in the mix since the perpendicular between last_camera_at and getAtAxis() bisects cam_rot_change + // Thus, we're actually testing against 0.2 degrees + const F32 ROTATION_THRESHOLD = 0.1f * 2.f*F_PI/360.f; // Rotation thresh 0.2 deg, see note above + + const U8 DUP_MSGS = 1; // HACK! number of times to repeat data on motionless agent + + // Store data on last sent update so that if no changes, no send + static LLVector3 last_camera_pos_agent, + last_camera_at, + last_camera_left, + last_camera_up; + + static LLVector3 cam_center_chg, + cam_rot_chg; + + static LLQuaternion last_head_rot; + static U32 last_control_flags = 0; + static U8 last_render_state; + static U8 duplicate_count = 0; + static F32 head_rot_chg = 1.0; + static U8 last_flags; + + LLMessageSystem *msg = gMessageSystem; + LLVector3 camera_pos_agent; // local to avatar's region + U8 render_state; + + LLQuaternion body_rotation = gAgent.getFrameAgent().getQuaternion(); + LLQuaternion head_rotation = gAgent.getHeadRotation(); + + camera_pos_agent = gAgentCamera.getCameraPositionAgent(); + + render_state = gAgent.getRenderState(); + + U32 control_flag_change = 0; + U8 flag_change = 0; + + cam_center_chg = last_camera_pos_agent - camera_pos_agent; + cam_rot_chg = last_camera_at - LLViewerCamera::getInstance()->getAtAxis(); + + // If a modifier key is held down, turn off + // LBUTTON and ML_LBUTTON so that using the camera (alt-key) doesn't + // trigger a control event. + U32 control_flags = gAgent.getControlFlags(); + MASK key_mask = gKeyboard->currentMask(TRUE); + if (key_mask & MASK_ALT || key_mask & MASK_CONTROL) + { + control_flags &= ~( AGENT_CONTROL_LBUTTON_DOWN | + AGENT_CONTROL_ML_LBUTTON_DOWN ); + control_flags |= AGENT_CONTROL_LBUTTON_UP | + AGENT_CONTROL_ML_LBUTTON_UP ; + } + + control_flag_change = last_control_flags ^ control_flags; + + U8 flags = AU_FLAGS_NONE; + if (gAgent.isGroupTitleHidden()) + { + flags |= AU_FLAGS_HIDETITLE; + } + if (gAgent.getAutoPilot()) + { + flags |= AU_FLAGS_CLIENT_AUTOPILOT; + } + + flag_change = last_flags ^ flags; + + head_rot_chg = dot(last_head_rot, head_rotation); + + if (force_send || + (cam_center_chg.magVec() > TRANSLATE_THRESHOLD) || + (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT) || + (last_render_state != render_state) || + (cam_rot_chg.magVec() > ROTATION_THRESHOLD) || + control_flag_change != 0 || + flag_change != 0) + { +/* + if (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT) + { + //LL_INFOS("Messaging") << "head rot " << head_rotation << LL_ENDL; + LL_INFOS("Messaging") << "head_rot_chg = " << head_rot_chg << LL_ENDL; + } + if (cam_rot_chg.magVec() > ROTATION_THRESHOLD) + { + LL_INFOS("Messaging") << "cam rot " << cam_rot_chg.magVec() << LL_ENDL; + } + if (cam_center_chg.magVec() > TRANSLATE_THRESHOLD) + { + LL_INFOS("Messaging") << "cam center " << cam_center_chg.magVec() << LL_ENDL; + } +// if (drag_delta_chg.magVec() > TRANSLATE_THRESHOLD) +// { +// LL_INFOS("Messaging") << "drag delta " << drag_delta_chg.magVec() << LL_ENDL; +// } + if (control_flag_change) + { + LL_INFOS("Messaging") << "dcf = " << control_flag_change << LL_ENDL; + } +*/ + + duplicate_count = 0; + } + else + { + duplicate_count++; + + if (head_rot_chg < MAX_HEAD_ROT_QDOT && duplicate_count < AGENT_UPDATES_PER_SECOND) + { + // The head_rotation is sent for updating things like attached guns. + // We only trigger a new update when head_rotation deviates beyond + // some threshold from the last update, however this can break fine + // adjustments when trying to aim an attached gun, so what we do here + // (where we would normally skip sending an update when nothing has changed) + // is gradually reduce the threshold to allow a better update to + // eventually get sent... should update to within 0.5 degrees in less + // than a second. + if (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT + (MAX_HEAD_ROT_QDOT - THRESHOLD_HEAD_ROT_QDOT) * duplicate_count / AGENT_UPDATES_PER_SECOND) + { + duplicate_count = 0; + } + else + { + return; + } + } + else + { + return; + } + } + + if (duplicate_count < DUP_MSGS && !gDisconnected) + { + LLFastTimer t(FTM_AGENT_UPDATE_SEND); + // Build the message + msg->newMessageFast(_PREHASH_AgentUpdate); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addQuatFast(_PREHASH_BodyRotation, body_rotation); + msg->addQuatFast(_PREHASH_HeadRotation, head_rotation); + msg->addU8Fast(_PREHASH_State, render_state); + msg->addU8Fast(_PREHASH_Flags, flags); + +// if (camera_pos_agent.mV[VY] > 255.f) +// { +// LL_INFOS("Messaging") << "Sending camera center " << camera_pos_agent << LL_ENDL; +// } + + msg->addVector3Fast(_PREHASH_CameraCenter, camera_pos_agent); + msg->addVector3Fast(_PREHASH_CameraAtAxis, LLViewerCamera::getInstance()->getAtAxis()); + msg->addVector3Fast(_PREHASH_CameraLeftAxis, LLViewerCamera::getInstance()->getLeftAxis()); + msg->addVector3Fast(_PREHASH_CameraUpAxis, LLViewerCamera::getInstance()->getUpAxis()); + msg->addF32Fast(_PREHASH_Far, gAgentCamera.mDrawDistance); + + msg->addU32Fast(_PREHASH_ControlFlags, control_flags); + + if (gDebugClicks) + { + if (control_flags & AGENT_CONTROL_LBUTTON_DOWN) + { + LL_INFOS("Messaging") << "AgentUpdate left button down" << LL_ENDL; + } + + if (control_flags & AGENT_CONTROL_LBUTTON_UP) + { + LL_INFOS("Messaging") << "AgentUpdate left button up" << LL_ENDL; + } + } + + gAgent.enableControlFlagReset(); + + if (!send_reliable) + { + gAgent.sendMessage(); + } + else + { + gAgent.sendReliableMessage(); + } + +// LL_DEBUGS("Messaging") << "agent " << avatar_pos_agent << " cam " << camera_pos_agent << LL_ENDL; + + // Copy the old data + last_head_rot = head_rotation; + last_render_state = render_state; + last_camera_pos_agent = camera_pos_agent; + last_camera_at = LLViewerCamera::getInstance()->getAtAxis(); + last_camera_left = LLViewerCamera::getInstance()->getLeftAxis(); + last_camera_up = LLViewerCamera::getInstance()->getUpAxis(); + last_control_flags = control_flags; + last_flags = flags; + } +} + + + +// *TODO: Remove this dependency, or figure out a better way to handle +// this hack. +extern U32 gObjectBits; + +void process_object_update(LLMessageSystem *mesgsys, void **user_data) +{ + LLMemType mt(LLMemType::MTYPE_OBJECT); + // Update the data counters + if (mesgsys->getReceiveCompressedSize()) + { + gObjectBits += mesgsys->getReceiveCompressedSize() * 8; + } + else + { + gObjectBits += mesgsys->getReceiveSize() * 8; + } + + // Update the object... + gObjectList.processObjectUpdate(mesgsys, user_data, OUT_FULL); +} + +void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data) +{ + LLMemType mt(LLMemType::MTYPE_OBJECT); + // Update the data counters + if (mesgsys->getReceiveCompressedSize()) + { + gObjectBits += mesgsys->getReceiveCompressedSize() * 8; + } + else + { + gObjectBits += mesgsys->getReceiveSize() * 8; + } + + // Update the object... + gObjectList.processCompressedObjectUpdate(mesgsys, user_data, OUT_FULL_COMPRESSED); +} + +void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data) +{ + LLMemType mt(LLMemType::MTYPE_OBJECT); + // Update the data counters + if (mesgsys->getReceiveCompressedSize()) + { + gObjectBits += mesgsys->getReceiveCompressedSize() * 8; + } + else + { + gObjectBits += mesgsys->getReceiveSize() * 8; + } + + // Update the object... + gObjectList.processCachedObjectUpdate(mesgsys, user_data, OUT_FULL_CACHED); +} + + +void process_terse_object_update_improved(LLMessageSystem *mesgsys, void **user_data) +{ + LLMemType mt(LLMemType::MTYPE_OBJECT); + if (mesgsys->getReceiveCompressedSize()) + { + gObjectBits += mesgsys->getReceiveCompressedSize() * 8; + } + else + { + gObjectBits += mesgsys->getReceiveSize() * 8; + } + + gObjectList.processCompressedObjectUpdate(mesgsys, user_data, OUT_TERSE_IMPROVED); +} + +static LLFastTimer::DeclareTimer FTM_PROCESS_OBJECTS("Process Objects"); + + +void process_kill_object(LLMessageSystem *mesgsys, void **user_data) +{ + LLFastTimer t(FTM_PROCESS_OBJECTS); + + LLUUID id; + U32 local_id; + S32 i; + S32 num_objects; + + num_objects = mesgsys->getNumberOfBlocksFast(_PREHASH_ObjectData); + + for (i = 0; i < num_objects; i++) + { + mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i); + + LLViewerObjectList::getUUIDFromLocal(id, + local_id, + gMessageSystem->getSenderIP(), + gMessageSystem->getSenderPort()); + if (id == LLUUID::null) + { + LL_DEBUGS("Messaging") << "Unknown kill for local " << local_id << LL_ENDL; + gObjectList.mNumUnknownKills++; + continue; + } + else + { + LL_DEBUGS("Messaging") << "Kill message for local " << local_id << LL_ENDL; + } + + LLSelectMgr::getInstance()->removeObjectFromSelections(id); + + // ...don't kill the avatar + if (!(id == gAgentID)) + { + LLViewerObject *objectp = gObjectList.findObject(id); + if (objectp) + { + // Display green bubble on kill + if ( gShowObjectUpdates ) + { + LLViewerObject* newobject; + newobject = gObjectList.createObjectViewer(LL_PCODE_LEGACY_TEXT_BUBBLE, objectp->getRegion()); + + LLVOTextBubble* bubble = (LLVOTextBubble*) newobject; + + bubble->mColor.setVec(0.f, 1.f, 0.f, 1.f); + bubble->setScale( 2.0f * bubble->getScale() ); + bubble->setPositionGlobal(objectp->getPositionGlobal()); + gPipeline.addObject(bubble); + } + + // Do the kill + gObjectList.killObject(objectp); + } + else + { + LL_WARNS("Messaging") << "Object in UUID lookup, but not on object list in kill!" << LL_ENDL; + gObjectList.mNumUnknownKills++; + } + } + } +} + +void process_time_synch(LLMessageSystem *mesgsys, void **user_data) +{ + LLVector3 sun_direction; + LLVector3 sun_ang_velocity; + F32 phase; + U64 space_time_usec; + + U32 seconds_per_day; + U32 seconds_per_year; + + // "SimulatorViewerTimeMessage" + mesgsys->getU64Fast(_PREHASH_TimeInfo, _PREHASH_UsecSinceStart, space_time_usec); + mesgsys->getU32Fast(_PREHASH_TimeInfo, _PREHASH_SecPerDay, seconds_per_day); + mesgsys->getU32Fast(_PREHASH_TimeInfo, _PREHASH_SecPerYear, seconds_per_year); + + // This should eventually be moved to an "UpdateHeavenlyBodies" message + mesgsys->getF32Fast(_PREHASH_TimeInfo, _PREHASH_SunPhase, phase); + mesgsys->getVector3Fast(_PREHASH_TimeInfo, _PREHASH_SunDirection, sun_direction); + mesgsys->getVector3Fast(_PREHASH_TimeInfo, _PREHASH_SunAngVelocity, sun_ang_velocity); + + LLWorld::getInstance()->setSpaceTimeUSec(space_time_usec); + + //LL_DEBUGS("Messaging") << "time_synch() - " << sun_direction << ", " << sun_ang_velocity + // << ", " << phase << LL_ENDL; + + gSky.setSunPhase(phase); + gSky.setSunTargetDirection(sun_direction, sun_ang_velocity); + if (!gNoRender && !(gSavedSettings.getBOOL("SkyOverrideSimSunPosition") || gSky.getOverrideSun())) + { + gSky.setSunDirection(sun_direction, sun_ang_velocity); + } +} + +void process_sound_trigger(LLMessageSystem *msg, void **) +{ + if (!gAudiop) return; + + U64 region_handle = 0; + F32 gain = 0; + LLUUID sound_id; + LLUUID owner_id; + LLUUID object_id; + LLUUID parent_id; + LLVector3 pos_local; + + msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_SoundID, sound_id); + msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_OwnerID, owner_id); + msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_ObjectID, object_id); + msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_ParentID, parent_id); + msg->getU64Fast(_PREHASH_SoundData, _PREHASH_Handle, region_handle); + msg->getVector3Fast(_PREHASH_SoundData, _PREHASH_Position, pos_local); + msg->getF32Fast(_PREHASH_SoundData, _PREHASH_Gain, gain); + + // adjust sound location to true global coords + LLVector3d pos_global = from_region_handle(region_handle); + pos_global.mdV[VX] += pos_local.mV[VX]; + pos_global.mdV[VY] += pos_local.mV[VY]; + pos_global.mdV[VZ] += pos_local.mV[VZ]; + + // Don't play a trigger sound if you can't hear it due + // to parcel "local audio only" settings. + if (!LLViewerParcelMgr::getInstance()->canHearSound(pos_global)) return; + + // Don't play sounds triggered by someone you muted. + if (LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagObjectSounds)) return; + + // Don't play sounds from an object you muted + if (LLMuteList::getInstance()->isMuted(object_id)) return; + + // Don't play sounds from an object whose parent you muted + if (parent_id.notNull() + && LLMuteList::getInstance()->isMuted(parent_id)) + { + return; + } + + // Don't play sounds from a region with maturity above current agent maturity + if( !gAgent.canAccessMaturityInRegion( region_handle ) ) + { + return; + } + + gAudiop->triggerSound(sound_id, owner_id, gain, LLAudioEngine::AUDIO_TYPE_SFX, pos_global); +} + +void process_preload_sound(LLMessageSystem *msg, void **user_data) +{ + if (!gAudiop) + { + return; + } + + LLUUID sound_id; + LLUUID object_id; + LLUUID owner_id; + + msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_SoundID, sound_id); + msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_id); + msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_OwnerID, owner_id); + + LLViewerObject *objectp = gObjectList.findObject(object_id); + if (!objectp) return; + + if (LLMuteList::getInstance()->isMuted(object_id)) return; + if (LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagObjectSounds)) return; + + LLAudioSource *sourcep = objectp->getAudioSource(owner_id); + if (!sourcep) return; + + LLAudioData *datap = gAudiop->getAudioData(sound_id); + + // Note that I don't actually do any loading of the + // audio data into a buffer at this point, as it won't actually + // help us out. + + // Don't play sounds from a region with maturity above current agent maturity + LLVector3d pos_global = objectp->getPositionGlobal(); + if (gAgent.canAccessMaturityAtGlobal(pos_global)) + { + // Add audioData starts a transfer internally. + sourcep->addAudioData(datap, FALSE); +} +} + +void process_attached_sound(LLMessageSystem *msg, void **user_data) +{ + F32 gain = 0; + LLUUID sound_id; + LLUUID object_id; + LLUUID owner_id; + U8 flags; + + msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_SoundID, sound_id); + msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_id); + msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_OwnerID, owner_id); + msg->getF32Fast(_PREHASH_DataBlock, _PREHASH_Gain, gain); + msg->getU8Fast(_PREHASH_DataBlock, _PREHASH_Flags, flags); + + LLViewerObject *objectp = gObjectList.findObject(object_id); + if (!objectp) + { + // we don't know about this object, just bail + return; + } + + if (LLMuteList::getInstance()->isMuted(object_id)) return; + + if (LLMuteList::getInstance()->isMuted(owner_id, LLMute::flagObjectSounds)) return; + + + // Don't play sounds from a region with maturity above current agent maturity + LLVector3d pos = objectp->getPositionGlobal(); + if( !gAgent.canAccessMaturityAtGlobal(pos) ) + { + return; + } + + objectp->setAttachedSound(sound_id, owner_id, gain, flags); +} + + +void process_attached_sound_gain_change(LLMessageSystem *mesgsys, void **user_data) +{ + F32 gain = 0; + LLUUID object_guid; + LLViewerObject *objectp = NULL; + + mesgsys->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_guid); + + if (!((objectp = gObjectList.findObject(object_guid)))) + { + // we don't know about this object, just bail + return; + } + + mesgsys->getF32Fast(_PREHASH_DataBlock, _PREHASH_Gain, gain); + + objectp->adjustAudioGain(gain); +} + + +void process_health_message(LLMessageSystem *mesgsys, void **user_data) +{ + F32 health; + + mesgsys->getF32Fast(_PREHASH_HealthData, _PREHASH_Health, health); + + if (gStatusBar) + { + gStatusBar->setHealth((S32)health); + } +} + + +void process_sim_stats(LLMessageSystem *msg, void **user_data) +{ + S32 count = msg->getNumberOfBlocks("Stat"); + for (S32 i = 0; i < count; ++i) + { + U32 stat_id; + F32 stat_value; + msg->getU32("Stat", "StatID", stat_id, i); + msg->getF32("Stat", "StatValue", stat_value, i); + switch (stat_id) + { + case LL_SIM_STAT_TIME_DILATION: + LLViewerStats::getInstance()->mSimTimeDilation.addValue(stat_value); + break; + case LL_SIM_STAT_FPS: + LLViewerStats::getInstance()->mSimFPS.addValue(stat_value); + break; + case LL_SIM_STAT_PHYSFPS: + LLViewerStats::getInstance()->mSimPhysicsFPS.addValue(stat_value); + break; + case LL_SIM_STAT_AGENTUPS: + LLViewerStats::getInstance()->mSimAgentUPS.addValue(stat_value); + break; + case LL_SIM_STAT_FRAMEMS: + LLViewerStats::getInstance()->mSimFrameMsec.addValue(stat_value); + break; + case LL_SIM_STAT_NETMS: + LLViewerStats::getInstance()->mSimNetMsec.addValue(stat_value); + break; + case LL_SIM_STAT_SIMOTHERMS: + LLViewerStats::getInstance()->mSimSimOtherMsec.addValue(stat_value); + break; + case LL_SIM_STAT_SIMPHYSICSMS: + LLViewerStats::getInstance()->mSimSimPhysicsMsec.addValue(stat_value); + break; + case LL_SIM_STAT_AGENTMS: + LLViewerStats::getInstance()->mSimAgentMsec.addValue(stat_value); + break; + case LL_SIM_STAT_IMAGESMS: + LLViewerStats::getInstance()->mSimImagesMsec.addValue(stat_value); + break; + case LL_SIM_STAT_SCRIPTMS: + LLViewerStats::getInstance()->mSimScriptMsec.addValue(stat_value); + break; + case LL_SIM_STAT_NUMTASKS: + LLViewerStats::getInstance()->mSimObjects.addValue(stat_value); + break; + case LL_SIM_STAT_NUMTASKSACTIVE: + LLViewerStats::getInstance()->mSimActiveObjects.addValue(stat_value); + break; + case LL_SIM_STAT_NUMAGENTMAIN: + LLViewerStats::getInstance()->mSimMainAgents.addValue(stat_value); + break; + case LL_SIM_STAT_NUMAGENTCHILD: + LLViewerStats::getInstance()->mSimChildAgents.addValue(stat_value); + break; + case LL_SIM_STAT_NUMSCRIPTSACTIVE: + LLViewerStats::getInstance()->mSimActiveScripts.addValue(stat_value); + break; + case LL_SIM_STAT_SCRIPT_EPS: + LLViewerStats::getInstance()->mSimScriptEPS.addValue(stat_value); + break; + case LL_SIM_STAT_INPPS: + LLViewerStats::getInstance()->mSimInPPS.addValue(stat_value); + break; + case LL_SIM_STAT_OUTPPS: + LLViewerStats::getInstance()->mSimOutPPS.addValue(stat_value); + break; + case LL_SIM_STAT_PENDING_DOWNLOADS: + LLViewerStats::getInstance()->mSimPendingDownloads.addValue(stat_value); + break; + case LL_SIM_STAT_PENDING_UPLOADS: + LLViewerStats::getInstance()->mSimPendingUploads.addValue(stat_value); + break; + case LL_SIM_STAT_PENDING_LOCAL_UPLOADS: + LLViewerStats::getInstance()->mSimPendingLocalUploads.addValue(stat_value); + break; + case LL_SIM_STAT_TOTAL_UNACKED_BYTES: + LLViewerStats::getInstance()->mSimTotalUnackedBytes.addValue(stat_value / 1024.f); + break; + case LL_SIM_STAT_PHYSICS_PINNED_TASKS: + LLViewerStats::getInstance()->mPhysicsPinnedTasks.addValue(stat_value); + break; + case LL_SIM_STAT_PHYSICS_LOD_TASKS: + LLViewerStats::getInstance()->mPhysicsLODTasks.addValue(stat_value); + break; + case LL_SIM_STAT_SIMPHYSICSSTEPMS: + LLViewerStats::getInstance()->mSimSimPhysicsStepMsec.addValue(stat_value); + break; + case LL_SIM_STAT_SIMPHYSICSSHAPEMS: + LLViewerStats::getInstance()->mSimSimPhysicsShapeUpdateMsec.addValue(stat_value); + break; + case LL_SIM_STAT_SIMPHYSICSOTHERMS: + LLViewerStats::getInstance()->mSimSimPhysicsOtherMsec.addValue(stat_value); + break; + case LL_SIM_STAT_SIMPHYSICSMEMORY: + LLViewerStats::getInstance()->mPhysicsMemoryAllocated.addValue(stat_value); + break; + case LL_SIM_STAT_SIMSPARETIME: + LLViewerStats::getInstance()->mSimSpareMsec.addValue(stat_value); + break; + case LL_SIM_STAT_SIMSLEEPTIME: + LLViewerStats::getInstance()->mSimSleepMsec.addValue(stat_value); + break; + case LL_SIM_STAT_IOPUMPTIME: + LLViewerStats::getInstance()->mSimPumpIOMsec.addValue(stat_value); + break; + default: + // Used to be a commented out warning. + LL_DEBUGS("Messaging") << "Unknown stat id" << stat_id << LL_ENDL; + break; + } + } + + /* + msg->getF32Fast(_PREHASH_Statistics, _PREHASH_PhysicsTimeDilation, time_dilation); + LLViewerStats::getInstance()->mSimTDStat.addValue(time_dilation); + + // Process information + // { CpuUsage F32 } + // { SimMemTotal F32 } + // { SimMemRSS F32 } + // { ProcessUptime F32 } + F32 cpu_usage; + F32 sim_mem_total; + F32 sim_mem_rss; + F32 process_uptime; + msg->getF32Fast(_PREHASH_Statistics, _PREHASH_CpuUsage, cpu_usage); + msg->getF32Fast(_PREHASH_Statistics, _PREHASH_SimMemTotal, sim_mem_total); + msg->getF32Fast(_PREHASH_Statistics, _PREHASH_SimMemRSS, sim_mem_rss); + msg->getF32Fast(_PREHASH_Statistics, _PREHASH_ProcessUptime, process_uptime); + LLViewerStats::getInstance()->mSimCPUUsageStat.addValue(cpu_usage); + LLViewerStats::getInstance()->mSimMemTotalStat.addValue(sim_mem_total); + LLViewerStats::getInstance()->mSimMemRSSStat.addValue(sim_mem_rss); + */ + + // + // Various hacks that aren't statistics, but are being handled here. + // + U32 max_tasks_per_region; + U32 region_flags; + msg->getU32("Region", "ObjectCapacity", max_tasks_per_region); + msg->getU32("Region", "RegionFlags", region_flags); + + LLViewerRegion* regionp = gAgent.getRegion(); + if (regionp) + { + BOOL was_flying = gAgent.getFlying(); + regionp->setRegionFlags(region_flags); + regionp->setMaxTasks(max_tasks_per_region); + // HACK: This makes agents drop from the sky if the region is + // set to no fly while people are still in the sim. + if (was_flying && regionp->getBlockFly()) + { + gAgent.setFlying(gAgent.canFly()); + } + } +} + + + +void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data) +{ + LLUUID animation_id; + LLUUID uuid; + S32 anim_sequence_id; + LLVOAvatar *avatarp; + + mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid); + + //clear animation flags + avatarp = (LLVOAvatar *)gObjectList.findObject(uuid); + + if (!avatarp) + { + // no agent by this ID...error? + LL_WARNS("Messaging") << "Received animation state for unknown avatar" << uuid << LL_ENDL; + return; + } + + S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList); + S32 num_source_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationSourceList); + + avatarp->mSignaledAnimations.clear(); + + if (avatarp->isSelf()) + { + LLUUID object_id; + + for( S32 i = 0; i < num_blocks; i++ ) + { + mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i); + mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i); + + LL_DEBUGS("Messaging") << "Anim sequence ID: " << anim_sequence_id << LL_ENDL; + + avatarp->mSignaledAnimations[animation_id] = anim_sequence_id; + + // *HACK: Disabling flying mode if it has been enabled shortly before the agent + // stand up animation is signaled. In this case we don't get a signal to start + // flying animation from server, the AGENT_CONTROL_FLY flag remains set but the + // avatar does not play flying animation, so we switch flying mode off. + // See LLAgent::setFlying(). This may cause "Stop Flying" button to blink. + // See EXT-2781. + if (animation_id == ANIM_AGENT_STANDUP && gAgent.getFlying()) + { + gAgent.setFlying(FALSE); + } + + if (i < num_source_blocks) + { + mesgsys->getUUIDFast(_PREHASH_AnimationSourceList, _PREHASH_ObjectID, object_id, i); + + LLViewerObject* object = gObjectList.findObject(object_id); + if (object) + { + object->mFlags |= FLAGS_ANIM_SOURCE; + + BOOL anim_found = FALSE; + LLVOAvatar::AnimSourceIterator anim_it = avatarp->mAnimationSources.find(object_id); + for (;anim_it != avatarp->mAnimationSources.end(); ++anim_it) + { + if (anim_it->second == animation_id) + { + anim_found = TRUE; + break; + } + } + + if (!anim_found) + { + avatarp->mAnimationSources.insert(LLVOAvatar::AnimationSourceMap::value_type(object_id, animation_id)); + } + } + } + } + } + else + { + for( S32 i = 0; i < num_blocks; i++ ) + { + mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i); + mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i); + avatarp->mSignaledAnimations[animation_id] = anim_sequence_id; + } + } + + if (num_blocks) + { + avatarp->processAnimationStateChanges(); + } +} + +void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data) +{ + LLUUID uuid; + mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid); + + LLVOAvatar* avatarp = (LLVOAvatar *)gObjectList.findObject(uuid); + if (avatarp) + { + avatarp->processAvatarAppearance( mesgsys ); + } + else + { + LL_WARNS("Messaging") << "avatar_appearance sent for unknown avatar " << uuid << LL_ENDL; + } +} + +void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data) +{ + LLVector4 cameraCollidePlane; + mesgsys->getVector4Fast(_PREHASH_CameraCollidePlane, _PREHASH_Plane, cameraCollidePlane); + + gAgentCamera.setCameraCollidePlane(cameraCollidePlane); +} + +void near_sit_object(BOOL success, void *data) +{ + if (success) + { + // Send message to sit on object + gMessageSystem->newMessageFast(_PREHASH_AgentSit); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gAgent.sendReliableMessage(); + } +} + +void process_avatar_sit_response(LLMessageSystem *mesgsys, void **user_data) +{ + LLVector3 sitPosition; + LLQuaternion sitRotation; + LLUUID sitObjectID; + BOOL use_autopilot; + mesgsys->getUUIDFast(_PREHASH_SitObject, _PREHASH_ID, sitObjectID); + mesgsys->getBOOLFast(_PREHASH_SitTransform, _PREHASH_AutoPilot, use_autopilot); + mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_SitPosition, sitPosition); + mesgsys->getQuatFast(_PREHASH_SitTransform, _PREHASH_SitRotation, sitRotation); + LLVector3 camera_eye; + mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_CameraEyeOffset, camera_eye); + LLVector3 camera_at; + mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_CameraAtOffset, camera_at); + BOOL force_mouselook; + mesgsys->getBOOLFast(_PREHASH_SitTransform, _PREHASH_ForceMouselook, force_mouselook); + + if (isAgentAvatarValid() && dist_vec_squared(camera_eye, camera_at) > 0.0001f) + { + gAgentCamera.setSitCamera(sitObjectID, camera_eye, camera_at); + } + + gAgentCamera.setForceMouselook(force_mouselook); + // Forcing turning off flying here to prevent flying after pressing "Stand" + // to stand up from an object. See EXT-1655. + gAgent.setFlying(FALSE); + + LLViewerObject* object = gObjectList.findObject(sitObjectID); + if (object) + { + LLVector3 sit_spot = object->getPositionAgent() + (sitPosition * object->getRotation()); + if (!use_autopilot || isAgentAvatarValid() && gAgentAvatarp->isSitting() && gAgentAvatarp->getRoot() == object->getRoot()) + { + //we're already sitting on this object, so don't autopilot + } + else + { + gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(sit_spot), "Sit", &sitRotation, near_sit_object, NULL, 0.5f); + } + } + else + { + LL_WARNS("Messaging") << "Received sit approval for unknown object " << sitObjectID << LL_ENDL; + } +} + +void process_clear_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data) +{ + LLUUID source_id; + + mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, source_id); + + LLFollowCamMgr::removeFollowCamParams(source_id); +} + +void process_set_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data) +{ + S32 type; + F32 value; + bool settingPosition = false; + bool settingFocus = false; + bool settingFocusOffset = false; + LLVector3 position; + LLVector3 focus; + LLVector3 focus_offset; + + LLUUID source_id; + + mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, source_id); + + LLViewerObject* objectp = gObjectList.findObject(source_id); + if (objectp) + { + objectp->mFlags |= FLAGS_CAMERA_SOURCE; + } + + S32 num_objects = mesgsys->getNumberOfBlocks("CameraProperty"); + for (S32 block_index = 0; block_index < num_objects; block_index++) + { + mesgsys->getS32("CameraProperty", "Type", type, block_index); + mesgsys->getF32("CameraProperty", "Value", value, block_index); + switch(type) + { + case FOLLOWCAM_PITCH: + LLFollowCamMgr::setPitch(source_id, value); + break; + case FOLLOWCAM_FOCUS_OFFSET_X: + focus_offset.mV[VX] = value; + settingFocusOffset = true; + break; + case FOLLOWCAM_FOCUS_OFFSET_Y: + focus_offset.mV[VY] = value; + settingFocusOffset = true; + break; + case FOLLOWCAM_FOCUS_OFFSET_Z: + focus_offset.mV[VZ] = value; + settingFocusOffset = true; + break; + case FOLLOWCAM_POSITION_LAG: + LLFollowCamMgr::setPositionLag(source_id, value); + break; + case FOLLOWCAM_FOCUS_LAG: + LLFollowCamMgr::setFocusLag(source_id, value); + break; + case FOLLOWCAM_DISTANCE: + LLFollowCamMgr::setDistance(source_id, value); + break; + case FOLLOWCAM_BEHINDNESS_ANGLE: + LLFollowCamMgr::setBehindnessAngle(source_id, value); + break; + case FOLLOWCAM_BEHINDNESS_LAG: + LLFollowCamMgr::setBehindnessLag(source_id, value); + break; + case FOLLOWCAM_POSITION_THRESHOLD: + LLFollowCamMgr::setPositionThreshold(source_id, value); + break; + case FOLLOWCAM_FOCUS_THRESHOLD: + LLFollowCamMgr::setFocusThreshold(source_id, value); + break; + case FOLLOWCAM_ACTIVE: + //if 1, set using followcam,. + LLFollowCamMgr::setCameraActive(source_id, value != 0.f); + break; + case FOLLOWCAM_POSITION_X: + settingPosition = true; + position.mV[ 0 ] = value; + break; + case FOLLOWCAM_POSITION_Y: + settingPosition = true; + position.mV[ 1 ] = value; + break; + case FOLLOWCAM_POSITION_Z: + settingPosition = true; + position.mV[ 2 ] = value; + break; + case FOLLOWCAM_FOCUS_X: + settingFocus = true; + focus.mV[ 0 ] = value; + break; + case FOLLOWCAM_FOCUS_Y: + settingFocus = true; + focus.mV[ 1 ] = value; + break; + case FOLLOWCAM_FOCUS_Z: + settingFocus = true; + focus.mV[ 2 ] = value; + break; + case FOLLOWCAM_POSITION_LOCKED: + LLFollowCamMgr::setPositionLocked(source_id, value != 0.f); + break; + case FOLLOWCAM_FOCUS_LOCKED: + LLFollowCamMgr::setFocusLocked(source_id, value != 0.f); + break; + + default: + break; + } + } + + if ( settingPosition ) + { + LLFollowCamMgr::setPosition(source_id, position); + } + if ( settingFocus ) + { + LLFollowCamMgr::setFocus(source_id, focus); + } + if ( settingFocusOffset ) + { + LLFollowCamMgr::setFocusOffset(source_id, focus_offset); + } +} +//end Ventrella + + +// Culled from newsim lltask.cpp +void process_name_value(LLMessageSystem *mesgsys, void **user_data) +{ + std::string temp_str; + LLUUID id; + S32 i, num_blocks; + + mesgsys->getUUIDFast(_PREHASH_TaskData, _PREHASH_ID, id); + + LLViewerObject* object = gObjectList.findObject(id); + + if (object) + { + num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_NameValueData); + for (i = 0; i < num_blocks; i++) + { + mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, temp_str, i); + LL_INFOS("Messaging") << "Added to object Name Value: " << temp_str << LL_ENDL; + object->addNVPair(temp_str); + } + } + else + { + LL_INFOS("Messaging") << "Can't find object " << id << " to add name value pair" << LL_ENDL; + } +} + +void process_remove_name_value(LLMessageSystem *mesgsys, void **user_data) +{ + std::string temp_str; + LLUUID id; + S32 i, num_blocks; + + mesgsys->getUUIDFast(_PREHASH_TaskData, _PREHASH_ID, id); + + LLViewerObject* object = gObjectList.findObject(id); + + if (object) + { + num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_NameValueData); + for (i = 0; i < num_blocks; i++) + { + mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, temp_str, i); + LL_INFOS("Messaging") << "Removed from object Name Value: " << temp_str << LL_ENDL; + object->removeNVPair(temp_str); + } + } + else + { + LL_INFOS("Messaging") << "Can't find object " << id << " to remove name value pair" << LL_ENDL; + } +} + +void process_kick_user(LLMessageSystem *msg, void** /*user_data*/) +{ + std::string message; + + msg->getStringFast(_PREHASH_UserInfo, _PREHASH_Reason, message); + + LLAppViewer::instance()->forceDisconnect(message); +} + + +/* +void process_user_list_reply(LLMessageSystem *msg, void **user_data) +{ + LLUserList::processUserListReply(msg, user_data); + return; + char firstname[MAX_STRING+1]; + char lastname[MAX_STRING+1]; + U8 status; + S32 user_count; + + user_count = msg->getNumberOfBlocks("UserBlock"); + + for (S32 i = 0; i < user_count; i++) + { + msg->getData("UserBlock", i, "FirstName", firstname); + msg->getData("UserBlock", i, "LastName", lastname); + msg->getData("UserBlock", i, "Status", &status); + + if (status & 0x01) + { + dialog_friends_add_friend(buffer, TRUE); + } + else + { + dialog_friends_add_friend(buffer, FALSE); + } + } + + dialog_friends_done_adding(); +} +*/ + +// this is not handled in processUpdateMessage +/* +void process_time_dilation(LLMessageSystem *msg, void **user_data) +{ + // get the time_dilation + U16 foo; + msg->getData("TimeDilation", "TimeDilation", &foo); + F32 time_dilation = ((F32) foo) / 65535.f; + + // get the pointer to the right region + U32 ip = msg->getSenderIP(); + U32 port = msg->getSenderPort(); + LLViewerRegion *regionp = LLWorld::getInstance()->getRegion(ip, port); + if (regionp) + { + regionp->setTimeDilation(time_dilation); + } +} +*/ + + +void process_money_balance_reply( LLMessageSystem* msg, void** ) +{ + S32 balance = 0; + S32 credit = 0; + S32 committed = 0; + std::string desc; + LLUUID tid; + + msg->getUUID("MoneyData", "TransactionID", tid); + msg->getS32("MoneyData", "MoneyBalance", balance); + msg->getS32("MoneyData", "SquareMetersCredit", credit); + msg->getS32("MoneyData", "SquareMetersCommitted", committed); + msg->getStringFast(_PREHASH_MoneyData, _PREHASH_Description, desc); + LL_INFOS("Messaging") << "L$, credit, committed: " << balance << " " << credit << " " + << committed << LL_ENDL; + + if (gStatusBar) + { + gStatusBar->setBalance(balance); + gStatusBar->setLandCredit(credit); + gStatusBar->setLandCommitted(committed); + } + + if (desc.empty() + || !gSavedSettings.getBOOL("NotifyMoneyChange")) + { + // ...nothing to display + return; + } + + // Suppress duplicate messages about the same transaction + static std::deque recent; + if (std::find(recent.rbegin(), recent.rend(), tid) != recent.rend()) + { + return; + } + + // Once the 'recent' container gets large enough, chop some + // off the beginning. + const U32 MAX_LOOKBACK = 30; + const S32 POP_FRONT_SIZE = 12; + if(recent.size() > MAX_LOOKBACK) + { + LL_DEBUGS("Messaging") << "Removing oldest transaction records" << LL_ENDL; + recent.erase(recent.begin(), recent.begin() + POP_FRONT_SIZE); + } + //LL_DEBUGS("Messaging") << "Pushing back transaction " << tid << LL_ENDL; + recent.push_back(tid); + + if (msg->has("TransactionInfo")) + { + // ...message has extended info for localization + process_money_balance_reply_extended(msg); + } + else + { + // Only old dev grids will not supply the TransactionInfo block, + // so we can just use the hard-coded English string. + LLSD args; + args["MESSAGE"] = desc; + LLNotificationsUtil::add("SystemMessage", args); + } +} + +static std::string reason_from_transaction_type(S32 transaction_type, + const std::string& item_desc) +{ + // *NOTE: The keys for the reason strings are unusual because + // an earlier version of the code used English language strings + // extracted from hard-coded server English descriptions. + // Keeping them so we don't have to re-localize them. + switch (transaction_type) + { + case TRANS_OBJECT_SALE: + { + LLStringUtil::format_map_t arg; + arg["ITEM"] = item_desc; + return LLTrans::getString("for item", arg); + } + case TRANS_LAND_SALE: + return LLTrans::getString("for a parcel of land"); + + case TRANS_LAND_PASS_SALE: + return LLTrans::getString("for a land access pass"); + + case TRANS_GROUP_LAND_DEED: + return LLTrans::getString("for deeding land"); + + case TRANS_GROUP_CREATE: + return LLTrans::getString("to create a group"); + + case TRANS_GROUP_JOIN: + return LLTrans::getString("to join a group"); + + case TRANS_UPLOAD_CHARGE: + return LLTrans::getString("to upload"); + + case TRANS_CLASSIFIED_CHARGE: + return LLTrans::getString("to publish a classified ad"); + + // These have no reason to display, but are expected and should not + // generate warnings + case TRANS_GIFT: + case TRANS_PAY_OBJECT: + case TRANS_OBJECT_PAYS: + return std::string(); + + default: + llwarns << "Unknown transaction type " + << transaction_type << llendl; + return std::string(); + } +} + +static void money_balance_group_notify(const LLUUID& group_id, + const std::string& name, + bool is_group, + std::string notification, + LLSD args, + LLSD payload) +{ + // Message uses name SLURLs, don't actually have to substitute in + // the name. We're just making sure it's available. + // Notification is either PaymentReceived or PaymentSent + LLNotificationsUtil::add(notification, args, payload); +} + +static void money_balance_avatar_notify(const LLUUID& agent_id, + const LLAvatarName& av_name, + std::string notification, + LLSD args, + LLSD payload) +{ + // Message uses name SLURLs, don't actually have to substitute in + // the name. We're just making sure it's available. + // Notification is either PaymentReceived or PaymentSent + LLNotificationsUtil::add(notification, args, payload); +} + +static void process_money_balance_reply_extended(LLMessageSystem* msg) +{ + // Added in server 1.40 and viewer 2.1, support for localization + // and agent ids for name lookup. + S32 transaction_type = 0; + LLUUID source_id; + BOOL is_source_group = FALSE; + LLUUID dest_id; + BOOL is_dest_group = FALSE; + S32 amount = 0; + std::string item_description; + + msg->getS32("TransactionInfo", "TransactionType", transaction_type); + msg->getUUID("TransactionInfo", "SourceID", source_id); + msg->getBOOL("TransactionInfo", "IsSourceGroup", is_source_group); + msg->getUUID("TransactionInfo", "DestID", dest_id); + msg->getBOOL("TransactionInfo", "IsDestGroup", is_dest_group); + msg->getS32("TransactionInfo", "Amount", amount); + msg->getString("TransactionInfo", "ItemDescription", item_description); + LL_INFOS("Money") << "MoneyBalanceReply source " << source_id + << " dest " << dest_id + << " type " << transaction_type + << " item " << item_description << LL_ENDL; + + if (source_id.isNull() && dest_id.isNull()) + { + // this is a pure balance update, no notification required + return; + } + + std::string source_slurl; + if (is_source_group) + { + source_slurl = + LLSLURL( "group", source_id, "inspect").getSLURLString(); + } + else + { + source_slurl = + LLSLURL( "agent", source_id, "completename").getSLURLString(); + } + + std::string dest_slurl; + if (is_dest_group) + { + dest_slurl = + LLSLURL( "group", dest_id, "inspect").getSLURLString(); + } + else + { + dest_slurl = + LLSLURL( "agent", dest_id, "completename").getSLURLString(); + } + + std::string reason = + reason_from_transaction_type(transaction_type, item_description); + + LLStringUtil::format_map_t args; + args["REASON"] = reason; // could be empty + args["AMOUNT"] = llformat("%d", amount); + + // Need to delay until name looked up, so need to know whether or not + // is group + bool is_name_group = false; + LLUUID name_id; + std::string message; + std::string notification; + LLSD final_args; + LLSD payload; + + bool you_paid_someone = (source_id == gAgentID); + if (you_paid_someone) + { + args["NAME"] = dest_slurl; + is_name_group = is_dest_group; + name_id = dest_id; + if (!reason.empty()) + { + if (dest_id.notNull()) + { + message = LLTrans::getString("you_paid_ldollars", args); + } + else + { + // transaction fee to the system, eg, to create a group + message = LLTrans::getString("you_paid_ldollars_no_name", args); + } + } + else + { + if (dest_id.notNull()) + { + message = LLTrans::getString("you_paid_ldollars_no_reason", args); + } + else + { + // no target, no reason, you just paid money + message = LLTrans::getString("you_paid_ldollars_no_info", args); + } + } + final_args["MESSAGE"] = message; + notification = "PaymentSent"; + } + else { + // ...someone paid you + args["NAME"] = source_slurl; + is_name_group = is_source_group; + name_id = source_id; + if (!reason.empty()) + { + message = LLTrans::getString("paid_you_ldollars", args); + } + else { + message = LLTrans::getString("paid_you_ldollars_no_reason", args); + } + final_args["MESSAGE"] = message; + + // make notification loggable + payload["from_id"] = source_id; + notification = "PaymentReceived"; + } + + // Despite using SLURLs, wait until the name is available before + // showing the notification, otherwise the UI layout is strange and + // the user sees a "Loading..." message + if (is_name_group) + { + gCacheName->getGroup(name_id, + boost::bind(&money_balance_group_notify, + _1, _2, _3, + notification, final_args, payload)); + } + else { + LLAvatarNameCache::get(name_id, + boost::bind(&money_balance_avatar_notify, + _1, _2, + notification, final_args, payload)); + } +} + + + +bool handle_special_notification_callback(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if (0 == option) + { + // set the preference to the maturity of the region we're calling + int preferredMaturity = notification["payload"]["_region_access"].asInteger(); + gSavedSettings.setU32("PreferredMaturity", preferredMaturity); + gAgent.sendMaturityPreferenceToServer(preferredMaturity); + + // notify user that the maturity preference has been changed + LLSD args; + args["RATING"] = LLViewerRegion::accessToString(preferredMaturity); + LLNotificationsUtil::add("PreferredMaturityChanged", args); + } + + return false; +} + +// some of the server notifications need special handling. This is where we do that. +bool handle_special_notification(std::string notificationID, LLSD& llsdBlock) +{ + int regionAccess = llsdBlock["_region_access"].asInteger(); + llsdBlock["REGIONMATURITY"] = LLViewerRegion::accessToString(regionAccess); + + // we're going to throw the LLSD in there in case anyone ever wants to use it + LLNotificationsUtil::add(notificationID+"_Notify", llsdBlock); + + if (regionAccess == SIM_ACCESS_MATURE) + { + if (gAgent.isTeen()) + { + LLNotificationsUtil::add(notificationID+"_KB", llsdBlock); + return true; + } + else if (gAgent.prefersPG()) + { + LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_special_notification_callback); + return true; + } + } + else if (regionAccess == SIM_ACCESS_ADULT) + { + if (!gAgent.isAdult()) + { + LLNotificationsUtil::add(notificationID+"_KB", llsdBlock); + return true; + } + else if (gAgent.prefersPG() || gAgent.prefersMature()) + { + LLNotificationsUtil::add(notificationID+"_Change", llsdBlock, llsdBlock, handle_special_notification_callback); + return true; + } + } + return false; +} + +bool attempt_standard_notification(LLMessageSystem* msgsystem) +{ + // if we have additional alert data + if (msgsystem->has(_PREHASH_AlertInfo) && msgsystem->getNumberOfBlocksFast(_PREHASH_AlertInfo) > 0) + { + // notification was specified using the new mechanism, so we can just handle it here + std::string notificationID; + std::string llsdRaw; + LLSD llsdBlock; + msgsystem->getStringFast(_PREHASH_AlertInfo, _PREHASH_Message, notificationID); + msgsystem->getStringFast(_PREHASH_AlertInfo, _PREHASH_ExtraParams, llsdRaw); + if (llsdRaw.length()) + { + std::istringstream llsdData(llsdRaw); + if (!LLSDSerialize::deserialize(llsdBlock, llsdData, llsdRaw.length())) + { + llwarns << "attempt_standard_notification: Attempted to read notification parameter data into LLSD but failed:" << llsdRaw << llendl; + } + } + + if ( + (notificationID == "RegionEntryAccessBlocked") || + (notificationID == "LandClaimAccessBlocked") || + (notificationID == "LandBuyAccessBlocked") + ) + { + /*--------------------------------------------------------------------- + (Commented so a grep will find the notification strings, since + we construct them on the fly; if you add additional notifications, + please update the comment.) + + Could throw any of the following notifications: + + RegionEntryAccessBlocked + RegionEntryAccessBlocked_Notify + RegionEntryAccessBlocked_Change + RegionEntryAccessBlocked_KB + LandClaimAccessBlocked + LandClaimAccessBlocked_Notify + LandClaimAccessBlocked_Change + LandClaimAccessBlocked_KB + LandBuyAccessBlocked + LandBuyAccessBlocked_Notify + LandBuyAccessBlocked_Change + LandBuyAccessBlocked_KB + + -----------------------------------------------------------------------*/ + if (handle_special_notification(notificationID, llsdBlock)) + { + return true; + } + } + + LLNotificationsUtil::add(notificationID, llsdBlock); + return true; + } + return false; +} + + +void process_agent_alert_message(LLMessageSystem* msgsystem, void** user_data) +{ + // make sure the cursor is back to the usual default since the + // alert is probably due to some kind of error. + gViewerWindow->getWindow()->resetBusyCount(); + + if (!attempt_standard_notification(msgsystem)) + { + BOOL modal = FALSE; + msgsystem->getBOOL("AlertData", "Modal", modal); + std::string buffer; + msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, buffer); + process_alert_core(buffer, modal); + } +} + +// The only difference between this routine and the previous is the fact that +// for this routine, the modal parameter is always false. Sadly, for the message +// handled by this routine, there is no "Modal" parameter on the message, and +// there's no API to tell if a message has the given parameter or not. +// So we can't handle the messages with the same handler. +void process_alert_message(LLMessageSystem *msgsystem, void **user_data) +{ + // make sure the cursor is back to the usual default since the + // alert is probably due to some kind of error. + gViewerWindow->getWindow()->resetBusyCount(); + + if (!attempt_standard_notification(msgsystem)) + { + BOOL modal = FALSE; + std::string buffer; + msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, buffer); + process_alert_core(buffer, modal); + } +} + +void process_alert_core(const std::string& message, BOOL modal) +{ + // HACK -- handle callbacks for specific alerts. It also is localized in notifications.xml + if ( message == "You died and have been teleported to your home location") + { + LLViewerStats::getInstance()->incStat(LLViewerStats::ST_KILLED_COUNT); + } + else if( message == "Home position set." ) + { + // save the home location image to disk + std::string snap_filename = gDirUtilp->getLindenUserDir(); + snap_filename += gDirUtilp->getDirDelimiter(); + snap_filename += SCREEN_HOME_FILENAME; + gViewerWindow->saveSnapshot(snap_filename, gViewerWindow->getWindowWidthRaw(), gViewerWindow->getWindowHeightRaw(), FALSE, FALSE); + } + + const std::string ALERT_PREFIX("ALERT: "); + const std::string NOTIFY_PREFIX("NOTIFY: "); + if (message.find(ALERT_PREFIX) == 0) + { + // Allow the server to spawn a named alert so that server alerts can be + // translated out of English. + std::string alert_name(message.substr(ALERT_PREFIX.length())); + LLNotificationsUtil::add(alert_name); + } + else if (message.find(NOTIFY_PREFIX) == 0) + { + // Allow the server to spawn a named notification so that server notifications can be + // translated out of English. + std::string notify_name(message.substr(NOTIFY_PREFIX.length())); + LLNotificationsUtil::add(notify_name); + } + else if (message[0] == '/') + { + // System message is important, show in upper-right box not tip + std::string text(message.substr(1)); + LLSD args; + if (text.substr(0,17) == "RESTART_X_MINUTES") + { + S32 mins = 0; + LLStringUtil::convertToS32(text.substr(18), mins); + args["MINUTES"] = llformat("%d",mins); + LLNotificationsUtil::add("RegionRestartMinutes", args); + } + else if (text.substr(0,17) == "RESTART_X_SECONDS") + { + S32 secs = 0; + LLStringUtil::convertToS32(text.substr(18), secs); + args["SECONDS"] = llformat("%d",secs); + LLNotificationsUtil::add("RegionRestartSeconds", args); + } + else + { + std::string new_msg =LLNotifications::instance().getGlobalString(text); + args["MESSAGE"] = new_msg; + LLNotificationsUtil::add("SystemMessage", args); + } + } + else if (modal) + { + LLSD args; + std::string new_msg =LLNotifications::instance().getGlobalString(message); + args["ERROR_MESSAGE"] = new_msg; + LLNotificationsUtil::add("ErrorMessage", args); + } + else + { + LLSD args; + std::string new_msg =LLNotifications::instance().getGlobalString(message); + args["MESSAGE"] = new_msg; + LLNotificationsUtil::add("SystemMessageTip", args); + } +} + +mean_collision_list_t gMeanCollisionList; +time_t gLastDisplayedTime = 0; + +void handle_show_mean_events(void *) +{ + if (gNoRender) + { + return; + } + LLFloaterReg::showInstance("bumps"); + //LLFloaterBump::showInstance(); +} + +void mean_name_callback(const LLUUID &id, const std::string& full_name, bool is_group) +{ + if (gNoRender) + { + return; + } + + static const U32 max_collision_list_size = 20; + if (gMeanCollisionList.size() > max_collision_list_size) + { + mean_collision_list_t::iterator iter = gMeanCollisionList.begin(); + for (U32 i=0; imPerp == id) + { + mcd->mFullName = full_name; + } + } +} + +void process_mean_collision_alert_message(LLMessageSystem *msgsystem, void **user_data) +{ + if (gAgent.inPrelude()) + { + // In prelude, bumping is OK. This dialog is rather confusing to + // newbies, so we don't show it. Drop the packet on the floor. + return; + } + + // make sure the cursor is back to the usual default since the + // alert is probably due to some kind of error. + gViewerWindow->getWindow()->resetBusyCount(); + + LLUUID perp; + U32 time; + U8 u8type; + EMeanCollisionType type; + F32 mag; + + S32 i, num = msgsystem->getNumberOfBlocks(_PREHASH_MeanCollision); + + for (i = 0; i < num; i++) + { + msgsystem->getUUIDFast(_PREHASH_MeanCollision, _PREHASH_Perp, perp); + msgsystem->getU32Fast(_PREHASH_MeanCollision, _PREHASH_Time, time); + msgsystem->getF32Fast(_PREHASH_MeanCollision, _PREHASH_Mag, mag); + msgsystem->getU8Fast(_PREHASH_MeanCollision, _PREHASH_Type, u8type); + + type = (EMeanCollisionType)u8type; + + BOOL b_found = FALSE; + + for (mean_collision_list_t::iterator iter = gMeanCollisionList.begin(); + iter != gMeanCollisionList.end(); ++iter) + { + LLMeanCollisionData *mcd = *iter; + if ((mcd->mPerp == perp) && (mcd->mType == type)) + { + mcd->mTime = time; + mcd->mMag = mag; + b_found = TRUE; + break; + } + } + + if (!b_found) + { + LLMeanCollisionData *mcd = new LLMeanCollisionData(gAgentID, perp, time, type, mag); + gMeanCollisionList.push_front(mcd); + gCacheName->get(perp, false, boost::bind(&mean_name_callback, _1, _2, _3)); + } + } +} + +void process_frozen_message(LLMessageSystem *msgsystem, void **user_data) +{ + // make sure the cursor is back to the usual default since the + // alert is probably due to some kind of error. + gViewerWindow->getWindow()->resetBusyCount(); + BOOL b_frozen; + + msgsystem->getBOOL("FrozenData", "Data", b_frozen); + + // TODO: make being frozen change view + if (b_frozen) + { + } + else + { + } +} + +// do some extra stuff once we get our economy data +void process_economy_data(LLMessageSystem *msg, void** /*user_data*/) +{ + LLGlobalEconomy::processEconomyData(msg, LLGlobalEconomy::Singleton::getInstance()); + + S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); + + LL_INFOS_ONCE("Messaging") << "EconomyData message arrived; upload cost is L$" << upload_cost << LL_ENDL; + + gMenuHolder->getChild("Upload Image")->setLabelArg("[COST]", llformat("%d", upload_cost)); + gMenuHolder->getChild("Upload Sound")->setLabelArg("[COST]", llformat("%d", upload_cost)); + gMenuHolder->getChild("Upload Animation")->setLabelArg("[COST]", llformat("%d", upload_cost)); + gMenuHolder->getChild("Bulk Upload")->setLabelArg("[COST]", llformat("%d", upload_cost)); +} + +void notify_cautioned_script_question(const LLSD& notification, const LLSD& response, S32 orig_questions, BOOL granted) +{ + // only continue if at least some permissions were requested + if (orig_questions) + { + // check to see if the person we are asking + + // "'[OBJECTNAME]', an object owned by '[OWNERNAME]', + // located in [REGIONNAME] at [REGIONPOS], + // has been permission to: [PERMISSIONS]." + + LLUIString notice(LLTrans::getString(granted ? "ScriptQuestionCautionChatGranted" : "ScriptQuestionCautionChatDenied")); + + // always include the object name and owner name + notice.setArg("[OBJECTNAME]", notification["payload"]["object_name"].asString()); + notice.setArg("[OWNERNAME]", notification["payload"]["owner_name"].asString()); + + // try to lookup viewerobject that corresponds to the object that + // requested permissions (here, taskid->requesting object id) + BOOL foundpos = FALSE; + LLViewerObject* viewobj = gObjectList.findObject(notification["payload"]["task_id"].asUUID()); + if (viewobj) + { + // found the viewerobject, get it's position in its region + LLVector3 objpos(viewobj->getPosition()); + + // try to lookup the name of the region the object is in + LLViewerRegion* viewregion = viewobj->getRegion(); + if (viewregion) + { + // got the region, so include the region and 3d coordinates of the object + notice.setArg("[REGIONNAME]", viewregion->getName()); + std::string formatpos = llformat("%.1f, %.1f,%.1f", objpos[VX], objpos[VY], objpos[VZ]); + notice.setArg("[REGIONPOS]", formatpos); + + foundpos = TRUE; + } + } + + if (!foundpos) + { + // unable to determine location of the object + notice.setArg("[REGIONNAME]", "(unknown region)"); + notice.setArg("[REGIONPOS]", "(unknown position)"); + } + + // check each permission that was requested, and list each + // permission that has been flagged as a caution permission + BOOL caution = FALSE; + S32 count = 0; + std::string perms; + for (S32 i = 0; i < SCRIPT_PERMISSION_EOF; i++) + { + if ((orig_questions & LSCRIPTRunTimePermissionBits[i]) && SCRIPT_QUESTION_IS_CAUTION[i]) + { + count++; + caution = TRUE; + + // add a comma before the permission description if it is not the first permission + // added to the list or the last permission to check + if ((count > 1) && (i < SCRIPT_PERMISSION_EOF)) + { + perms.append(", "); + } + + perms.append(LLTrans::getString(SCRIPT_QUESTIONS[i])); + } + } + + notice.setArg("[PERMISSIONS]", perms); + + // log a chat message as long as at least one requested permission + // is a caution permission + if (caution) + { + LLChat chat(notice.getString()); + // LLFloaterChat::addChat(chat, FALSE, FALSE); + } + } +} + +bool script_question_cb(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + LLMessageSystem *msg = gMessageSystem; + S32 orig = notification["payload"]["questions"].asInteger(); + S32 new_questions = orig; + + // check whether permissions were granted or denied + BOOL allowed = TRUE; + // the "yes/accept" button is the first button in the template, making it button 0 + // if any other button was clicked, the permissions were denied + if (option != 0) + { + new_questions = 0; + allowed = FALSE; + } + + LLUUID task_id = notification["payload"]["task_id"].asUUID(); + LLUUID item_id = notification["payload"]["item_id"].asUUID(); + + // reply with the permissions granted or denied + msg->newMessageFast(_PREHASH_ScriptAnswerYes); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_Data); + msg->addUUIDFast(_PREHASH_TaskID, task_id); + msg->addUUIDFast(_PREHASH_ItemID, item_id); + msg->addS32Fast(_PREHASH_Questions, new_questions); + msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); + + // only log a chat message if caution prompts are enabled + if (gSavedSettings.getBOOL("PermissionsCautionEnabled")) + { + // log a chat message, if appropriate + notify_cautioned_script_question(notification, response, orig, allowed); + } + + if ( response["Mute"] ) // mute + { + LLMuteList::getInstance()->add(LLMute(item_id, notification["payload"]["object_name"].asString(), LLMute::OBJECT)); + + // purge the message queue of any previously queued requests from the same source. DEV-4879 + class OfferMatcher : public LLNotificationsUI::LLScreenChannel::Matcher + { + public: + OfferMatcher(const LLUUID& to_block) : blocked_id(to_block) {} + bool matches(const LLNotificationPtr notification) const + { + if (notification->getName() == "ScriptQuestionCaution" + || notification->getName() == "ScriptQuestion") + { + return (notification->getPayload()["item_id"].asUUID() == blocked_id); + } + return false; + } + private: + const LLUUID& blocked_id; + }; + + LLNotificationsUI::LLChannelManager::getInstance()->killToastsFromChannel(LLUUID( + gSavedSettings.getString("NotificationChannelUUID")), OfferMatcher(item_id)); + } + + if (response["Details"]) + { + // respawn notification... + LLNotificationsUtil::add(notification["name"], notification["substitutions"], notification["payload"]); + + // ...with description on top + LLNotificationsUtil::add("DebitPermissionDetails"); + } + return false; +} +static LLNotificationFunctorRegistration script_question_cb_reg_1("ScriptQuestion", script_question_cb); +static LLNotificationFunctorRegistration script_question_cb_reg_2("ScriptQuestionCaution", script_question_cb); + +void process_script_question(LLMessageSystem *msg, void **user_data) +{ + // *TODO: Translate owner name -> [FIRST] [LAST] + + LLHost sender = msg->getSender(); + + LLUUID taskid; + LLUUID itemid; + S32 questions; + std::string object_name; + std::string owner_name; + + // taskid -> object key of object requesting permissions + msg->getUUIDFast(_PREHASH_Data, _PREHASH_TaskID, taskid ); + // itemid -> script asset key of script requesting permissions + msg->getUUIDFast(_PREHASH_Data, _PREHASH_ItemID, itemid ); + msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectName, object_name); + msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectOwner, owner_name); + msg->getS32Fast(_PREHASH_Data, _PREHASH_Questions, questions ); + + // Special case. If the objects are owned by this agent, throttle per-object instead + // of per-owner. It's common for residents to reset a ton of scripts that re-request + // permissions, as with tier boxes. UUIDs can't be valid agent names and vice-versa, + // so we'll reuse the same namespace for both throttle types. + std::string throttle_name = owner_name; + std::string self_name; + LLAgentUI::buildFullname( self_name ); + if( owner_name == self_name ) + { + throttle_name = taskid.getString(); + } + + // don't display permission requests if this object is muted + if (LLMuteList::getInstance()->isMuted(taskid)) return; + + // throttle excessive requests from any specific user's scripts + typedef LLKeyThrottle LLStringThrottle; + static LLStringThrottle question_throttle( LLREQUEST_PERMISSION_THROTTLE_LIMIT, LLREQUEST_PERMISSION_THROTTLE_INTERVAL ); + + switch (question_throttle.noteAction(throttle_name)) + { + case LLStringThrottle::THROTTLE_NEWLY_BLOCKED: + LL_INFOS("Messaging") << "process_script_question throttled" + << " owner_name:" << owner_name + << LL_ENDL; + // Fall through + + case LLStringThrottle::THROTTLE_BLOCKED: + // Escape altogether until we recover + return; + + case LLStringThrottle::THROTTLE_OK: + break; + } + + std::string script_question; + if (questions) + { + BOOL caution = FALSE; + S32 count = 0; + LLSD args; + args["OBJECTNAME"] = object_name; + args["NAME"] = LLCacheName::cleanFullName(owner_name); + + // check the received permission flags against each permission + for (S32 i = 0; i < SCRIPT_PERMISSION_EOF; i++) + { + if (questions & LSCRIPTRunTimePermissionBits[i]) + { + count++; + script_question += " " + LLTrans::getString(SCRIPT_QUESTIONS[i]) + "\n"; + + // check whether permission question should cause special caution dialog + caution |= (SCRIPT_QUESTION_IS_CAUTION[i]); + } + } + args["QUESTIONS"] = script_question; + + LLSD payload; + payload["task_id"] = taskid; + payload["item_id"] = itemid; + payload["sender"] = sender.getIPandPort(); + payload["questions"] = questions; + payload["object_name"] = object_name; + payload["owner_name"] = owner_name; + + // check whether cautions are even enabled or not + if (gSavedSettings.getBOOL("PermissionsCautionEnabled")) + { + // display the caution permissions prompt + LLNotificationsUtil::add(caution ? "ScriptQuestionCaution" : "ScriptQuestion", args, payload); + } + else + { + // fall back to default behavior if cautions are entirely disabled + LLNotificationsUtil::add("ScriptQuestion", args, payload); + } + + } +} + + +void process_derez_container(LLMessageSystem *msg, void**) +{ + LL_WARNS("Messaging") << "call to deprecated process_derez_container" << LL_ENDL; +} + +void container_inventory_arrived(LLViewerObject* object, + LLInventoryObject::object_list_t* inventory, + S32 serial_num, + void* data) +{ + LL_DEBUGS("Messaging") << "container_inventory_arrived()" << LL_ENDL; + if( gAgentCamera.cameraMouselook() ) + { + gAgentCamera.changeCameraToDefault(); + } + + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); + + if (inventory->size() > 2) + { + // create a new inventory category to put this in + LLUUID cat_id; + cat_id = gInventory.createNewCategory(gInventory.getRootFolderID(), + LLFolderType::FT_NONE, + LLTrans::getString("AcquiredItems")); + + LLInventoryObject::object_list_t::const_iterator it = inventory->begin(); + LLInventoryObject::object_list_t::const_iterator end = inventory->end(); + for ( ; it != end; ++it) + { + if ((*it)->getType() != LLAssetType::AT_CATEGORY) + { + LLInventoryObject* obj = (LLInventoryObject*)(*it); + LLInventoryItem* item = (LLInventoryItem*)(obj); + LLUUID item_id; + item_id.generate(); + time_t creation_date_utc = time_corrected(); + LLPointer new_item + = new LLViewerInventoryItem(item_id, + cat_id, + item->getPermissions(), + item->getAssetUUID(), + item->getType(), + item->getInventoryType(), + item->getName(), + item->getDescription(), + LLSaleInfo::DEFAULT, + item->getFlags(), + creation_date_utc); + new_item->updateServer(TRUE); + gInventory.updateItem(new_item); + } + } + gInventory.notifyObservers(); + if(active_panel) + { + active_panel->setSelection(cat_id, TAKE_FOCUS_NO); + } + } + else if (inventory->size() == 2) + { + // we're going to get one fake root category as well as the + // one actual object + LLInventoryObject::object_list_t::iterator it = inventory->begin(); + + if ((*it)->getType() == LLAssetType::AT_CATEGORY) + { + ++it; + } + + LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it)); + const LLUUID category = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(item->getType())); + + LLUUID item_id; + item_id.generate(); + time_t creation_date_utc = time_corrected(); + LLPointer new_item + = new LLViewerInventoryItem(item_id, category, + item->getPermissions(), + item->getAssetUUID(), + item->getType(), + item->getInventoryType(), + item->getName(), + item->getDescription(), + LLSaleInfo::DEFAULT, + item->getFlags(), + creation_date_utc); + new_item->updateServer(TRUE); + gInventory.updateItem(new_item); + gInventory.notifyObservers(); + if(active_panel) + { + active_panel->setSelection(item_id, TAKE_FOCUS_NO); + } + } + + // we've got the inventory, now delete this object if this was a take + BOOL delete_object = (BOOL)(intptr_t)data; + LLViewerRegion *region = gAgent.getRegion(); + if (delete_object && region) + { + gMessageSystem->newMessageFast(_PREHASH_ObjectDelete); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + const U8 NO_FORCE = 0; + gMessageSystem->addU8Fast(_PREHASH_Force, NO_FORCE); + gMessageSystem->nextBlockFast(_PREHASH_ObjectData); + gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID()); + gMessageSystem->sendReliable(region->getHost()); + } +} + +// method to format the time. +std::string formatted_time(const time_t& the_time) +{ + std::string dateStr = "["+LLTrans::getString("LTimeWeek")+"] [" + +LLTrans::getString("LTimeMonth")+"] [" + +LLTrans::getString("LTimeDay")+"] [" + +LLTrans::getString("LTimeHour")+"]:[" + +LLTrans::getString("LTimeMin")+"]:[" + +LLTrans::getString("LTimeSec")+"] [" + +LLTrans::getString("LTimeYear")+"]"; + + LLSD substitution; + substitution["datetime"] = (S32) the_time; + LLStringUtil::format (dateStr, substitution); + return dateStr; +} + + +void process_teleport_failed(LLMessageSystem *msg, void**) +{ + std::string reason; + std::string big_reason; + LLSD args; + + // if we have additional alert data + if (msg->has(_PREHASH_AlertInfo) && msg->getSizeFast(_PREHASH_AlertInfo, _PREHASH_Message) > 0) + { + // Get the message ID + msg->getStringFast(_PREHASH_AlertInfo, _PREHASH_Message, reason); + big_reason = LLAgent::sTeleportErrorMessages[reason]; + if ( big_reason.size() > 0 ) + { // Substitute verbose reason from the local map + args["REASON"] = big_reason; + } + else + { // Nothing found in the map - use what the server returned in the original message block + msg->getStringFast(_PREHASH_Info, _PREHASH_Reason, reason); + args["REASON"] = reason; + } + + LLSD llsd_block; + std::string llsd_raw; + msg->getStringFast(_PREHASH_AlertInfo, _PREHASH_ExtraParams, llsd_raw); + if (llsd_raw.length()) + { + std::istringstream llsd_data(llsd_raw); + if (!LLSDSerialize::deserialize(llsd_block, llsd_data, llsd_raw.length())) + { + llwarns << "process_teleport_failed: Attempted to read alert parameter data into LLSD but failed:" << llsd_raw << llendl; + } + else + { + // change notification name in this special case + if (handle_special_notification("RegionEntryAccessBlocked", llsd_block)) + { + if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) + { + gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); + } + return; + } + } + } + + } + else + { + msg->getStringFast(_PREHASH_Info, _PREHASH_Reason, reason); + + big_reason = LLAgent::sTeleportErrorMessages[reason]; + if ( big_reason.size() > 0 ) + { // Substitute verbose reason from the local map + args["REASON"] = big_reason; + } + else + { // Nothing found in the map - use what the server returned + args["REASON"] = reason; + } + } + + LLNotificationsUtil::add("CouldNotTeleportReason", args); + + // Let the interested parties know that teleport failed. + LLViewerParcelMgr::getInstance()->onTeleportFailed(); + + if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) + { + gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); + } +} + +void process_teleport_local(LLMessageSystem *msg,void**) +{ + LLUUID agent_id; + msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id); + if (agent_id != gAgent.getID()) + { + LL_WARNS("Messaging") << "Got teleport notification for wrong agent!" << LL_ENDL; + return; + } + + U32 location_id; + LLVector3 pos, look_at; + U32 teleport_flags; + msg->getU32Fast(_PREHASH_Info, _PREHASH_LocationID, location_id); + msg->getVector3Fast(_PREHASH_Info, _PREHASH_Position, pos); + msg->getVector3Fast(_PREHASH_Info, _PREHASH_LookAt, look_at); + msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags); + + if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE ) + { + if( gAgent.getTeleportState() == LLAgent::TELEPORT_LOCAL ) + { + // To prevent TeleportStart messages re-activating the progress screen right + // after tp, keep the teleport state and let progress screen clear it after a short delay + // (progress screen is active but not visible) *TODO: remove when SVC-5290 is fixed + gTeleportDisplayTimer.reset(); + gTeleportDisplay = TRUE; + } + else + { + gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); + } + } + + // Sim tells us whether the new position is off the ground + if (teleport_flags & TELEPORT_FLAGS_IS_FLYING) + { + gAgent.setFlying(TRUE); + } + else + { + gAgent.setFlying(FALSE); + } + + gAgent.setPositionAgent(pos); + gAgentCamera.slamLookAt(look_at); + + if ( !(gAgent.getTeleportKeepsLookAt() && LLViewerJoystick::getInstance()->getOverrideCamera()) ) + { + gAgentCamera.resetView(TRUE, TRUE); + } + + // send camera update to new region + gAgentCamera.updateCamera(); + + send_agent_update(TRUE, TRUE); + + // Let the interested parties know we've teleported. + // Vadim *HACK: Agent position seems to get reset (to render position?) + // on each frame, so we have to pass the new position manually. + LLViewerParcelMgr::getInstance()->onTeleportFinished(true, gAgent.getPosGlobalFromAgent(pos)); +} + +void send_simple_im(const LLUUID& to_id, + const std::string& message, + EInstantMessage dialog, + const LLUUID& id) +{ + std::string my_name; + LLAgentUI::buildFullname(my_name); + send_improved_im(to_id, + my_name, + message, + IM_ONLINE, + dialog, + id, + NO_TIMESTAMP, + (U8*)EMPTY_BINARY_BUCKET, + EMPTY_BINARY_BUCKET_SIZE); +} + +void send_group_notice(const LLUUID& group_id, + const std::string& subject, + const std::string& message, + const LLInventoryItem* item) +{ + // Put this notice into an instant message form. + // This will mean converting the item to a binary bucket, + // and the subject/message into a single field. + std::string my_name; + LLAgentUI::buildFullname(my_name); + + // Combine subject + message into a single string. + std::ostringstream subject_and_message; + // TODO: turn all existing |'s into ||'s in subject and message. + subject_and_message << subject << "|" << message; + + // Create an empty binary bucket. + U8 bin_bucket[MAX_INVENTORY_BUFFER_SIZE]; + U8* bucket_to_send = bin_bucket; + bin_bucket[0] = '\0'; + S32 bin_bucket_size = EMPTY_BINARY_BUCKET_SIZE; + // If there is an item being sent, pack it into the binary bucket. + if (item) + { + LLSD item_def; + item_def["item_id"] = item->getUUID(); + item_def["owner_id"] = item->getPermissions().getOwner(); + std::ostringstream ostr; + LLSDSerialize::serialize(item_def, ostr, LLSDSerialize::LLSD_XML); + bin_bucket_size = ostr.str().copy( + (char*)bin_bucket, ostr.str().size()); + bin_bucket[bin_bucket_size] = '\0'; + } + else + { + bucket_to_send = (U8*) EMPTY_BINARY_BUCKET; + } + + + send_improved_im( + group_id, + my_name, + subject_and_message.str(), + IM_ONLINE, + IM_GROUP_NOTICE, + LLUUID::null, + NO_TIMESTAMP, + bucket_to_send, + bin_bucket_size); +} + +bool handle_lure_callback(const LLSD& notification, const LLSD& response) +{ + std::string text = response["message"].asString(); + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + text.append("\r\n").append(slurl.getSLURLString()); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if(0 == option) + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_StartLure); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_Info); + msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in. + msg->addStringFast(_PREHASH_Message, text); + for(LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray(); + it != notification["payload"]["ids"].endArray(); + ++it) + { + LLUUID target_id = it->asUUID(); + + msg->nextBlockFast(_PREHASH_TargetData); + msg->addUUIDFast(_PREHASH_TargetID, target_id); + + // Record the offer. + { + std::string target_name; + gCacheName->getFullName(target_id, target_name); // for im log filenames + LLSD args; + args["TO_NAME"] = LLSLURL("agent", target_id, "displayname").getSLURLString();; + + LLSD payload; + + //*TODO please rewrite all keys to the same case, lower or upper + payload["from_id"] = target_id; + payload["SUPPRESS_TOAST"] = true; + LLNotificationsUtil::add("TeleportOfferSent", args, payload); + + // Add the recepient to the recent people list. + LLRecentPeople::instance().add(target_id); + } + } + gAgent.sendReliableMessage(); + } + + return false; +} + +void handle_lure(const LLUUID& invitee) +{ + LLDynamicArray ids; + ids.push_back(invitee); + handle_lure(ids); +} + +// Prompt for a message to the invited user. +void handle_lure(const uuid_vec_t& ids) +{ + if (ids.empty()) return; + + if (!gAgent.getRegion()) return; + + LLSD edit_args; + edit_args["REGION"] = gAgent.getRegion()->getName(); + + LLSD payload; + for (LLDynamicArray::const_iterator it = ids.begin(); + it != ids.end(); + ++it) + { + payload["ids"].append(*it); + } + if (gAgent.isGodlike()) + { + LLNotificationsUtil::add("OfferTeleportFromGod", edit_args, payload, handle_lure_callback); + } + else + { + LLNotificationsUtil::add("OfferTeleport", edit_args, payload, handle_lure_callback); + } +} + + +void send_improved_im(const LLUUID& to_id, + const std::string& name, + const std::string& message, + U8 offline, + EInstantMessage dialog, + const LLUUID& id, + U32 timestamp, + const U8* binary_bucket, + S32 binary_bucket_size) +{ + pack_instant_message( + gMessageSystem, + gAgent.getID(), + FALSE, + gAgent.getSessionID(), + to_id, + name, + message, + offline, + dialog, + id, + 0, + LLUUID::null, + gAgent.getPositionAgent(), + timestamp, + binary_bucket, + binary_bucket_size); + gAgent.sendReliableMessage(); +} + + +void send_places_query(const LLUUID& query_id, + const LLUUID& trans_id, + const std::string& query_text, + U32 query_flags, + S32 category, + const std::string& sim_name) +{ + LLMessageSystem* msg = gMessageSystem; + + msg->newMessage("PlacesQuery"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgent.getID()); + msg->addUUID("SessionID", gAgent.getSessionID()); + msg->addUUID("QueryID", query_id); + msg->nextBlock("TransactionData"); + msg->addUUID("TransactionID", trans_id); + msg->nextBlock("QueryData"); + msg->addString("QueryText", query_text); + msg->addU32("QueryFlags", query_flags); + msg->addS8("Category", (S8)category); + msg->addString("SimName", sim_name); + gAgent.sendReliableMessage(); +} + + +void process_user_info_reply(LLMessageSystem* msg, void**) +{ + LLUUID agent_id; + msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id); + if(agent_id != gAgent.getID()) + { + LL_WARNS("Messaging") << "process_user_info_reply - " + << "wrong agent id." << LL_ENDL; + } + + BOOL im_via_email; + msg->getBOOLFast(_PREHASH_UserData, _PREHASH_IMViaEMail, im_via_email); + std::string email; + msg->getStringFast(_PREHASH_UserData, _PREHASH_EMail, email); + std::string dir_visibility; + msg->getString( "UserData", "DirectoryVisibility", dir_visibility); + + LLFloaterPreference::updateUserInfo(dir_visibility, im_via_email, email); + LLFloaterPostcard::updateUserInfo(email); +} + + +//--------------------------------------------------------------------------- +// Script Dialog +//--------------------------------------------------------------------------- + +const S32 SCRIPT_DIALOG_MAX_BUTTONS = 12; +const S32 SCRIPT_DIALOG_BUTTON_STR_SIZE = 24; +const S32 SCRIPT_DIALOG_MAX_MESSAGE_SIZE = 512; +const char* SCRIPT_DIALOG_HEADER = "Script Dialog:\n"; + +bool callback_script_dialog(const LLSD& notification, const LLSD& response) +{ + LLNotificationForm form(notification["form"]); + + std::string rtn_text; + S32 button_idx; + button_idx = LLNotification::getSelectedOption(notification, response); + if (response[TEXTBOX_MAGIC_TOKEN].isDefined()) + { + if (response[TEXTBOX_MAGIC_TOKEN].isString()) + rtn_text = response[TEXTBOX_MAGIC_TOKEN].asString(); + else + rtn_text.clear(); // bool marks empty string + } + else + { + rtn_text = LLNotification::getSelectedOptionName(response); + } + + // Didn't click "Ignore" + if (button_idx != -1) + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessage("ScriptDialogReply"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID", gAgent.getID()); + msg->addUUID("SessionID", gAgent.getSessionID()); + msg->nextBlock("Data"); + msg->addUUID("ObjectID", notification["payload"]["object_id"].asUUID()); + msg->addS32("ChatChannel", notification["payload"]["chat_channel"].asInteger()); + msg->addS32("ButtonIndex", button_idx); + msg->addString("ButtonLabel", rtn_text); + msg->sendReliable(LLHost(notification["payload"]["sender"].asString())); + } + + return false; +} +static LLNotificationFunctorRegistration callback_script_dialog_reg_1("ScriptDialog", callback_script_dialog); +static LLNotificationFunctorRegistration callback_script_dialog_reg_2("ScriptDialogGroup", callback_script_dialog); + +void process_script_dialog(LLMessageSystem* msg, void**) +{ + S32 i; + LLSD payload; + + LLUUID object_id; + msg->getUUID("Data", "ObjectID", object_id); + + if (LLMuteList::getInstance()->isMuted(object_id)) + { + return; + } + + std::string message; + std::string first_name; + std::string last_name; + std::string title; + + S32 chat_channel; + msg->getString("Data", "FirstName", first_name); + msg->getString("Data", "LastName", last_name); + msg->getString("Data", "ObjectName", title); + msg->getString("Data", "Message", message); + msg->getS32("Data", "ChatChannel", chat_channel); + + // unused for now + LLUUID image_id; + msg->getUUID("Data", "ImageID", image_id); + + payload["sender"] = msg->getSender().getIPandPort(); + payload["object_id"] = object_id; + payload["chat_channel"] = chat_channel; + + // build up custom form + S32 button_count = msg->getNumberOfBlocks("Buttons"); + if (button_count > SCRIPT_DIALOG_MAX_BUTTONS) + { + llwarns << "Too many script dialog buttons - omitting some" << llendl; + button_count = SCRIPT_DIALOG_MAX_BUTTONS; + } + + LLNotificationForm form; + for (i = 0; i < button_count; i++) + { + std::string tdesc; + msg->getString("Buttons", "ButtonLabel", tdesc, i); + form.addElement("button", std::string(tdesc)); + } + + LLSD args; + args["TITLE"] = title; + args["MESSAGE"] = message; + LLNotificationPtr notification; + if (!first_name.empty()) + { + args["NAME"] = LLCacheName::buildFullName(first_name, last_name); + notification = LLNotifications::instance().add( + LLNotification::Params("ScriptDialog").substitutions(args).payload(payload).form_elements(form.asLLSD())); + } + else + { + args["GROUPNAME"] = last_name; + notification = LLNotifications::instance().add( + LLNotification::Params("ScriptDialogGroup").substitutions(args).payload(payload).form_elements(form.asLLSD())); + } +} + +//--------------------------------------------------------------------------- + + +std::vector gLoadUrlList; + +bool callback_load_url(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if (0 == option) + { + LLWeb::loadURL(notification["payload"]["url"].asString()); + } + + return false; +} +static LLNotificationFunctorRegistration callback_load_url_reg("LoadWebPage", callback_load_url); + + +// We've got the name of the person who owns the object hurling the url. +// Display confirmation dialog. +void callback_load_url_name(const LLUUID& id, const std::string& full_name, bool is_group) +{ + std::vector::iterator it; + for (it = gLoadUrlList.begin(); it != gLoadUrlList.end(); ) + { + LLSD load_url_info = *it; + if (load_url_info["owner_id"].asUUID() == id) + { + it = gLoadUrlList.erase(it); + + std::string owner_name; + if (is_group) + { + owner_name = full_name + LLTrans::getString("Group"); + } + else + { + owner_name = full_name; + } + + // For legacy name-only mutes. + if (LLMuteList::getInstance()->isMuted(LLUUID::null, owner_name)) + { + continue; + } + LLSD args; + args["URL"] = load_url_info["url"].asString(); + args["MESSAGE"] = load_url_info["message"].asString();; + args["OBJECTNAME"] = load_url_info["object_name"].asString(); + args["NAME"] = owner_name; + + LLNotificationsUtil::add("LoadWebPage", args, load_url_info); + } + else + { + ++it; + } + } +} + +void process_load_url(LLMessageSystem* msg, void**) +{ + LLUUID object_id; + LLUUID owner_id; + BOOL owner_is_group; + char object_name[256]; /* Flawfinder: ignore */ + char message[256]; /* Flawfinder: ignore */ + char url[256]; /* Flawfinder: ignore */ + + msg->getString("Data", "ObjectName", 256, object_name); + msg->getUUID( "Data", "ObjectID", object_id); + msg->getUUID( "Data", "OwnerID", owner_id); + msg->getBOOL( "Data", "OwnerIsGroup", owner_is_group); + msg->getString("Data", "Message", 256, message); + msg->getString("Data", "URL", 256, url); + + LLSD payload; + payload["object_id"] = object_id; + payload["owner_id"] = owner_id; + payload["owner_is_group"] = owner_is_group; + payload["object_name"] = object_name; + payload["message"] = message; + payload["url"] = url; + + // URL is safety checked in load_url above + + // Check if object or owner is muted + if (LLMuteList::getInstance()->isMuted(object_id, object_name) || + LLMuteList::getInstance()->isMuted(owner_id)) + { + LL_INFOS("Messaging")<<"Ignoring load_url from muted object/owner."<get(owner_id, owner_is_group, + boost::bind(&callback_load_url_name, _1, _2, _3)); +} + + +void callback_download_complete(void** data, S32 result, LLExtStat ext_status) +{ + std::string* filepath = (std::string*)data; + LLSD args; + args["DOWNLOAD_PATH"] = *filepath; + LLNotificationsUtil::add("FinishedRawDownload", args); + delete filepath; +} + + +void process_initiate_download(LLMessageSystem* msg, void**) +{ + LLUUID agent_id; + msg->getUUID("AgentData", "AgentID", agent_id); + if (agent_id != gAgent.getID()) + { + LL_WARNS("Messaging") << "Initiate download for wrong agent" << LL_ENDL; + return; + } + + std::string sim_filename; + std::string viewer_filename; + msg->getString("FileData", "SimFilename", sim_filename); + msg->getString("FileData", "ViewerFilename", viewer_filename); + + if (!gXferManager->validateFileForRequest(viewer_filename)) + { + llwarns << "SECURITY: Unauthorized download to local file " << viewer_filename << llendl; + return; + } + gXferManager->requestFile(viewer_filename, + sim_filename, + LL_PATH_NONE, + msg->getSender(), + FALSE, // don't delete remote + callback_download_complete, + (void**)new std::string(viewer_filename)); +} + + +void process_script_teleport_request(LLMessageSystem* msg, void**) +{ + if (!gSavedSettings.getBOOL("ScriptsCanShowUI")) return; + + std::string object_name; + std::string sim_name; + LLVector3 pos; + LLVector3 look_at; + + msg->getString("Data", "ObjectName", object_name); + msg->getString("Data", "SimName", sim_name); + msg->getVector3("Data", "SimPosition", pos); + msg->getVector3("Data", "LookAt", look_at); + + LLFloaterWorldMap* instance = LLFloaterWorldMap::getInstance(); + if(instance) + { + instance->trackURL( + sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]); + LLFloaterReg::showInstance("world_map", "center"); + } + + // remove above two lines and replace with below line + // to re-enable parcel browser for llMapDestination() + // LLURLDispatcher::dispatch(LLSLURL::buildSLURL(sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]), FALSE); + +} + +void process_covenant_reply(LLMessageSystem* msg, void**) +{ + LLUUID covenant_id, estate_owner_id; + std::string estate_name; + U32 covenant_timestamp; + msg->getUUID("Data", "CovenantID", covenant_id); + msg->getU32("Data", "CovenantTimestamp", covenant_timestamp); + msg->getString("Data", "EstateName", estate_name); + msg->getUUID("Data", "EstateOwnerID", estate_owner_id); + + LLPanelEstateCovenant::updateEstateName(estate_name); + LLPanelLandCovenant::updateEstateName(estate_name); + LLFloaterBuyLand::updateEstateName(estate_name); + + std::string owner_name = + LLSLURL("agent", estate_owner_id, "inspect").getSLURLString(); + LLPanelEstateCovenant::updateEstateOwnerName(owner_name); + LLPanelLandCovenant::updateEstateOwnerName(owner_name); + LLFloaterBuyLand::updateEstateOwnerName(owner_name); + + LLPanelPlaceProfile* panel = LLSideTray::getInstance()->getPanel("panel_place_profile"); + if (panel) + { + panel->updateEstateName(estate_name); + panel->updateEstateOwnerName(owner_name); + } + + // standard message, not from system + std::string last_modified; + if (covenant_timestamp == 0) + { + last_modified = LLTrans::getString("covenant_last_modified")+LLTrans::getString("never_text"); + } + else + { + last_modified = LLTrans::getString("covenant_last_modified")+"[" + +LLTrans::getString("LTimeWeek")+"] [" + +LLTrans::getString("LTimeMonth")+"] [" + +LLTrans::getString("LTimeDay")+"] [" + +LLTrans::getString("LTimeHour")+"]:[" + +LLTrans::getString("LTimeMin")+"]:[" + +LLTrans::getString("LTimeSec")+"] [" + +LLTrans::getString("LTimeYear")+"]"; + LLSD substitution; + substitution["datetime"] = (S32) covenant_timestamp; + LLStringUtil::format (last_modified, substitution); + } + + LLPanelEstateCovenant::updateLastModified(last_modified); + LLPanelLandCovenant::updateLastModified(last_modified); + LLFloaterBuyLand::updateLastModified(last_modified); + + // load the actual covenant asset data + const BOOL high_priority = TRUE; + if (covenant_id.notNull()) + { + gAssetStorage->getEstateAsset(gAgent.getRegionHost(), + gAgent.getID(), + gAgent.getSessionID(), + covenant_id, + LLAssetType::AT_NOTECARD, + ET_Covenant, + onCovenantLoadComplete, + NULL, + high_priority); + } + else + { + std::string covenant_text; + if (estate_owner_id.isNull()) + { + // mainland + covenant_text = LLTrans::getString("RegionNoCovenant"); + } + else + { + covenant_text = LLTrans::getString("RegionNoCovenantOtherOwner"); + } + LLPanelEstateCovenant::updateCovenantText(covenant_text, covenant_id); + LLPanelLandCovenant::updateCovenantText(covenant_text); + LLFloaterBuyLand::updateCovenantText(covenant_text, covenant_id); + if (panel) + { + panel->updateCovenantText(covenant_text); + } + } +} + +void onCovenantLoadComplete(LLVFS *vfs, + const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) +{ + LL_DEBUGS("Messaging") << "onCovenantLoadComplete()" << LL_ENDL; + std::string covenant_text; + if(0 == status) + { + LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + + S32 file_length = file.getSize(); + + std::vector buffer(file_length+1); + file.read((U8*)&buffer[0], file_length); + // put a EOS at the end + buffer[file_length] = '\0'; + + if( (file_length > 19) && !strncmp( &buffer[0], "Linden text version", 19 ) ) + { + LLViewerTextEditor::Params params; + params.name("temp"); + params.max_text_length(file_length+1); + LLViewerTextEditor * editor = LLUICtrlFactory::create (params); + if( !editor->importBuffer( &buffer[0], file_length+1 ) ) + { + LL_WARNS("Messaging") << "Problem importing estate covenant." << LL_ENDL; + covenant_text = "Problem importing estate covenant."; + } + else + { + // Version 0 (just text, doesn't include version number) + covenant_text = editor->getText(); + } + delete editor; + } + else + { + LL_WARNS("Messaging") << "Problem importing estate covenant: Covenant file format error." << LL_ENDL; + covenant_text = "Problem importing estate covenant: Covenant file format error."; + } + } + else + { + LLViewerStats::getInstance()->incStat( LLViewerStats::ST_DOWNLOAD_FAILED ); + + if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status || + LL_ERR_FILE_EMPTY == status) + { + covenant_text = "Estate covenant notecard is missing from database."; + } + else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status) + { + covenant_text = "Insufficient permissions to view estate covenant."; + } + else + { + covenant_text = "Unable to load estate covenant at this time."; + } + + LL_WARNS("Messaging") << "Problem loading notecard: " << status << LL_ENDL; + } + LLPanelEstateCovenant::updateCovenantText(covenant_text, asset_uuid); + LLPanelLandCovenant::updateCovenantText(covenant_text); + LLFloaterBuyLand::updateCovenantText(covenant_text, asset_uuid); + + LLPanelPlaceProfile* panel = LLSideTray::getInstance()->getPanel("panel_place_profile"); + if (panel) + { + panel->updateCovenantText(covenant_text); + } +} + + +void process_feature_disabled_message(LLMessageSystem* msg, void**) +{ + // Handle Blacklisted feature simulator response... + LLUUID agentID; + LLUUID transactionID; + std::string messageText; + msg->getStringFast(_PREHASH_FailureInfo,_PREHASH_ErrorMessage, messageText,0); + msg->getUUIDFast(_PREHASH_FailureInfo,_PREHASH_AgentID,agentID); + msg->getUUIDFast(_PREHASH_FailureInfo,_PREHASH_TransactionID,transactionID); + + LL_WARNS("Messaging") << "Blacklisted Feature Response:" << messageText << LL_ENDL; +} + +// ------------------------------------------------------------ +// Message system exception callbacks +// ------------------------------------------------------------ + +void invalid_message_callback(LLMessageSystem* msg, + void*, + EMessageException exception) +{ + LLAppViewer::instance()->badNetworkHandler(); +} + +// Please do not add more message handlers here. This file is huge. +// Put them in a file related to the functionality you are implementing. + +void LLOfferInfo::forceResponse(InventoryOfferResponse response) +{ + LLNotification::Params params("UserGiveItem"); + params.functor.function(boost::bind(&LLOfferInfo::inventory_offer_callback, this, _1, _2)); + LLNotifications::instance().forceResponse(params, response); +} + diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 82bc164021..81479e8b49 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -884,13 +884,14 @@ void LLViewerObjectList::clearDebugText() void LLViewerObjectList::cleanupReferences(LLViewerObject *objectp) { LLMemType mt(LLMemType::MTYPE_OBJECT); - if (mDeadObjects.count(objectp->mID)) + if (mDeadObjects.find(objectp->mID) != mDeadObjects.end()) { - llinfos << "Object " << objectp->mID << " already on dead list, ignoring cleanup!" << llendl; - return; + llinfos << "Object " << objectp->mID << " already on dead list!" << llendl; + } + else + { + mDeadObjects.insert(objectp->mID); } - - mDeadObjects.insert(std::pair >(objectp->mID, objectp)); // Cleanup any references we have to this object // Remove from object map so noone can look it up. @@ -1140,6 +1141,45 @@ bool LLViewerObjectList::hasMapObjectInRegion(LLViewerRegion* regionp) return false ; } +//make sure the region is cleaned up. +void LLViewerObjectList::clearAllMapObjectsInRegion(LLViewerRegion* regionp) +{ + std::set dead_object_list ; + std::set region_object_list ; + for (vobj_list_t::iterator iter = mMapObjects.begin(); iter != mMapObjects.end(); ++iter) + { + LLViewerObject* objectp = *iter; + + if(objectp->isDead()) + { + dead_object_list.insert(objectp) ; + } + else if(objectp->getRegion() == regionp) + { + region_object_list.insert(objectp) ; + } + } + + if(dead_object_list.size() > 0) + { + llwarns << "There are " << dead_object_list.size() << " dead objects on the map!" << llendl ; + + for(std::set::iterator iter = dead_object_list.begin(); iter != dead_object_list.end(); ++iter) + { + cleanupReferences(*iter) ; + } + } + if(region_object_list.size() > 0) + { + llwarns << "There are " << region_object_list.size() << " objects not removed from the deleted region!" << llendl ; + + for(std::set::iterator iter = region_object_list.begin(); iter != region_object_list.end(); ++iter) + { + (*iter)->markDead() ; + } + } +} + void LLViewerObjectList::renderObjectsForMap(LLNetMap &netmap) { LLColor4 above_water_color = LLUIColorTable::instance().getColor( "NetMapOtherOwnAboveWater" ); @@ -1159,7 +1199,11 @@ void LLViewerObjectList::renderObjectsForMap(LLNetMap &netmap) { LLViewerObject* objectp = *iter; - llassert_always(!objectp->isDead()); + //llassert_always(!objectp->isDead()); + if(objectp->isDead())//some dead objects somehow not cleaned. + { + continue ; + } if (!objectp->getRegion() || objectp->isOrphaned() || objectp->isAttachment()) { diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h index 8cff8e988a..22a7f97c38 100644 --- a/indra/newview/llviewerobjectlist.h +++ b/indra/newview/llviewerobjectlist.h @@ -88,6 +88,7 @@ public: void shiftObjects(const LLVector3 &offset); bool hasMapObjectInRegion(LLViewerRegion* regionp) ; + void clearAllMapObjectsInRegion(LLViewerRegion* regionp) ; void renderObjectsForMap(LLNetMap &netmap); void renderObjectBounds(const LLVector3 ¢er); @@ -181,8 +182,7 @@ protected: vobj_list_t mMapObjects; - typedef std::map > vo_map; - vo_map mDeadObjects; // Need to keep multiple entries per UUID + std::set mDeadObjects; std::map > mUUIDObjectMap; diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index cd16b15e3e..cf7f3f80ad 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -2691,12 +2691,10 @@ void LLViewerFetchedTexture::saveRawImage() mLastReferencedSavedRawImageTime = sCurrentTime ; } -void LLViewerFetchedTexture::forceToSaveRawImage(S32 desired_discard, bool from_callback) +void LLViewerFetchedTexture::forceToSaveRawImage(S32 desired_discard) { if(!mForceToSaveRawImage || mDesiredSavedRawDiscardLevel < 0 || mDesiredSavedRawDiscardLevel > desired_discard) { - llassert_always(from_callback || mBoostLevel == LLViewerTexture::BOOST_PREVIEW) ; - mForceToSaveRawImage = TRUE ; mDesiredSavedRawDiscardLevel = desired_discard ; diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index b5636bbdc7..d512f8ec3a 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -465,7 +465,7 @@ public: S32 getCachedRawImageLevel() const {return mCachedRawDiscardLevel;} BOOL isCachedRawImageReady() const {return mCachedRawImageReady ;} BOOL isRawImageValid()const { return mIsRawImageValid ; } - void forceToSaveRawImage(S32 desired_discard = 0, bool from_callback = false) ; + void forceToSaveRawImage(S32 desired_discard = 0) ; /*virtual*/ void setCachedRawImage(S32 discard_level, LLImageRaw* imageraw) ; void destroySavedRawImage() ; LLImageRaw* getSavedRawImage() ; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 093ce9a67c..2843e4e9ae 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1,5026 +1,5034 @@ -/** - * @file llviewerwindow.cpp - * @brief Implementation of the LLViewerWindow class. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llviewerwindow.h" - -#if LL_WINDOWS -#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally -#endif - -// system library includes -#include -#include -#include -#include - -#include "llagent.h" -#include "llagentcamera.h" -#include "llfloaterreg.h" -#include "llpanellogin.h" -#include "llviewerkeyboard.h" -#include "llviewermenu.h" - -#include "llviewquery.h" -#include "llxmltree.h" -#include "llslurl.h" -//#include "llviewercamera.h" -#include "llrender.h" - -#include "llvoiceclient.h" // for push-to-talk button handling - -// -// TODO: Many of these includes are unnecessary. Remove them. -// - -// linden library includes -#include "llaudioengine.h" // mute on minimize -#include "indra_constants.h" -#include "llassetstorage.h" -#include "llerrorcontrol.h" -#include "llfontgl.h" -#include "llmousehandler.h" -#include "llrect.h" -#include "llsky.h" -#include "llstring.h" -#include "llui.h" -#include "lluuid.h" -#include "llview.h" -#include "llxfermanager.h" -#include "message.h" -#include "object_flags.h" -#include "lltimer.h" -#include "timing.h" -#include "llviewermenu.h" -#include "lltooltip.h" -#include "llmediaentry.h" -#include "llurldispatcher.h" - -// newview includes -#include "llagent.h" -#include "llbox.h" -#include "llconsole.h" -#include "llviewercontrol.h" -#include "llcylinder.h" -#include "lldebugview.h" -#include "lldir.h" -#include "lldrawable.h" -#include "lldrawpoolalpha.h" -#include "lldrawpoolbump.h" -#include "lldrawpoolwater.h" -#include "llmaniptranslate.h" -#include "llface.h" -#include "llfeaturemanager.h" -#include "llfilepicker.h" -#include "llfirstuse.h" -#include "llfloater.h" -#include "llfloaterbuildoptions.h" -#include "llfloaterbuyland.h" -#include "llfloatercamera.h" -#include "llfloaterland.h" -#include "llfloaterinspect.h" -#include "llfloatermap.h" -#include "llfloaternamedesc.h" -#include "llfloaterpreference.h" -#include "llfloatersnapshot.h" -#include "llfloatertools.h" -#include "llfloaterworldmap.h" -#include "llfocusmgr.h" -#include "llfontfreetype.h" -#include "llgesturemgr.h" -#include "llglheaders.h" -#include "lltooltip.h" -#include "llhudmanager.h" -#include "llhudobject.h" -#include "llhudview.h" -#include "llimagebmp.h" -#include "llimagej2c.h" -#include "llimageworker.h" -#include "llkeyboard.h" -#include "lllineeditor.h" -#include "llmenugl.h" -#include "llmodaldialog.h" -#include "llmorphview.h" -#include "llmoveview.h" -#include "llnavigationbar.h" -#include "llpopupview.h" -#include "llpreviewtexture.h" -#include "llprogressview.h" -#include "llresmgr.h" -#include "llsidetray.h" -#include "llselectmgr.h" -#include "llrootview.h" -#include "llrendersphere.h" -#include "llstartup.h" -#include "llstatusbar.h" -#include "llstatview.h" -#include "llsurface.h" -#include "llsurfacepatch.h" -#include "lltexlayer.h" -#include "lltextbox.h" -#include "lltexturecache.h" -#include "lltexturefetch.h" -#include "lltextureview.h" -#include "lltool.h" -#include "lltoolcomp.h" -#include "lltooldraganddrop.h" -#include "lltoolface.h" -#include "lltoolfocus.h" -#include "lltoolgrab.h" -#include "lltoolmgr.h" -#include "lltoolmorph.h" -#include "lltoolpie.h" -#include "lltoolselectland.h" -#include "lltrans.h" -#include "lluictrlfactory.h" -#include "llurldispatcher.h" // SLURL from other app instance -#include "llversioninfo.h" -#include "llvieweraudio.h" -#include "llviewercamera.h" -#include "llviewergesture.h" -#include "llviewertexturelist.h" -#include "llviewerinventory.h" -#include "llviewerkeyboard.h" -#include "llviewermedia.h" -#include "llviewermediafocus.h" -#include "llviewermenu.h" -#include "llviewermessage.h" -#include "llviewerobjectlist.h" -#include "llviewerparcelmgr.h" -#include "llviewerregion.h" -#include "llviewershadermgr.h" -#include "llviewerstats.h" -#include "llvoavatarself.h" -#include "llvovolume.h" -#include "llworld.h" -#include "llworldmapview.h" -#include "pipeline.h" -#include "llappviewer.h" -#include "llviewerdisplay.h" -#include "llspatialpartition.h" -#include "llviewerjoystick.h" -#include "llviewernetwork.h" -#include "llpostprocess.h" -#include "llbottomtray.h" -#include "llnearbychatbar.h" -#include "llagentui.h" -#include "llwearablelist.h" - -#include "llnotifications.h" -#include "llnotificationsutil.h" -#include "llnotificationmanager.h" - -#include "llfloaternotificationsconsole.h" - -#include "llnearbychat.h" -#include "llviewerwindowlistener.h" -#include "llpaneltopinfobar.h" - -#if LL_WINDOWS -#include // For Unicode conversion methods -#endif - -// -// Globals -// -void render_ui(F32 zoom_factor = 1.f, int subfield = 0); - -extern BOOL gDebugClicks; -extern BOOL gDisplaySwapBuffers; -extern BOOL gDepthDirty; -extern BOOL gResizeScreenTexture; - -LLViewerWindow *gViewerWindow = NULL; - -LLFrameTimer gAwayTimer; -LLFrameTimer gAwayTriggerTimer; - -BOOL gShowOverlayTitle = FALSE; - -LLViewerObject* gDebugRaycastObject = NULL; -LLVector3 gDebugRaycastIntersection; -LLVector2 gDebugRaycastTexCoord; -LLVector3 gDebugRaycastNormal; -LLVector3 gDebugRaycastBinormal; -S32 gDebugRaycastFaceHit; - -// HUD display lines in lower right -BOOL gDisplayWindInfo = FALSE; -BOOL gDisplayCameraPos = FALSE; -BOOL gDisplayFOV = FALSE; -BOOL gDisplayBadge = FALSE; - -S32 CHAT_BAR_HEIGHT = 28; -S32 OVERLAY_BAR_HEIGHT = 20; - -const U8 NO_FACE = 255; -BOOL gQuietSnapshot = FALSE; - -const F32 MIN_AFK_TIME = 2.f; // minimum time after setting away state before coming back -const F32 MAX_FAST_FRAME_TIME = 0.5f; -const F32 FAST_FRAME_INCREMENT = 0.1f; - -const F32 MIN_DISPLAY_SCALE = 0.75f; - -std::string LLViewerWindow::sSnapshotBaseName; -std::string LLViewerWindow::sSnapshotDir; - -std::string LLViewerWindow::sMovieBaseName; - -class RecordToChatConsole : public LLError::Recorder, public LLSingleton -{ -public: - virtual void recordMessage(LLError::ELevel level, - const std::string& message) - { - //FIXME: this is NOT thread safe, and will do bad things when a warning is issued from a non-UI thread - - // only log warnings to chat console - //if (level == LLError::LEVEL_WARN) - //{ - //LLFloaterChat* chat_floater = LLFloaterReg::findTypedInstance("chat"); - //if (chat_floater && gSavedSettings.getBOOL("WarningsAsChat")) - //{ - // LLChat chat; - // chat.mText = message; - // chat.mSourceType = CHAT_SOURCE_SYSTEM; - - // chat_floater->addChat(chat, FALSE, FALSE); - //} - //} - } -}; - -//////////////////////////////////////////////////////////////////////////// -// -// LLDebugText -// - -class LLDebugText -{ -private: - struct Line - { - Line(const std::string& in_text, S32 in_x, S32 in_y) : text(in_text), x(in_x), y(in_y) {} - std::string text; - S32 x,y; - }; - - LLViewerWindow *mWindow; - - typedef std::vector line_list_t; - line_list_t mLineList; - LLColor4 mTextColor; - - void addText(S32 x, S32 y, const std::string &text) - { - mLineList.push_back(Line(text, x, y)); - } - - void clearText() { mLineList.clear(); } - -public: - LLDebugText(LLViewerWindow* window) : mWindow(window) {} - - void update() - { - static LLCachedControl log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; - - std::string wind_vel_text; - std::string wind_vector_text; - std::string rwind_vel_text; - std::string rwind_vector_text; - std::string audio_text; - - // Draw the statistics in a light gray - // and in a thin font - mTextColor = LLColor4( 0.86f, 0.86f, 0.86f, 1.f ); - - // Draw stuff growing up from right lower corner of screen - U32 xpos = mWindow->getWindowWidthScaled() - 350; - U32 ypos = 64; - const U32 y_inc = 20; - - clearText(); - - if (gSavedSettings.getBOOL("DebugShowTime")) - { - const U32 y_inc2 = 15; - for (std::map::reverse_iterator iter = gDebugTimers.rbegin(); - iter != gDebugTimers.rend(); ++iter) - { - S32 idx = iter->first; - LLFrameTimer& timer = iter->second; - F32 time = timer.getElapsedTimeF32(); - S32 hours = (S32)(time / (60*60)); - S32 mins = (S32)((time - hours*(60*60)) / 60); - S32 secs = (S32)((time - hours*(60*60) - mins*60)); - std::string label = gDebugTimerLabel[idx]; - if (label.empty()) label = llformat("Debug: %d", idx); - addText(xpos, ypos, llformat(" %s: %d:%02d:%02d", label.c_str(), hours,mins,secs)); ypos += y_inc2; - } - - F32 time = gFrameTimeSeconds; - S32 hours = (S32)(time / (60*60)); - S32 mins = (S32)((time - hours*(60*60)) / 60); - S32 secs = (S32)((time - hours*(60*60) - mins*60)); - addText(xpos, ypos, llformat("Time: %d:%02d:%02d", hours,mins,secs)); ypos += y_inc; - } - -#if LL_WINDOWS - if (gSavedSettings.getBOOL("DebugShowMemory")) - { - addText(xpos, ypos, llformat("Memory: %d (KB)", LLMemory::getWorkingSetSize() / 1024)); - ypos += y_inc; - } -#endif - - if (gDisplayCameraPos) - { - std::string camera_view_text; - std::string camera_center_text; - std::string agent_view_text; - std::string agent_left_text; - std::string agent_center_text; - std::string agent_root_center_text; - - LLVector3d tvector; // Temporary vector to hold data for printing. - - // Update camera center, camera view, wind info every other frame - tvector = gAgent.getPositionGlobal(); - agent_center_text = llformat("AgentCenter %f %f %f", - (F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ])); - - if (isAgentAvatarValid()) - { - tvector = gAgent.getPosGlobalFromAgent(gAgentAvatarp->mRoot.getWorldPosition()); - agent_root_center_text = llformat("AgentRootCenter %f %f %f", - (F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ])); - } - else - { - agent_root_center_text = "---"; - } - - - tvector = LLVector4(gAgent.getFrameAgent().getAtAxis()); - agent_view_text = llformat("AgentAtAxis %f %f %f", - (F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ])); - - tvector = LLVector4(gAgent.getFrameAgent().getLeftAxis()); - agent_left_text = llformat("AgentLeftAxis %f %f %f", - (F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ])); - - tvector = gAgentCamera.getCameraPositionGlobal(); - camera_center_text = llformat("CameraCenter %f %f %f", - (F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ])); - - tvector = LLVector4(LLViewerCamera::getInstance()->getAtAxis()); - camera_view_text = llformat("CameraAtAxis %f %f %f", - (F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ])); - - addText(xpos, ypos, agent_center_text); ypos += y_inc; - addText(xpos, ypos, agent_root_center_text); ypos += y_inc; - addText(xpos, ypos, agent_view_text); ypos += y_inc; - addText(xpos, ypos, agent_left_text); ypos += y_inc; - addText(xpos, ypos, camera_center_text); ypos += y_inc; - addText(xpos, ypos, camera_view_text); ypos += y_inc; - } - - if (gDisplayWindInfo) - { - wind_vel_text = llformat("Wind velocity %.2f m/s", gWindVec.magVec()); - wind_vector_text = llformat("Wind vector %.2f %.2f %.2f", gWindVec.mV[0], gWindVec.mV[1], gWindVec.mV[2]); - rwind_vel_text = llformat("RWind vel %.2f m/s", gRelativeWindVec.magVec()); - rwind_vector_text = llformat("RWind vec %.2f %.2f %.2f", gRelativeWindVec.mV[0], gRelativeWindVec.mV[1], gRelativeWindVec.mV[2]); - - addText(xpos, ypos, wind_vel_text); ypos += y_inc; - addText(xpos, ypos, wind_vector_text); ypos += y_inc; - addText(xpos, ypos, rwind_vel_text); ypos += y_inc; - addText(xpos, ypos, rwind_vector_text); ypos += y_inc; - } - if (gDisplayWindInfo) - { - if (gAudiop) - { - audio_text= llformat("Audio for wind: %d", gAudiop->isWindEnabled()); - } - addText(xpos, ypos, audio_text); ypos += y_inc; - } - if (gDisplayFOV) - { - addText(xpos, ypos, llformat("FOV: %2.1f deg", RAD_TO_DEG * LLViewerCamera::getInstance()->getView())); - ypos += y_inc; - } - if (gDisplayBadge) - { - addText(xpos, ypos+(y_inc/2), llformat("Hippos!", RAD_TO_DEG * LLViewerCamera::getInstance()->getView())); - ypos += y_inc * 2; - } - - /*if (LLViewerJoystick::getInstance()->getOverrideCamera()) - { - addText(xpos + 200, ypos, llformat("Flycam")); - ypos += y_inc; - }*/ - - if (gSavedSettings.getBOOL("DebugShowRenderInfo")) - { - if (gPipeline.getUseVertexShaders() == 0) - { - addText(xpos, ypos, "Shaders Disabled"); - ypos += y_inc; - } - addText(xpos, ypos, llformat("%d MB Vertex Data", LLVertexBuffer::sAllocatedBytes/(1024*1024))); - ypos += y_inc; - - addText(xpos, ypos, llformat("%d Vertex Buffers", LLVertexBuffer::sGLCount)); - ypos += y_inc; - - addText(xpos, ypos, llformat("%d Mapped Buffers", LLVertexBuffer::sMappedCount)); - ypos += y_inc; - - addText(xpos, ypos, llformat("%d Vertex Buffer Binds", LLVertexBuffer::sBindCount)); - ypos += y_inc; - - addText(xpos, ypos, llformat("%d Vertex Buffer Sets", LLVertexBuffer::sSetCount)); - ypos += y_inc; - - addText(xpos, ypos, llformat("%d Texture Binds", LLImageGL::sBindCount)); - ypos += y_inc; - - addText(xpos, ypos, llformat("%d Unique Textures", LLImageGL::sUniqueCount)); - ypos += y_inc; - - addText(xpos, ypos, llformat("%d Render Calls", gPipeline.mBatchCount)); - ypos += y_inc; - - addText(xpos, ypos, llformat("%d Matrix Ops", gPipeline.mMatrixOpCount)); - ypos += y_inc; - - addText(xpos, ypos, llformat("%d Texture Matrix Ops", gPipeline.mTextureMatrixOps)); - ypos += y_inc; - - gPipeline.mTextureMatrixOps = 0; - gPipeline.mMatrixOpCount = 0; - - if (gPipeline.mBatchCount > 0) - { - addText(xpos, ypos, llformat("Batch min/max/mean: %d/%d/%d", gPipeline.mMinBatchSize, gPipeline.mMaxBatchSize, - gPipeline.mTrianglesDrawn/gPipeline.mBatchCount)); - - gPipeline.mMinBatchSize = gPipeline.mMaxBatchSize; - gPipeline.mMaxBatchSize = 0; - gPipeline.mBatchCount = 0; - } - ypos += y_inc; - - addText(xpos, ypos, llformat("UI Verts/Calls: %d/%d", LLRender::sUIVerts, LLRender::sUICalls)); - LLRender::sUICalls = LLRender::sUIVerts = 0; - ypos += y_inc; - - addText(xpos,ypos, llformat("%d/%d Nodes visible", gPipeline.mNumVisibleNodes, LLSpatialGroup::sNodeCount)); - - ypos += y_inc; - - - addText(xpos,ypos, llformat("%d Avatars visible", LLVOAvatar::sNumVisibleAvatars)); - - ypos += y_inc; - - addText(xpos,ypos, llformat("%d Lights visible", LLPipeline::sVisibleLightCount)); - - ypos += y_inc; - - LLVertexBuffer::sBindCount = LLImageGL::sBindCount = - LLVertexBuffer::sSetCount = LLImageGL::sUniqueCount = - gPipeline.mNumVisibleNodes = LLPipeline::sVisibleLightCount = 0; - } - if (gSavedSettings.getBOOL("DebugShowRenderMatrices")) - { - addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLProjection[12], gGLProjection[13], gGLProjection[14], gGLProjection[15])); - ypos += y_inc; - - addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLProjection[8], gGLProjection[9], gGLProjection[10], gGLProjection[11])); - ypos += y_inc; - - addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLProjection[4], gGLProjection[5], gGLProjection[6], gGLProjection[7])); - ypos += y_inc; - - addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLProjection[0], gGLProjection[1], gGLProjection[2], gGLProjection[3])); - ypos += y_inc; - - addText(xpos, ypos, "Projection Matrix"); - ypos += y_inc; - - - addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLModelView[12], gGLModelView[13], gGLModelView[14], gGLModelView[15])); - ypos += y_inc; - - addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLModelView[8], gGLModelView[9], gGLModelView[10], gGLModelView[11])); - ypos += y_inc; - - addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLModelView[4], gGLModelView[5], gGLModelView[6], gGLModelView[7])); - ypos += y_inc; - - addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLModelView[0], gGLModelView[1], gGLModelView[2], gGLModelView[3])); - ypos += y_inc; - - addText(xpos, ypos, "View Matrix"); - ypos += y_inc; - } - if (gSavedSettings.getBOOL("DebugShowColor")) - { - U8 color[4]; - LLCoordGL coord = gViewerWindow->getCurrentMouse(); - glReadPixels(coord.mX, coord.mY, 1,1,GL_RGBA, GL_UNSIGNED_BYTE, color); - addText(xpos, ypos, llformat("%d %d %d %d", color[0], color[1], color[2], color[3])); - ypos += y_inc; - } - // only display these messages if we are actually rendering beacons at this moment - if (LLPipeline::getRenderBeacons(NULL) && LLFloaterReg::instanceVisible("beacons")) - { - if (LLPipeline::getRenderParticleBeacons(NULL)) - { - addText(xpos, ypos, "Viewing particle beacons (blue)"); - ypos += y_inc; - } - if (LLPipeline::toggleRenderTypeControlNegated((void*)LLPipeline::RENDER_TYPE_PARTICLES)) - { - addText(xpos, ypos, "Hiding particles"); - ypos += y_inc; - } - if (LLPipeline::getRenderPhysicalBeacons(NULL)) - { - addText(xpos, ypos, "Viewing physical object beacons (green)"); - ypos += y_inc; - } - if (LLPipeline::getRenderScriptedBeacons(NULL)) - { - addText(xpos, ypos, "Viewing scripted object beacons (red)"); - ypos += y_inc; - } - else - if (LLPipeline::getRenderScriptedTouchBeacons(NULL)) - { - addText(xpos, ypos, "Viewing scripted object with touch function beacons (red)"); - ypos += y_inc; - } - if (LLPipeline::getRenderSoundBeacons(NULL)) - { - addText(xpos, ypos, "Viewing sound beacons (yellow)"); - ypos += y_inc; - } - } - if(log_texture_traffic) - { - U32 old_y = ypos ; - for(S32 i = LLViewerTexture::BOOST_NONE; i < LLViewerTexture::MAX_GL_IMAGE_CATEGORY; i++) - { - if(gTotalTextureBytesPerBoostLevel[i] > 0) - { - addText(xpos, ypos, llformat("Boost_Level %d: %.3f MB", i, (F32)gTotalTextureBytesPerBoostLevel[i] / (1024 * 1024))); - ypos += y_inc; - } - } - if(ypos != old_y) - { - addText(xpos, ypos, "Network traffic for textures:"); - ypos += y_inc; - } - } - - if (gSavedSettings.getBOOL("DebugShowTextureInfo")) - { - LLViewerObject* objectp = NULL ; - //objectp = = gAgentCamera.getFocusObject(); - - LLSelectNode* nodep = LLSelectMgr::instance().getHoverNode(); - if (nodep) - { - objectp = nodep->getObject(); - } - if (objectp && !objectp->isDead()) - { - S32 num_faces = objectp->mDrawable->getNumFaces() ; - - for(S32 i = 0 ; i < num_faces; i++) - { - LLFace* facep = objectp->mDrawable->getFace(i) ; - if(facep) - { - //addText(xpos, ypos, llformat("ts_min: %.3f ts_max: %.3f tt_min: %.3f tt_max: %.3f", facep->mTexExtents[0].mV[0], facep->mTexExtents[1].mV[0], - // facep->mTexExtents[0].mV[1], facep->mTexExtents[1].mV[1])); - //ypos += y_inc; - - addText(xpos, ypos, llformat("v_size: %.3f: p_size: %.3f", facep->getVirtualSize(), facep->getPixelArea())); - ypos += y_inc; - - //const LLTextureEntry *tep = facep->getTextureEntry(); - //if(tep) - //{ - // addText(xpos, ypos, llformat("scale_s: %.3f: scale_t: %.3f", tep->mScaleS, tep->mScaleT)) ; - // ypos += y_inc; - //} - - LLViewerTexture* tex = facep->getTexture() ; - if(tex) - { - addText(xpos, ypos, llformat("ID: %s v_size: %.3f", tex->getID().asString().c_str(), tex->getMaxVirtualSize())); - ypos += y_inc; - } - } - } - } - } - } - - void draw() - { - for (line_list_t::iterator iter = mLineList.begin(); - iter != mLineList.end(); ++iter) - { - const Line& line = *iter; - LLFontGL::getFontMonospace()->renderUTF8(line.text, 0, (F32)line.x, (F32)line.y, mTextColor, - LLFontGL::LEFT, LLFontGL::TOP, - LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); - } - mLineList.clear(); - } - -}; - -void LLViewerWindow::updateDebugText() -{ - mDebugText->update(); -} - -//////////////////////////////////////////////////////////////////////////// -// -// LLViewerWindow -// - -BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) -{ - const char* buttonname = ""; - const char* buttonstatestr = ""; - S32 x = pos.mX; - S32 y = pos.mY; - x = llround((F32)x / mDisplayScale.mV[VX]); - y = llround((F32)y / mDisplayScale.mV[VY]); - - // only send mouse clicks to UI if UI is visible - if(gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) - { - - if (down) - { - buttonstatestr = "down" ; - } - else - { - buttonstatestr = "up" ; - } - - switch (clicktype) - { - case LLMouseHandler::CLICK_LEFT: - mLeftMouseDown = down; - buttonname = "Left"; - break; - case LLMouseHandler::CLICK_RIGHT: - mRightMouseDown = down; - buttonname = "Right"; - break; - case LLMouseHandler::CLICK_MIDDLE: - mMiddleMouseDown = down; - buttonname = "Middle"; - break; - case LLMouseHandler::CLICK_DOUBLELEFT: - mLeftMouseDown = down; - buttonname = "Left Double Click"; - break; - } - - LLView::sMouseHandlerMessage.clear(); - - if (gMenuBarView) - { - // stop ALT-key access to menu - gMenuBarView->resetMenuTrigger(); - } - - if (gDebugClicks) - { - llinfos << "ViewerWindow " << buttonname << " mouse " << buttonstatestr << " at " << x << "," << y << llendl; - } - - // Make sure we get a corresponding mouseup event, even if the mouse leaves the window - if (down) - mWindow->captureMouse(); - else - mWindow->releaseMouse(); - - // Indicate mouse was active - LLUI::resetMouseIdleTimer(); - - // Don't let the user move the mouse out of the window until mouse up. - if( LLToolMgr::getInstance()->getCurrentTool()->clipMouseWhenDown() ) - { - mWindow->setMouseClipping(down); - } - - LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); - if( mouse_captor ) - { - S32 local_x; - S32 local_y; - mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); - if (LLView::sDebugMouseHandling) - { - llinfos << buttonname << " Mouse " << buttonstatestr << " handled by captor " << mouse_captor->getName() << llendl; - } - return mouse_captor->handleAnyMouseClick(local_x, local_y, mask, clicktype, down); - } - - // Topmost view gets a chance before the hierarchy - //LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); - //if (top_ctrl) - //{ - // S32 local_x, local_y; - // top_ctrl->screenPointToLocal( x, y, &local_x, &local_y ); - // if (top_ctrl->pointInView(local_x, local_y)) - // { - // return top_ctrl->handleAnyMouseClick(local_x, local_y, mask, clicktype, down) ; - // } - // else - // { - // if (down) - // { - // gFocusMgr.setTopCtrl(NULL); - // } - // } - //} - - // Give the UI views a chance to process the click - if( mRootView->handleAnyMouseClick(x, y, mask, clicktype, down) ) - { - if (LLView::sDebugMouseHandling) - { - llinfos << buttonname << " Mouse " << buttonstatestr << " " << LLView::sMouseHandlerMessage << llendl; - } - return TRUE; - } - else if (LLView::sDebugMouseHandling) - { - llinfos << buttonname << " Mouse " << buttonstatestr << " not handled by view" << llendl; - } - } - - // Do not allow tool manager to handle mouseclicks if we have disconnected - if(!gDisconnected && LLToolMgr::getInstance()->getCurrentTool()->handleAnyMouseClick( x, y, mask, clicktype, down ) ) - { - return TRUE; - } - - - // If we got this far on a down-click, it wasn't handled. - // Up-clicks, though, are always handled as far as the OS is concerned. - BOOL default_rtn = !down; - return default_rtn; -} - -BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) -{ - BOOL down = TRUE; - return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down); -} - -BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask) -{ - // try handling as a double-click first, then a single-click if that - // wasn't handled. - BOOL down = TRUE; - if (handleAnyMouseClick(window, pos, mask, - LLMouseHandler::CLICK_DOUBLELEFT, down)) - { - return TRUE; - } - return handleMouseDown(window, pos, mask); -} - -BOOL LLViewerWindow::handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) -{ - BOOL down = FALSE; - return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down); -} - - -BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) -{ - S32 x = pos.mX; - S32 y = pos.mY; - x = llround((F32)x / mDisplayScale.mV[VX]); - y = llround((F32)y / mDisplayScale.mV[VY]); - - BOOL down = TRUE; - BOOL handle = handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down); - if (handle) - return handle; - - // *HACK: this should be rolled into the composite tool logic, not - // hardcoded at the top level. - if (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgentCamera.getCameraMode() && LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance()) - { - // If the current tool didn't process the click, we should show - // the pie menu. This can be done by passing the event to the pie - // menu tool. - LLToolPie::getInstance()->handleRightMouseDown(x, y, mask); - // show_context_menu( x, y, mask ); - } - - return TRUE; -} - -BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) -{ - BOOL down = FALSE; - return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down); -} - -BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) -{ - BOOL down = TRUE; - LLVoiceClient::getInstance()->middleMouseState(true); - handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); - - // Always handled as far as the OS is concerned. - return TRUE; -} - -LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data) -{ - LLWindowCallbacks::DragNDropResult result = LLWindowCallbacks::DND_NONE; - - const bool prim_media_dnd_enabled = gSavedSettings.getBOOL("PrimMediaDragNDrop"); - const bool slurl_dnd_enabled = gSavedSettings.getBOOL("SLURLDragNDrop"); - - if ( prim_media_dnd_enabled || slurl_dnd_enabled ) - { - switch(action) - { - // Much of the handling for these two cases is the same. - case LLWindowCallbacks::DNDA_TRACK: - case LLWindowCallbacks::DNDA_DROPPED: - case LLWindowCallbacks::DNDA_START_TRACKING: - { - bool drop = (LLWindowCallbacks::DNDA_DROPPED == action); - - if (slurl_dnd_enabled) - { - LLSLURL dropped_slurl(data); - if(dropped_slurl.isSpatial()) - { - if (drop) - { - LLURLDispatcher::dispatch( dropped_slurl.getSLURLString(), NULL, true ); - return LLWindowCallbacks::DND_MOVE; - } - return LLWindowCallbacks::DND_COPY; - } - } - - if (prim_media_dnd_enabled) - { - LLPickInfo pick_info = pickImmediate( pos.mX, pos.mY, TRUE /*BOOL pick_transparent*/ ); - - LLUUID object_id = pick_info.getObjectID(); - S32 object_face = pick_info.mObjectFace; - std::string url = data; - - lldebugs << "Object: picked at " << pos.mX << ", " << pos.mY << " - face = " << object_face << " - URL = " << url << llendl; - - LLVOVolume *obj = dynamic_cast(static_cast(pick_info.getObject())); - - if (obj && !obj->getRegion()->getCapability("ObjectMedia").empty()) - { - LLTextureEntry *te = obj->getTE(object_face); - - // can modify URL if we can modify the object or we have navigate permissions - bool allow_modify_url = obj->permModify() || obj->hasMediaPermission( te->getMediaData(), LLVOVolume::MEDIA_PERM_INTERACT ); - - if (te && allow_modify_url ) - { - if (drop) - { - // object does NOT have media already - if ( ! te->hasMedia() ) - { - // we are allowed to modify the object - if ( obj->permModify() ) - { - // Create new media entry - LLSD media_data; - // XXX Should we really do Home URL too? - media_data[LLMediaEntry::HOME_URL_KEY] = url; - media_data[LLMediaEntry::CURRENT_URL_KEY] = url; - media_data[LLMediaEntry::AUTO_PLAY_KEY] = true; - obj->syncMediaData(object_face, media_data, true, true); - // XXX This shouldn't be necessary, should it ?!? - if (obj->getMediaImpl(object_face)) - obj->getMediaImpl(object_face)->navigateReload(); - obj->sendMediaDataUpdate(); - - result = LLWindowCallbacks::DND_COPY; - } - } - else - // object HAS media already - { - // URL passes the whitelist - if (te->getMediaData()->checkCandidateUrl( url ) ) - { - // just navigate to the URL - if (obj->getMediaImpl(object_face)) - { - obj->getMediaImpl(object_face)->navigateTo(url); - } - else - { - // This is very strange. Navigation should - // happen via the Impl, but we don't have one. - // This sends it to the server, which /should/ - // trigger us getting it. Hopefully. - LLSD media_data; - media_data[LLMediaEntry::CURRENT_URL_KEY] = url; - obj->syncMediaData(object_face, media_data, true, true); - obj->sendMediaDataUpdate(); - } - result = LLWindowCallbacks::DND_LINK; - - } - } - LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); - mDragHoveredObject = NULL; - - } - else - { - // Check the whitelist, if there's media (otherwise just show it) - if (te->getMediaData() == NULL || te->getMediaData()->checkCandidateUrl(url)) - { - if ( obj != mDragHoveredObject) - { - // Highlight the dragged object - LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); - mDragHoveredObject = obj; - LLSelectMgr::getInstance()->highlightObjectOnly(mDragHoveredObject); - } - result = (! te->hasMedia()) ? LLWindowCallbacks::DND_COPY : LLWindowCallbacks::DND_LINK; - - } - } - } - } - } - } - break; - - case LLWindowCallbacks::DNDA_STOP_TRACKING: - // The cleanup case below will make sure things are unhilighted if necessary. - break; - } - - if (prim_media_dnd_enabled && - result == LLWindowCallbacks::DND_NONE && !mDragHoveredObject.isNull()) - { - LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); - mDragHoveredObject = NULL; - } - } - - return result; -} - -BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) -{ - BOOL down = FALSE; - LLVoiceClient::getInstance()->middleMouseState(false); - handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); - - // Always handled as far as the OS is concerned. - return TRUE; -} - -// WARNING: this is potentially called multiple times per frame -void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask) -{ - S32 x = pos.mX; - S32 y = pos.mY; - - x = llround((F32)x / mDisplayScale.mV[VX]); - y = llround((F32)y / mDisplayScale.mV[VY]); - - mMouseInWindow = TRUE; - - // Save mouse point for access during idle() and display() - - LLCoordGL mouse_point(x, y); - - if (mouse_point != mCurrentMousePoint) - { - LLUI::resetMouseIdleTimer(); - } - - saveLastMouse(mouse_point); - - mWindow->showCursorFromMouseMove(); - - if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME) - { - gAgent.clearAFK(); - } -} - -void LLViewerWindow::handleMouseLeave(LLWindow *window) -{ - // Note: we won't get this if we have captured the mouse. - llassert( gFocusMgr.getMouseCapture() == NULL ); - mMouseInWindow = FALSE; - LLToolTipMgr::instance().blockToolTips(); -} - -BOOL LLViewerWindow::handleCloseRequest(LLWindow *window) -{ - // User has indicated they want to close, but we may need to ask - // about modified documents. - LLAppViewer::instance()->userQuit(); - // Don't quit immediately - return FALSE; -} - -void LLViewerWindow::handleQuit(LLWindow *window) -{ - LLAppViewer::instance()->forceQuit(); -} - -void LLViewerWindow::handleResize(LLWindow *window, S32 width, S32 height) -{ - reshape(width, height); - mResDirty = true; -} - -// The top-level window has gained focus (e.g. via ALT-TAB) -void LLViewerWindow::handleFocus(LLWindow *window) -{ - gFocusMgr.setAppHasFocus(TRUE); - LLModalDialog::onAppFocusGained(); - - gAgent.onAppFocusGained(); - LLToolMgr::getInstance()->onAppFocusGained(); - - // See if we're coming in with modifier keys held down - if (gKeyboard) - { - gKeyboard->resetMaskKeys(); - } - - // resume foreground running timer - // since we artifically limit framerate when not frontmost - gForegroundTime.unpause(); -} - -// The top-level window has lost focus (e.g. via ALT-TAB) -void LLViewerWindow::handleFocusLost(LLWindow *window) -{ - gFocusMgr.setAppHasFocus(FALSE); - //LLModalDialog::onAppFocusLost(); - LLToolMgr::getInstance()->onAppFocusLost(); - gFocusMgr.setMouseCapture( NULL ); - - if (gMenuBarView) - { - // stop ALT-key access to menu - gMenuBarView->resetMenuTrigger(); - } - - // restore mouse cursor - showCursor(); - getWindow()->setMouseClipping(FALSE); - - // If losing focus while keys are down, reset them. - if (gKeyboard) - { - gKeyboard->resetKeys(); - } - - // pause timer that tracks total foreground running time - gForegroundTime.pause(); -} - - -BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) -{ - // Let the voice chat code check for its PTT key. Note that this never affects event processing. - LLVoiceClient::getInstance()->keyDown(key, mask); - - if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME) - { - gAgent.clearAFK(); - } - - // *NOTE: We want to interpret KEY_RETURN later when it arrives as - // a Unicode char, not as a keydown. Otherwise when client frame - // rate is really low, hitting return sends your chat text before - // it's all entered/processed. - if (key == KEY_RETURN && mask == MASK_NONE) - { - return FALSE; - } - - return gViewerKeyboard.handleKey(key, mask, repeated); -} - -BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask) -{ - // Let the voice chat code check for its PTT key. Note that this never affects event processing. - LLVoiceClient::getInstance()->keyUp(key, mask); - - return FALSE; -} - - -void LLViewerWindow::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) -{ - LLViewerJoystick::getInstance()->setCameraNeedsUpdate(true); - return gViewerKeyboard.scanKey(key, key_down, key_up, key_level); -} - - - - -BOOL LLViewerWindow::handleActivate(LLWindow *window, BOOL activated) -{ - if (activated) - { - mActive = TRUE; - send_agent_resume(); - gAgent.clearAFK(); - - // Unmute audio - audio_update_volume(); - } - else - { - mActive = FALSE; - - if (gSavedSettings.getS32("AFKTimeout")) - { - gAgent.setAFK(); - } - - // SL-53351: Make sure we're not in mouselook when minimised, to prevent control issues - if (gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK) - { - gAgentCamera.changeCameraToDefault(); - } - - send_agent_pause(); - - // Mute audio - audio_update_volume(); - } - return TRUE; -} - -BOOL LLViewerWindow::handleActivateApp(LLWindow *window, BOOL activating) -{ - //if (!activating) gAgentCamera.changeCameraToDefault(); - - LLViewerJoystick::getInstance()->setNeedsReset(true); - return FALSE; -} - - -void LLViewerWindow::handleMenuSelect(LLWindow *window, S32 menu_item) -{ -} - - -BOOL LLViewerWindow::handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S32 height) -{ -#if LL_WINDOWS - if (gNoRender) - { - HWND window_handle = (HWND)window->getPlatformWindow(); - PAINTSTRUCT ps; - HDC hdc; - - RECT wnd_rect; - wnd_rect.left = 0; - wnd_rect.top = 0; - wnd_rect.bottom = 200; - wnd_rect.right = 500; - - hdc = BeginPaint(window_handle, &ps); - //SetBKColor(hdc, RGB(255, 255, 255)); - FillRect(hdc, &wnd_rect, CreateSolidBrush(RGB(255, 255, 255))); - - std::string temp_str; - temp_str = llformat( "FPS %3.1f Phy FPS %2.1f Time Dil %1.3f", /* Flawfinder: ignore */ - LLViewerStats::getInstance()->mFPSStat.getMeanPerSec(), - LLViewerStats::getInstance()->mSimPhysicsFPS.getPrev(0), - LLViewerStats::getInstance()->mSimTimeDilation.getPrev(0)); - S32 len = temp_str.length(); - TextOutA(hdc, 0, 0, temp_str.c_str(), len); - - - LLVector3d pos_global = gAgent.getPositionGlobal(); - temp_str = llformat( "Avatar pos %6.1lf %6.1lf %6.1lf", pos_global.mdV[0], pos_global.mdV[1], pos_global.mdV[2]); - len = temp_str.length(); - TextOutA(hdc, 0, 25, temp_str.c_str(), len); - - TextOutA(hdc, 0, 50, "Set \"DisableRendering FALSE\" in settings.ini file to reenable", 61); - EndPaint(window_handle, &ps); - return TRUE; - } -#endif - return FALSE; -} - - -void LLViewerWindow::handleScrollWheel(LLWindow *window, S32 clicks) -{ - handleScrollWheel( clicks ); -} - -void LLViewerWindow::handleWindowBlock(LLWindow *window) -{ - send_agent_pause(); -} - -void LLViewerWindow::handleWindowUnblock(LLWindow *window) -{ - send_agent_resume(); -} - -void LLViewerWindow::handleDataCopy(LLWindow *window, S32 data_type, void *data) -{ - const S32 SLURL_MESSAGE_TYPE = 0; - switch (data_type) - { - case SLURL_MESSAGE_TYPE: - // received URL - std::string url = (const char*)data; - LLMediaCtrl* web = NULL; - const bool trusted_browser = false; - if (LLURLDispatcher::dispatch(url, web, trusted_browser)) - { - // bring window to foreground, as it has just been "launched" from a URL - mWindow->bringToFront(); - } - break; - } -} - -BOOL LLViewerWindow::handleTimerEvent(LLWindow *window) -{ - if (LLViewerJoystick::getInstance()->getOverrideCamera()) - { - LLViewerJoystick::getInstance()->updateStatus(); - return TRUE; - } - return FALSE; -} - -BOOL LLViewerWindow::handleDeviceChange(LLWindow *window) -{ - // give a chance to use a joystick after startup (hot-plugging) - if (!LLViewerJoystick::getInstance()->isJoystickInitialized() ) - { - LLViewerJoystick::getInstance()->init(true); - return TRUE; - } - return FALSE; -} - -void LLViewerWindow::handlePingWatchdog(LLWindow *window, const char * msg) -{ - LLAppViewer::instance()->pingMainloopTimeout(msg); -} - - -void LLViewerWindow::handleResumeWatchdog(LLWindow *window) -{ - LLAppViewer::instance()->resumeMainloopTimeout(); -} - -void LLViewerWindow::handlePauseWatchdog(LLWindow *window) -{ - LLAppViewer::instance()->pauseMainloopTimeout(); -} - -//virtual -std::string LLViewerWindow::translateString(const char* tag) -{ - return LLTrans::getString( std::string(tag) ); -} - -//virtual -std::string LLViewerWindow::translateString(const char* tag, - const std::map& args) -{ - // LLTrans uses a special subclass of std::string for format maps, - // but we must use std::map<> in these callbacks, otherwise we create - // a dependency between LLWindow and LLFormatMapString. So copy the data. - LLStringUtil::format_map_t args_copy; - std::map::const_iterator it = args.begin(); - for ( ; it != args.end(); ++it) - { - args_copy[it->first] = it->second; - } - return LLTrans::getString( std::string(tag), args_copy); -} - -// -// Classes -// -LLViewerWindow::LLViewerWindow( - const std::string& title, const std::string& name, - S32 x, S32 y, - S32 width, S32 height, - BOOL fullscreen, BOOL ignore_pixel_depth) // fullscreen is no longer used - : - mWindow(NULL), - mActive(TRUE), - mWindowRectRaw(0, height, width, 0), - mWindowRectScaled(0, height, width, 0), - mWorldViewRectRaw(0, height, width, 0), - mLeftMouseDown(FALSE), - mMiddleMouseDown(FALSE), - mRightMouseDown(FALSE), - mMouseInWindow( FALSE ), - mLastMask( MASK_NONE ), - mToolStored( NULL ), - mHideCursorPermanent( FALSE ), - mCursorHidden(FALSE), - mIgnoreActivate( FALSE ), - mResDirty(false), - mStatesDirty(false), - mCurrResolutionIndex(0), - mViewerWindowListener(new LLViewerWindowListener(this)), - mProgressView(NULL) -{ - LLNotificationChannel::buildChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "alert")); - LLNotificationChannel::buildChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "alertmodal")); - - LLNotifications::instance().getChannel("VW_alerts")->connectChanged(&LLViewerWindow::onAlert); - LLNotifications::instance().getChannel("VW_alertmodal")->connectChanged(&LLViewerWindow::onAlert); - LLNotifications::instance().setIgnoreAllNotifications(gSavedSettings.getBOOL("IgnoreAllNotifications")); - llinfos << "NOTE: ALL NOTIFICATIONS THAT OCCUR WILL GET ADDED TO IGNORE LIST FOR LATER RUNS." << llendl; - - // Default to application directory. - LLViewerWindow::sSnapshotBaseName = "Snapshot"; - LLViewerWindow::sMovieBaseName = "SLmovie"; - resetSnapshotLoc(); - - // create window - mWindow = LLWindowManager::createWindow(this, - title, name, x, y, width, height, 0, - fullscreen, - gNoRender, - gSavedSettings.getBOOL("DisableVerticalSync"), - !gNoRender, - ignore_pixel_depth, - gSavedSettings.getBOOL("RenderUseFBO") ? 0 : gSavedSettings.getU32("RenderFSAASamples")); //don't use window level anti-aliasing if FBOs are enabled - - if (!LLAppViewer::instance()->restoreErrorTrap()) - { - LL_WARNS("Window") << " Someone took over my signal/exception handler (post createWindow)!" << LL_ENDL; - } - - LLCoordScreen scr; - mWindow->getSize(&scr); - - if(fullscreen && ( scr.mX!=width || scr.mY!=height)) - { - llwarns << "Fullscreen has forced us in to a different resolution now using "<Display->Settings" - << LL_ENDL; -#endif - LLAppViewer::instance()->fastQuit(1); - } - - // Get the real window rect the window was created with (since there are various OS-dependent reasons why - // the size of a window or fullscreen context may have been adjusted slightly...) - F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor"); - - mDisplayScale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f)); - mDisplayScale *= ui_scale_factor; - LLUI::setScaleFactor(mDisplayScale); - - { - LLCoordWindow size; - mWindow->getSize(&size); - mWindowRectRaw.set(0, size.mY, size.mX, 0); - mWindowRectScaled.set(0, llround((F32)size.mY / mDisplayScale.mV[VY]), llround((F32)size.mX / mDisplayScale.mV[VX]), 0); - } - - LLFontManager::initClass(); - - // - // We want to set this stuff up BEFORE we initialize the pipeline, so we can turn off - // stuff like AGP if we think that it'll crash the viewer. - // - LL_DEBUGS("Window") << "Loading feature tables." << LL_ENDL; - - LLFeatureManager::getInstance()->init(); - - // Initialize OpenGL Renderer - if (!LLFeatureManager::getInstance()->isFeatureAvailable("RenderVBOEnable") || - !gGLManager.mHasVertexBufferObject) - { - gSavedSettings.setBOOL("RenderVBOEnable", FALSE); - } - LLVertexBuffer::initClass(gSavedSettings.getBOOL("RenderVBOEnable")); - - if (LLFeatureManager::getInstance()->isSafe() - || (gSavedSettings.getS32("LastFeatureVersion") != LLFeatureManager::getInstance()->getVersion()) - || (gSavedSettings.getS32("LastGPUClass") != LLFeatureManager::getInstance()->getGPUClass()) - || (gSavedSettings.getBOOL("ProbeHardwareOnStartup"))) - { - LLFeatureManager::getInstance()->applyRecommendedSettings(); - gSavedSettings.setBOOL("ProbeHardwareOnStartup", FALSE); - } - - if (!gGLManager.mHasDepthClamp) - { - LL_INFOS("RenderInit") << "Missing feature GL_ARB_depth_clamp. Void water might disappear in rare cases." << LL_ENDL; - } - - // If we crashed while initializng GL stuff last time, disable certain features - if (gSavedSettings.getBOOL("RenderInitError")) - { - mInitAlert = "DisplaySettingsNoShaders"; - LLFeatureManager::getInstance()->setGraphicsLevel(0, false); - gSavedSettings.setU32("RenderQualityPerformance", 0); - } - - // Init the image list. Must happen after GL is initialized and before the images that - // LLViewerWindow needs are requested. - LLImageGL::initClass(LLViewerTexture::MAX_GL_IMAGE_CATEGORY) ; - gTextureList.init(); - LLViewerTextureManager::init() ; - gBumpImageList.init(); - - // Init font system, but don't actually load the fonts yet - // because our window isn't onscreen and they take several - // seconds to parse. - LLFontGL::initClass( gSavedSettings.getF32("FontScreenDPI"), - mDisplayScale.mV[VX], - mDisplayScale.mV[VY], - gDirUtilp->getAppRODataDir(), - LLUI::getXUIPaths()); - - // Create container for all sub-views - LLView::Params rvp; - rvp.name("root"); - rvp.rect(mWindowRectScaled); - rvp.mouse_opaque(false); - rvp.follows.flags(FOLLOWS_NONE); - mRootView = LLUICtrlFactory::create(rvp); - LLUI::setRootView(mRootView); - - // Make avatar head look forward at start - mCurrentMousePoint.mX = getWindowWidthScaled() / 2; - mCurrentMousePoint.mY = getWindowHeightScaled() / 2; - - gShowOverlayTitle = gSavedSettings.getBOOL("ShowOverlayTitle"); - mOverlayTitle = gSavedSettings.getString("OverlayTitle"); - // Can't have spaces in settings.ini strings, so use underscores instead and convert them. - LLStringUtil::replaceChar(mOverlayTitle, '_', ' '); - - // sync the keyboard's setting with the saved setting - gSavedSettings.getControl("NumpadControl")->firePropertyChanged(); - - mDebugText = new LLDebugText(this); - - mWorldViewRectScaled = calcScaledRect(mWorldViewRectRaw, mDisplayScale); -} - -void LLViewerWindow::initGLDefaults() -{ - gGL.setSceneBlendType(LLRender::BT_ALPHA); - glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ); - - F32 ambient[4] = {0.f,0.f,0.f,0.f }; - F32 diffuse[4] = {1.f,1.f,1.f,1.f }; - glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambient); - glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuse); - - glPixelStorei(GL_PACK_ALIGNMENT,1); - glPixelStorei(GL_UNPACK_ALIGNMENT,1); - - gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); - - // lights for objects - glShadeModel( GL_SMOOTH ); - - glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); - - gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); - - glCullFace(GL_BACK); - - // RN: Need this for translation and stretch manip. - gCone.prerender(); - gBox.prerender(); - gSphere.prerender(); - gCylinder.prerender(); -} - -struct MainPanel : public LLPanel -{ -}; - -void LLViewerWindow::initBase() -{ - S32 height = getWindowHeightScaled(); - S32 width = getWindowWidthScaled(); - - LLRect full_window(0, height, width, 0); - - //////////////////// - // - // Set the gamma - // - - F32 gamma = gSavedSettings.getF32("RenderGamma"); - if (gamma != 0.0f) - { - getWindow()->setGamma(gamma); - } - - // Create global views - - // Create the floater view at the start so that other views can add children to it. - // (But wait to add it as a child of the root view so that it will be in front of the - // other views.) - MainPanel* main_view = new MainPanel(); - main_view->buildFromFile("main_view.xml"); - main_view->setShape(full_window); - getRootView()->addChild(main_view); - - // placeholder widget that controls where "world" is rendered - mWorldViewPlaceholder = main_view->getChildView("world_view_rect")->getHandle(); - mNonSideTrayView = main_view->getChildView("non_side_tray_view")->getHandle(); - mFloaterViewHolder = main_view->getChildView("floater_view_holder")->getHandle(); - mPopupView = main_view->getChild("popup_holder"); - mHintHolder = main_view->getChild("hint_holder")->getHandle(); - mLoginPanelHolder = main_view->getChild("login_panel_holder")->getHandle(); - - // Constrain floaters to inside the menu and status bar regions. - gFloaterView = main_view->getChild("Floater View"); - gFloaterView->setFloaterSnapView(main_view->getChild("floater_snap_region")->getHandle()); - gSnapshotFloaterView = main_view->getChild("Snapshot Floater View"); - - - // Console - llassert( !gConsole ); - LLConsole::Params cp; - cp.name("console"); - cp.max_lines(gSavedSettings.getS32("ConsoleBufferSize")); - cp.rect(getChatConsoleRect()); - cp.persist_time(gSavedSettings.getF32("ChatPersistTime")); - cp.font_size_index(gSavedSettings.getS32("ChatFontSize")); - cp.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM); - gConsole = LLUICtrlFactory::create(cp); - getRootView()->addChild(gConsole); - - // optionally forward warnings to chat console/chat floater - // for qa runs and dev builds -#if !LL_RELEASE_FOR_DOWNLOAD - LLError::addRecorder(RecordToChatConsole::getInstance()); -#else - if(gSavedSettings.getBOOL("QAMode")) - { - LLError::addRecorder(RecordToChatConsole::getInstance()); - } -#endif - - gDebugView = getRootView()->getChild("DebugView"); - gDebugView->init(); - gToolTipView = getRootView()->getChild("tooltip view"); - - // Initialize busy response message when logged in - LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLFloaterPreference::initBusyResponse)); - - // Add the progress bar view (startup view), which overrides everything - mProgressView = getRootView()->findChild("progress_view"); - setShowProgress(FALSE); - setProgressCancelButtonVisible(FALSE); - - gMenuHolder = getRootView()->getChild("Menu Holder"); - - LLMenuGL::sMenuContainer = gMenuHolder; - -} - -void LLViewerWindow::initWorldUI() -{ - S32 height = mRootView->getRect().getHeight(); - S32 width = mRootView->getRect().getWidth(); - LLRect full_window(0, height, width, 0); - - - gIMMgr = LLIMMgr::getInstance(); - - //getRootView()->sendChildToFront(gFloaterView); - //getRootView()->sendChildToFront(gSnapshotFloaterView); - - // new bottom panel - LLPanel* bottom_tray_container = getRootView()->getChild("bottom_tray_container"); - LLBottomTray* bottom_tray = LLBottomTray::getInstance(); - bottom_tray->setShape(bottom_tray_container->getLocalRect()); - bottom_tray->setFollowsAll(); - bottom_tray_container->addChild(bottom_tray); - bottom_tray_container->setVisible(TRUE); - - LLRect morph_view_rect = full_window; - morph_view_rect.stretch( -STATUS_BAR_HEIGHT ); - morph_view_rect.mTop = full_window.mTop - 32; - LLMorphView::Params mvp; - mvp.name("MorphView"); - mvp.rect(morph_view_rect); - mvp.visible(false); - gMorphView = LLUICtrlFactory::create(mvp); - getRootView()->addChild(gMorphView); - - LLWorldMapView::initClass(); - - // Force gFloaterWorldMap to initialize - LLFloaterReg::getInstance("world_map"); - - // Force gFloaterTools to initialize - LLFloaterReg::getInstance("build"); - LLFloaterReg::hideInstance("build"); - - // Status bar - LLPanel* status_bar_container = getRootView()->getChild("status_bar_container"); - gStatusBar = new LLStatusBar(status_bar_container->getLocalRect()); - gStatusBar->setFollowsAll(); - gStatusBar->setShape(status_bar_container->getLocalRect()); - // sync bg color with menu bar - gStatusBar->setBackgroundColor( gMenuBarView->getBackgroundColor().get() ); - status_bar_container->addChild(gStatusBar); - status_bar_container->setVisible(TRUE); - - // Navigation bar - LLPanel* nav_bar_container = getRootView()->getChild("nav_bar_container"); - - LLNavigationBar* navbar = LLNavigationBar::getInstance(); - navbar->setShape(nav_bar_container->getLocalRect()); - navbar->setBackgroundColor(gMenuBarView->getBackgroundColor().get()); - nav_bar_container->addChild(navbar); - nav_bar_container->setVisible(TRUE); - - if (!gSavedSettings.getBOOL("ShowNavbarNavigationPanel")) - { - navbar->showNavigationPanel(FALSE); - } - - if (!gSavedSettings.getBOOL("ShowNavbarFavoritesPanel")) - { - navbar->showFavoritesPanel(FALSE); - } - - // Top Info bar - LLPanel* topinfo_bar_container = getRootView()->getChild("topinfo_bar_container"); - LLPanelTopInfoBar* topinfo_bar = LLPanelTopInfoBar::getInstance(); - - topinfo_bar->setShape(topinfo_bar_container->getLocalRect()); - - topinfo_bar_container->addChild(topinfo_bar); - topinfo_bar_container->setVisible(TRUE); - - if (!gSavedSettings.getBOOL("ShowMiniLocationPanel")) - { - topinfo_bar->setVisible(FALSE); - } - - if ( gHUDView == NULL ) - { - LLRect hud_rect = full_window; - hud_rect.mBottom += 50; - if (gMenuBarView && gMenuBarView->isInVisibleChain()) - { - hud_rect.mTop -= gMenuBarView->getRect().getHeight(); - } - gHUDView = new LLHUDView(hud_rect); - // put behind everything else in the UI - getRootView()->addChildInBack(gHUDView); - } - - LLPanel* panel_ssf_container = getRootView()->getChild("stand_stop_flying_container"); - LLPanelStandStopFlying* panel_stand_stop_flying = LLPanelStandStopFlying::getInstance(); - panel_ssf_container->addChild(panel_stand_stop_flying); - panel_ssf_container->setVisible(TRUE); - - // put sidetray in container - LLPanel* side_tray_container = getRootView()->getChild("side_tray_container"); - LLSideTray* sidetrayp = LLSideTray::getInstance(); - sidetrayp->setShape(side_tray_container->getLocalRect()); - // don't follow right edge to avoid spurious resizes, since we are using a fixed width layout - sidetrayp->setFollows(FOLLOWS_LEFT|FOLLOWS_TOP|FOLLOWS_BOTTOM); - side_tray_container->addChild(sidetrayp); - side_tray_container->setVisible(FALSE); - - // put sidetray buttons in their own panel - LLPanel* buttons_panel = sidetrayp->getButtonsPanel(); - LLPanel* buttons_panel_container = getRootView()->getChild("side_bar_tabs"); - buttons_panel->setShape(buttons_panel_container->getLocalRect()); - buttons_panel->setFollowsAll(); - buttons_panel_container->addChild(buttons_panel); - - LLView* avatar_picker_destination_guide_container = gViewerWindow->getRootView()->getChild("avatar_picker_and_destination_guide_container"); - avatar_picker_destination_guide_container->getChild("close")->setCommitCallback(boost::bind(toggle_destination_and_avatar_picker, LLSD())); - LLMediaCtrl* destinations = avatar_picker_destination_guide_container->findChild("destination_guide_contents"); - LLMediaCtrl* avatar_picker = avatar_picker_destination_guide_container->findChild("avatar_picker_contents"); - if (destinations) - { - destinations->navigateTo(gSavedSettings.getString("DestinationGuideURL"), "text/html"); - } - - if (avatar_picker) - { - avatar_picker->navigateTo(gSavedSettings.getString("AvatarPickerURL"), "text/html"); - } - -} - -// Destroy the UI -void LLViewerWindow::shutdownViews() -{ - // clean up warning logger - LLError::removeRecorder(RecordToChatConsole::getInstance()); - - delete mDebugText; - mDebugText = NULL; - - // Cleanup global views - if (gMorphView) - { - gMorphView->setVisible(FALSE); - } - - // DEV-40930: Clear sModalStack. Otherwise, any LLModalDialog left open - // will crump with LL_ERRS. - LLModalDialog::shutdownModals(); - - // destroy the nav bar, not currently part of gViewerWindow - // *TODO: Make LLNavigationBar part of gViewerWindow - delete LLNavigationBar::getInstance(); - - // destroy menus after instantiating navbar above, as it needs - // access to gMenuHolder - cleanup_menus(); - - // Delete all child views. - delete mRootView; - mRootView = NULL; - - // Automatically deleted as children of mRootView. Fix the globals. - gStatusBar = NULL; - gIMMgr = NULL; - gToolTipView = NULL; - - gFloaterView = NULL; - gMorphView = NULL; - - gHUDView = NULL; -} - -void LLViewerWindow::shutdownGL() -{ - //-------------------------------------------------------- - // Shutdown GL cleanly. Order is very important here. - //-------------------------------------------------------- - LLFontGL::destroyDefaultFonts(); - LLFontManager::cleanupClass(); - stop_glerror(); - - gSky.cleanup(); - stop_glerror(); - - LLWearableList::instance().cleanup() ; - - gTextureList.shutdown(); - stop_glerror(); - - gBumpImageList.shutdown(); - stop_glerror(); - - LLWorldMapView::cleanupTextures(); - - llinfos << "Cleaning up pipeline" << llendl; - gPipeline.cleanup(); - stop_glerror(); - - LLViewerTextureManager::cleanup() ; - LLImageGL::cleanupClass() ; - - llinfos << "All textures and llimagegl images are destroyed!" << llendl ; - - llinfos << "Cleaning up select manager" << llendl; - LLSelectMgr::getInstance()->cleanup(); - - LLVertexBuffer::cleanupClass(); - - llinfos << "Stopping GL during shutdown" << llendl; - if (!gNoRender) - { - stopGL(FALSE); - stop_glerror(); - } - - gGL.shutdown(); -} - -// shutdownViews() and shutdownGL() need to be called first -LLViewerWindow::~LLViewerWindow() -{ - llinfos << "Destroying Window" << llendl; - destroyWindow(); - - delete mDebugText; - mDebugText = NULL; -} - - -void LLViewerWindow::setCursor( ECursorType c ) -{ - mWindow->setCursor( c ); -} - -void LLViewerWindow::showCursor() -{ - mWindow->showCursor(); - - mCursorHidden = FALSE; -} - -void LLViewerWindow::hideCursor() -{ - // And hide the cursor - mWindow->hideCursor(); - - mCursorHidden = TRUE; -} - -void LLViewerWindow::sendShapeToSim() -{ - LLMessageSystem* msg = gMessageSystem; - if(!msg) return; - msg->newMessageFast(_PREHASH_AgentHeightWidth); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addU32Fast(_PREHASH_CircuitCode, gMessageSystem->mOurCircuitCode); - msg->nextBlockFast(_PREHASH_HeightWidthBlock); - msg->addU32Fast(_PREHASH_GenCounter, 0); - U16 height16 = (U16) mWorldViewRectRaw.getHeight(); - U16 width16 = (U16) mWorldViewRectRaw.getWidth(); - msg->addU16Fast(_PREHASH_Height, height16); - msg->addU16Fast(_PREHASH_Width, width16); - gAgent.sendReliableMessage(); -} - -// Must be called after window is created to set up agent -// camera variables and UI variables. -void LLViewerWindow::reshape(S32 width, S32 height) -{ - // Destroying the window at quit time generates spurious - // reshape messages. We don't care about these, and we - // don't want to send messages because the message system - // may have been destructed. - if (!LLApp::isExiting()) - { - if (gNoRender) - { - return; - } - - gWindowResized = TRUE; - - // update our window rectangle - mWindowRectRaw.mRight = mWindowRectRaw.mLeft + width; - mWindowRectRaw.mTop = mWindowRectRaw.mBottom + height; - - //glViewport(0, 0, width, height ); - - if (height > 0) - { - LLViewerCamera::getInstance()->setViewHeightInPixels( mWorldViewRectRaw.getHeight() ); - LLViewerCamera::getInstance()->setAspect( getWorldViewAspectRatio() ); - } - - calcDisplayScale(); - - BOOL display_scale_changed = mDisplayScale != LLUI::sGLScaleFactor; - LLUI::setScaleFactor(mDisplayScale); - - // update our window rectangle - mWindowRectScaled.mRight = mWindowRectScaled.mLeft + llround((F32)width / mDisplayScale.mV[VX]); - mWindowRectScaled.mTop = mWindowRectScaled.mBottom + llround((F32)height / mDisplayScale.mV[VY]); - - setup2DViewport(); - - // Inform lower views of the change - // round up when converting coordinates to make sure there are no gaps at edge of window - LLView::sForceReshape = display_scale_changed; - mRootView->reshape(llceil((F32)width / mDisplayScale.mV[VX]), llceil((F32)height / mDisplayScale.mV[VY])); - LLView::sForceReshape = FALSE; - - // clear font width caches - if (display_scale_changed) - { - LLHUDObject::reshapeAll(); - } - - sendShapeToSim(); - - // store new settings for the mode we are in, regardless - // Only save size if not maximized - BOOL maximized = mWindow->getMaximized(); - gSavedSettings.setBOOL("WindowMaximized", maximized); - - LLCoordScreen window_size; - if (!maximized - && mWindow->getSize(&window_size)) - { - gSavedSettings.setS32("WindowWidth", window_size.mX); - gSavedSettings.setS32("WindowHeight", window_size.mY); - } - - LLViewerStats::getInstance()->setStat(LLViewerStats::ST_WINDOW_WIDTH, (F64)width); - LLViewerStats::getInstance()->setStat(LLViewerStats::ST_WINDOW_HEIGHT, (F64)height); - } -} - - -// Hide normal UI when a logon fails -void LLViewerWindow::setNormalControlsVisible( BOOL visible ) -{ - if(LLBottomTray::instanceExists()) - { - LLBottomTray::getInstance()->setVisible(visible); - LLBottomTray::getInstance()->setEnabled(visible); - } - - if ( gMenuBarView ) - { - gMenuBarView->setVisible( visible ); - gMenuBarView->setEnabled( visible ); - - // ...and set the menu color appropriately. - setMenuBackgroundColor(gAgent.getGodLevel() > GOD_NOT, - LLGridManager::getInstance()->isInProductionGrid()); - } - - if ( gStatusBar ) - { - gStatusBar->setVisible( visible ); - gStatusBar->setEnabled( visible ); - } - - LLNavigationBar* navbarp = LLUI::getRootView()->findChild("navigation_bar"); - if (navbarp) - { - navbarp->setVisible( visible ); - } -} - -void LLViewerWindow::setMenuBackgroundColor(bool god_mode, bool dev_grid) -{ - LLSD args; - LLColor4 new_bg_color; - - // no l10n problem because channel is always an english string - std::string channel = LLVersionInfo::getChannel(); - bool isProject = (channel.find("Project") != std::string::npos); - - // god more important than project, proj more important than grid - if(god_mode && LLGridManager::getInstance()->isInProductionGrid()) - { - new_bg_color = LLUIColorTable::instance().getColor( "MenuBarGodBgColor" ); - } - else if(god_mode && !LLGridManager::getInstance()->isInProductionGrid()) - { - new_bg_color = LLUIColorTable::instance().getColor( "MenuNonProductionGodBgColor" ); - } - else if (!god_mode && isProject) - { - new_bg_color = LLUIColorTable::instance().getColor( "MenuBarProjectBgColor" ); - } - else if(!god_mode && !LLGridManager::getInstance()->isInProductionGrid()) - { - new_bg_color = LLUIColorTable::instance().getColor( "MenuNonProductionBgColor" ); - } - else - { - new_bg_color = LLUIColorTable::instance().getColor( "MenuBarBgColor" ); - } - - if(gMenuBarView) - { - gMenuBarView->setBackgroundColor( new_bg_color ); - } - - if(gStatusBar) - { - gStatusBar->setBackgroundColor( new_bg_color ); - } -} - -void LLViewerWindow::drawDebugText() -{ - gGL.color4f(1,1,1,1); - gGL.pushMatrix(); - gGL.pushUIMatrix(); - { - // scale view by UI global scale factor and aspect ratio correction factor - gGL.scaleUI(mDisplayScale.mV[VX], mDisplayScale.mV[VY], 1.f); - mDebugText->draw(); - } - gGL.popUIMatrix(); - gGL.popMatrix(); - - gGL.flush(); -} - -void LLViewerWindow::draw() -{ - -//#if LL_DEBUG - LLView::sIsDrawing = TRUE; -//#endif - stop_glerror(); - - LLUI::setLineWidth(1.f); - - LLUI::setLineWidth(1.f); - // Reset any left-over transforms - glMatrixMode(GL_MODELVIEW); - - glLoadIdentity(); - - //S32 screen_x, screen_y; - - if (!gSavedSettings.getBOOL("RenderUIBuffer")) - { - LLUI::sDirtyRect = getWindowRectScaled(); - } - - // HACK for timecode debugging - if (gSavedSettings.getBOOL("DisplayTimecode")) - { - // draw timecode block - std::string text; - - glLoadIdentity(); - - microsecondsToTimecodeString(gFrameTime,text); - const LLFontGL* font = LLFontGL::getFontSansSerif(); - font->renderUTF8(text, 0, - llround((getWindowWidthScaled()/2)-100.f), - llround((getWindowHeightScaled()-60.f)), - LLColor4( 1.f, 1.f, 1.f, 1.f ), - LLFontGL::LEFT, LLFontGL::TOP); - } - - // Draw all nested UI views. - // No translation needed, this view is glued to 0,0 - - gGL.pushMatrix(); - LLUI::pushMatrix(); - { - - // scale view by UI global scale factor and aspect ratio correction factor - gGL.scaleUI(mDisplayScale.mV[VX], mDisplayScale.mV[VY], 1.f); - - LLVector2 old_scale_factor = LLUI::sGLScaleFactor; - // apply camera zoom transform (for high res screenshots) - F32 zoom_factor = LLViewerCamera::getInstance()->getZoomFactor(); - S16 sub_region = LLViewerCamera::getInstance()->getZoomSubRegion(); - if (zoom_factor > 1.f) - { - //decompose subregion number to x and y values - int pos_y = sub_region / llceil(zoom_factor); - int pos_x = sub_region - (pos_y*llceil(zoom_factor)); - // offset for this tile - glTranslatef((F32)getWindowWidthScaled() * -(F32)pos_x, - (F32)getWindowHeightScaled() * -(F32)pos_y, - 0.f); - glScalef(zoom_factor, zoom_factor, 1.f); - LLUI::sGLScaleFactor *= zoom_factor; - } - - // Draw tool specific overlay on world - LLToolMgr::getInstance()->getCurrentTool()->draw(); - - if( gAgentCamera.cameraMouselook() || LLFloaterCamera::inFreeCameraMode() ) - { - drawMouselookInstructions(); - stop_glerror(); - } - - // Draw all nested UI views. - // No translation needed, this view is glued to 0,0 - mRootView->draw(); - - if (LLView::sDebugRects) - { - gToolTipView->drawStickyRect(); - } - - // Draw optional on-top-of-everyone view - LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); - if (top_ctrl && top_ctrl->getVisible()) - { - S32 screen_x, screen_y; - top_ctrl->localPointToScreen(0, 0, &screen_x, &screen_y); - - glMatrixMode(GL_MODELVIEW); - LLUI::pushMatrix(); - LLUI::translate( (F32) screen_x, (F32) screen_y, 0.f); - top_ctrl->draw(); - LLUI::popMatrix(); - } - - - if( gShowOverlayTitle && !mOverlayTitle.empty() ) - { - // Used for special titles such as "Second Life - Special E3 2003 Beta" - const S32 DIST_FROM_TOP = 20; - LLFontGL::getFontSansSerifBig()->renderUTF8( - mOverlayTitle, 0, - llround( getWindowWidthScaled() * 0.5f), - getWindowHeightScaled() - DIST_FROM_TOP, - LLColor4(1, 1, 1, 0.4f), - LLFontGL::HCENTER, LLFontGL::TOP); - } - - LLUI::sGLScaleFactor = old_scale_factor; - } - LLUI::popMatrix(); - gGL.popMatrix(); - -//#if LL_DEBUG - LLView::sIsDrawing = FALSE; -//#endif -} - -// Takes a single keydown event, usually when UI is visible -BOOL LLViewerWindow::handleKey(KEY key, MASK mask) -{ - // hide tooltips on keypress - LLToolTipMgr::instance().blockToolTips(); - - if (gFocusMgr.getKeyboardFocus() - && !(mask & (MASK_CONTROL | MASK_ALT)) - && !gFocusMgr.getKeystrokesOnly()) - { - // We have keyboard focus, and it's not an accelerator - if (key < 0x80) - { - // Not a special key, so likely (we hope) to generate a character. Let it fall through to character handler first. - return (gFocusMgr.getKeyboardFocus() != NULL); - } - } - - // let menus handle navigation keys for navigation - if ((gMenuBarView && gMenuBarView->handleKey(key, mask, TRUE)) - ||(gLoginMenuBarView && gLoginMenuBarView->handleKey(key, mask, TRUE)) - ||(gMenuHolder && gMenuHolder->handleKey(key, mask, TRUE))) - { - return TRUE; - } - - LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); - - // give menus a chance to handle modified (Ctrl, Alt) shortcut keys before current focus - // as long as focus isn't locked - if (mask & (MASK_CONTROL | MASK_ALT) && !gFocusMgr.focusLocked()) - { - // Check the current floater's menu first, if it has one. - if (gFocusMgr.keyboardFocusHasAccelerators() - && keyboard_focus - && keyboard_focus->handleKey(key,mask,FALSE)) - { - return TRUE; - } - - if ((gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask)) - ||(gLoginMenuBarView && gLoginMenuBarView->handleAcceleratorKey(key, mask))) - { - return TRUE; - } - } - - // give floaters first chance to handle TAB key - // so frontmost floater gets focus - // if nothing has focus, go to first or last UI element as appropriate - if (key == KEY_TAB && (mask & MASK_CONTROL || gFocusMgr.getKeyboardFocus() == NULL)) - { - if (gMenuHolder) gMenuHolder->hideMenus(); - - // if CTRL-tabbing (and not just TAB with no focus), go into window cycle mode - gFloaterView->setCycleMode((mask & MASK_CONTROL) != 0); - - // do CTRL-TAB and CTRL-SHIFT-TAB logic - if (mask & MASK_SHIFT) - { - mRootView->focusPrevRoot(); - } - else - { - mRootView->focusNextRoot(); - } - return TRUE; - } - // hidden edit menu for cut/copy/paste - if (gEditMenu && gEditMenu->handleAcceleratorKey(key, mask)) - { - return TRUE; - } - - // Traverses up the hierarchy - if( keyboard_focus ) - { - LLLineEditor* chat_editor = LLBottomTray::instanceExists() ? LLBottomTray::getInstance()->getNearbyChatBar()->getChatBox() : NULL; - // arrow keys move avatar while chatting hack - if (chat_editor && chat_editor->hasFocus()) - { - // If text field is empty, there's no point in trying to move - // cursor with arrow keys, so allow movement - if (chat_editor->getText().empty() - || gSavedSettings.getBOOL("ArrowKeysAlwaysMove")) - { - // let Control-Up and Control-Down through for chat line history, - if (!(key == KEY_UP && mask == MASK_CONTROL) - && !(key == KEY_DOWN && mask == MASK_CONTROL)) - { - switch(key) - { - case KEY_LEFT: - case KEY_RIGHT: - case KEY_UP: - case KEY_DOWN: - case KEY_PAGE_UP: - case KEY_PAGE_DOWN: - case KEY_HOME: - // when chatbar is empty or ArrowKeysAlwaysMove set, - // pass arrow keys on to avatar... - return FALSE; - default: - break; - } - } - } - } - - if (keyboard_focus->handleKey(key, mask, FALSE)) - { - return TRUE; - } - } - - if( LLToolMgr::getInstance()->getCurrentTool()->handleKey(key, mask) ) - { - return TRUE; - } - - // Try for a new-format gesture - if (LLGestureMgr::instance().triggerGesture(key, mask)) - { - return TRUE; - } - - // See if this is a gesture trigger. If so, eat the key and - // don't pass it down to the menus. - if (gGestureList.trigger(key, mask)) - { - return TRUE; - } - - // If "Pressing letter keys starts local chat" option is selected, we are not in mouselook, - // no view has keyboard focus, this is a printable character key (and no modifier key is - // pressed except shift), then give focus to nearby chat (STORM-560) - if ( gSavedSettings.getS32("LetterKeysFocusChatBar") && !gAgentCamera.cameraMouselook() && - !keyboard_focus && key < 0x80 && (mask == MASK_NONE || mask == MASK_SHIFT) ) - { - LLLineEditor* chat_editor = LLBottomTray::instanceExists() ? LLBottomTray::getInstance()->getNearbyChatBar()->getChatBox() : NULL; - if (chat_editor) - { - // passing NULL here, character will be added later when it is handled by character handler. - LLBottomTray::getInstance()->getNearbyChatBar()->startChat(NULL); - return TRUE; - } - } - - // give menus a chance to handle unmodified accelerator keys - if ((gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask)) - ||(gLoginMenuBarView && gLoginMenuBarView->handleAcceleratorKey(key, mask))) - { - return TRUE; - } - - // don't pass keys on to world when something in ui has focus - return gFocusMgr.childHasKeyboardFocus(mRootView) - || LLMenuGL::getKeyboardMode() - || (gMenuBarView && gMenuBarView->getHighlightedItem() && gMenuBarView->getHighlightedItem()->isActive()); -} - - -BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask) -{ - // HACK: We delay processing of return keys until they arrive as a Unicode char, - // so that if you're typing chat text at low frame rate, we don't send the chat - // until all keystrokes have been entered. JC - // HACK: Numeric keypad on Mac is Unicode 3 - // HACK: Control-M on Windows is Unicode 13 - if ((uni_char == 13 && mask != MASK_CONTROL) - || (uni_char == 3 && mask == MASK_NONE)) - { - return gViewerKeyboard.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN)); - } - - // let menus handle navigation (jump) keys - if (gMenuBarView && gMenuBarView->handleUnicodeChar(uni_char, TRUE)) - { - return TRUE; - } - - // Traverses up the hierarchy - LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); - if( keyboard_focus ) - { - if (keyboard_focus->handleUnicodeChar(uni_char, FALSE)) - { - return TRUE; - } - - //// Topmost view gets a chance before the hierarchy - //LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); - //if (top_ctrl && top_ctrl->handleUnicodeChar( uni_char, FALSE ) ) - //{ - // return TRUE; - //} - - return TRUE; - } - - return FALSE; -} - - -void LLViewerWindow::handleScrollWheel(S32 clicks) -{ - LLView::sMouseHandlerMessage.clear(); - - LLUI::resetMouseIdleTimer(); - - LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); - if( mouse_captor ) - { - S32 local_x; - S32 local_y; - mouse_captor->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y ); - mouse_captor->handleScrollWheel(local_x, local_y, clicks); - if (LLView::sDebugMouseHandling) - { - llinfos << "Scroll Wheel handled by captor " << mouse_captor->getName() << llendl; - } - return; - } - - LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); - if (top_ctrl) - { - S32 local_x; - S32 local_y; - top_ctrl->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y ); - if (top_ctrl->handleScrollWheel(local_x, local_y, clicks)) return; - } - - if (mRootView->handleScrollWheel(mCurrentMousePoint.mX, mCurrentMousePoint.mY, clicks) ) - { - if (LLView::sDebugMouseHandling) - { - llinfos << "Scroll Wheel" << LLView::sMouseHandlerMessage << llendl; - } - return; - } - else if (LLView::sDebugMouseHandling) - { - llinfos << "Scroll Wheel not handled by view" << llendl; - } - - // Zoom the camera in and out behavior - - if(top_ctrl == 0 - && getWorldViewRectScaled().pointInRect(mCurrentMousePoint.mX, mCurrentMousePoint.mY) - && gAgentCamera.isInitialized()) - gAgentCamera.handleScrollWheel(clicks); - - return; -} - -void LLViewerWindow::addPopup(LLView* popup) -{ - if (mPopupView) - { - mPopupView->addPopup(popup); - } -} - -void LLViewerWindow::removePopup(LLView* popup) -{ - if (mPopupView) - { - mPopupView->removePopup(popup); - } -} - -void LLViewerWindow::clearPopups() -{ - if (mPopupView) - { - mPopupView->clearPopups(); - } -} - -void LLViewerWindow::moveCursorToCenter() -{ - if (! gSavedSettings.getBOOL("DisableMouseWarp")) - { - S32 x = getWorldViewWidthScaled() / 2; - S32 y = getWorldViewHeightScaled() / 2; - - //on a forced move, all deltas get zeroed out to prevent jumping - mCurrentMousePoint.set(x,y); - mLastMousePoint.set(x,y); - mCurrentMouseDelta.set(0,0); - - LLUI::setMousePositionScreen(x, y); - } -} - - -////////////////////////////////////////////////////////////////////// -// -// Hover handlers -// - -void append_xui_tooltip(LLView* viewp, LLToolTip::Params& params) -{ - if (viewp) - { - if (!params.styled_message.empty()) - { - params.styled_message.add().text("\n---------\n"); - } - LLView::root_to_view_iterator_t end_tooltip_it = viewp->endRootToView(); - // NOTE: we skip "root" since it is assumed - for (LLView::root_to_view_iterator_t tooltip_it = ++viewp->beginRootToView(); - tooltip_it != end_tooltip_it; - ++tooltip_it) - { - LLView* viewp = *tooltip_it; - - params.styled_message.add().text(viewp->getName()); - - LLPanel* panelp = dynamic_cast(viewp); - if (panelp && !panelp->getXMLFilename().empty()) - { - params.styled_message.add() - .text("(" + panelp->getXMLFilename() + ")") - .style.color(LLColor4(0.7f, 0.7f, 1.f, 1.f)); - } - params.styled_message.add().text("/"); - } - } -} - -// Update UI based on stored mouse position from mouse-move -// event processing. -void LLViewerWindow::updateUI() -{ - static LLFastTimer::DeclareTimer ftm("Update UI"); - LLFastTimer t(ftm); - - static std::string last_handle_msg; - - if (gLoggedInTime.getStarted()) - { - if (gLoggedInTime.getElapsedTimeF32() > gSavedSettings.getF32("DestinationGuideHintTimeout")) - { - LLFirstUse::notUsingDestinationGuide(); - } - if (gLoggedInTime.getElapsedTimeF32() > gSavedSettings.getF32("SidePanelHintTimeout")) - { - LLFirstUse::notUsingSidePanel(); - } - } - - LLConsole::updateClass(); - - // animate layout stacks so we have up to date rect for world view - LLLayoutStack::updateClass(); - - // use full window for world view when not rendering UI - bool world_view_uses_full_window = gAgentCamera.cameraMouselook() || !gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI); - updateWorldViewRect(world_view_uses_full_window); - - LLView::sMouseHandlerMessage.clear(); - - S32 x = mCurrentMousePoint.mX; - S32 y = mCurrentMousePoint.mY; - MASK mask = gKeyboard->currentMask(TRUE); - - if (gNoRender) - { - return; - } - - if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST)) - { - gDebugRaycastFaceHit = -1; - gDebugRaycastObject = cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE, - &gDebugRaycastFaceHit, - &gDebugRaycastIntersection, - &gDebugRaycastTexCoord, - &gDebugRaycastNormal, - &gDebugRaycastBinormal); - } - - updateMouseDelta(); - updateKeyboardFocus(); - - BOOL handled = FALSE; - - BOOL handled_by_top_ctrl = FALSE; - LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); - LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); - LLView* captor_view = dynamic_cast(mouse_captor); - - //FIXME: only include captor and captor's ancestors if mouse is truly over them --RN - - //build set of views containing mouse cursor by traversing UI hierarchy and testing - //screen rect against mouse cursor - view_handle_set_t mouse_hover_set; - - // constraint mouse enter events to children of mouse captor - LLView* root_view = captor_view; - - // if mouse captor doesn't exist or isn't a LLView - // then allow mouse enter events on entire UI hierarchy - if (!root_view) - { - root_view = mRootView; - } - - // only update mouse hover set when UI is visible (since we shouldn't send hover events to invisible UI - if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) - { - // include all ancestors of captor_view as automatically having mouse - if (captor_view) - { - LLView* captor_parent_view = captor_view->getParent(); - while(captor_parent_view) - { - mouse_hover_set.insert(captor_parent_view->getHandle()); - captor_parent_view = captor_parent_view->getParent(); - } - } - - // aggregate visible views that contain mouse cursor in display order - LLPopupView::popup_list_t popups = mPopupView->getCurrentPopups(); - - for(LLPopupView::popup_list_t::iterator popup_it = popups.begin(); popup_it != popups.end(); ++popup_it) - { - LLView* popup = popup_it->get(); - if (popup && popup->calcScreenBoundingRect().pointInRect(x, y)) - { - // iterator over contents of top_ctrl, and throw into mouse_hover_set - for (LLView::tree_iterator_t it = popup->beginTreeDFS(); - it != popup->endTreeDFS(); - ++it) - { - LLView* viewp = *it; - if (viewp->getVisible() - && viewp->calcScreenBoundingRect().pointInRect(x, y)) - { - // we have a view that contains the mouse, add it to the set - mouse_hover_set.insert(viewp->getHandle()); - } - else - { - // skip this view and all of its children - it.skipDescendants(); - } - } - } - } - - // while the top_ctrl contains the mouse cursor, only it and its descendants will receive onMouseEnter events - if (top_ctrl && top_ctrl->calcScreenBoundingRect().pointInRect(x, y)) - { - // iterator over contents of top_ctrl, and throw into mouse_hover_set - for (LLView::tree_iterator_t it = top_ctrl->beginTreeDFS(); - it != top_ctrl->endTreeDFS(); - ++it) - { - LLView* viewp = *it; - if (viewp->getVisible() - && viewp->calcScreenBoundingRect().pointInRect(x, y)) - { - // we have a view that contains the mouse, add it to the set - mouse_hover_set.insert(viewp->getHandle()); - } - else - { - // skip this view and all of its children - it.skipDescendants(); - } - } - } - else - { - // walk UI tree in depth-first order - for (LLView::tree_iterator_t it = root_view->beginTreeDFS(); - it != root_view->endTreeDFS(); - ++it) - { - LLView* viewp = *it; - // calculating the screen rect involves traversing the parent, so this is less than optimal - if (viewp->getVisible() - && viewp->calcScreenBoundingRect().pointInRect(x, y)) - { - - // if this view is mouse opaque, nothing behind it should be in mouse_hover_set - if (viewp->getMouseOpaque()) - { - // constrain further iteration to children of this widget - it = viewp->beginTreeDFS(); - } - - // we have a view that contains the mouse, add it to the set - mouse_hover_set.insert(viewp->getHandle()); - } - else - { - // skip this view and all of its children - it.skipDescendants(); - } - } - } - } - - typedef std::vector > view_handle_list_t; - - // call onMouseEnter() on all views which contain the mouse cursor but did not before - view_handle_list_t mouse_enter_views; - std::set_difference(mouse_hover_set.begin(), mouse_hover_set.end(), - mMouseHoverViews.begin(), mMouseHoverViews.end(), - std::back_inserter(mouse_enter_views)); - for (view_handle_list_t::iterator it = mouse_enter_views.begin(); - it != mouse_enter_views.end(); - ++it) - { - LLView* viewp = it->get(); - if (viewp) - { - LLRect view_screen_rect = viewp->calcScreenRect(); - viewp->onMouseEnter(x - view_screen_rect.mLeft, y - view_screen_rect.mBottom, mask); - } - } - - // call onMouseLeave() on all views which no longer contain the mouse cursor - view_handle_list_t mouse_leave_views; - std::set_difference(mMouseHoverViews.begin(), mMouseHoverViews.end(), - mouse_hover_set.begin(), mouse_hover_set.end(), - std::back_inserter(mouse_leave_views)); - for (view_handle_list_t::iterator it = mouse_leave_views.begin(); - it != mouse_leave_views.end(); - ++it) - { - LLView* viewp = it->get(); - if (viewp) - { - LLRect view_screen_rect = viewp->calcScreenRect(); - viewp->onMouseLeave(x - view_screen_rect.mLeft, y - view_screen_rect.mBottom, mask); - } - } - - // store resulting hover set for next frame - swap(mMouseHoverViews, mouse_hover_set); - - // only handle hover events when UI is enabled - if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) - { - - if( mouse_captor ) - { - // Pass hover events to object capturing mouse events. - S32 local_x; - S32 local_y; - mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); - handled = mouse_captor->handleHover(local_x, local_y, mask); - if (LLView::sDebugMouseHandling) - { - llinfos << "Hover handled by captor " << mouse_captor->getName() << llendl; - } - - if( !handled ) - { - lldebugst(LLERR_USER_INPUT) << "hover not handled by mouse captor" << llendl; - } - } - else - { - if (top_ctrl) - { - S32 local_x, local_y; - top_ctrl->screenPointToLocal( x, y, &local_x, &local_y ); - handled = top_ctrl->pointInView(local_x, local_y) && top_ctrl->handleHover(local_x, local_y, mask); - handled_by_top_ctrl = TRUE; - } - - if ( !handled ) - { - // x and y are from last time mouse was in window - // mMouseInWindow tracks *actual* mouse location - if (mMouseInWindow && mRootView->handleHover(x, y, mask) ) - { - if (LLView::sDebugMouseHandling && LLView::sMouseHandlerMessage != last_handle_msg) - { - last_handle_msg = LLView::sMouseHandlerMessage; - llinfos << "Hover" << LLView::sMouseHandlerMessage << llendl; - } - handled = TRUE; - } - else if (LLView::sDebugMouseHandling) - { - if (last_handle_msg != LLStringUtil::null) - { - last_handle_msg.clear(); - llinfos << "Hover not handled by view" << llendl; - } - } - } - - if (!handled) - { - LLTool *tool = LLToolMgr::getInstance()->getCurrentTool(); - - if(mMouseInWindow && tool) - { - handled = tool->handleHover(x, y, mask); - } - } - } - - // Show a new tool tip (or update one that is already shown) - BOOL tool_tip_handled = FALSE; - std::string tool_tip_msg; - if( handled - && !mWindow->isCursorHidden()) - { - LLRect screen_sticky_rect = mRootView->getLocalRect(); - S32 local_x, local_y; - - if (gSavedSettings.getBOOL("DebugShowXUINames")) - { - LLToolTip::Params params; - - LLView* tooltip_view = mRootView; - LLView::tree_iterator_t end_it = mRootView->endTreeDFS(); - for (LLView::tree_iterator_t it = mRootView->beginTreeDFS(); it != end_it; ++it) - { - LLView* viewp = *it; - LLRect screen_rect; - viewp->localRectToScreen(viewp->getLocalRect(), &screen_rect); - if (!(viewp->getVisible() - && screen_rect.pointInRect(x, y))) - { - it.skipDescendants(); - } - // only report xui names for LLUICtrls, - // and blacklist the various containers we don't care about - else if (dynamic_cast(viewp) - && viewp != gMenuHolder - && viewp != gFloaterView - && viewp != gConsole) - { - if (dynamic_cast(viewp)) - { - // constrain search to descendants of this (frontmost) floater - // by resetting iterator - it = viewp->beginTreeDFS(); - } - - // if we are in a new part of the tree (not a descendent of current tooltip_view) - // then push the results for tooltip_view and start with a new potential view - // NOTE: this emulates visiting only the leaf nodes that meet our criteria - if (!viewp->hasAncestor(tooltip_view)) - { - append_xui_tooltip(tooltip_view, params); - screen_sticky_rect.intersectWith(tooltip_view->calcScreenRect()); - } - tooltip_view = viewp; - } - } - - append_xui_tooltip(tooltip_view, params); - screen_sticky_rect.intersectWith(tooltip_view->calcScreenRect()); - - params.sticky_rect = screen_sticky_rect; - params.max_width = 400; - - LLToolTipMgr::instance().show(params); - } - // if there is a mouse captor, nothing else gets a tooltip - else if (mouse_captor) - { - mouse_captor->screenPointToLocal(x, y, &local_x, &local_y); - tool_tip_handled = mouse_captor->handleToolTip(local_x, local_y, mask); - } - else - { - // next is top_ctrl - if (!tool_tip_handled && top_ctrl) - { - top_ctrl->screenPointToLocal(x, y, &local_x, &local_y); - tool_tip_handled = top_ctrl->handleToolTip(local_x, local_y, mask ); - } - - if (!tool_tip_handled) - { - local_x = x; local_y = y; - tool_tip_handled = mRootView->handleToolTip(local_x, local_y, mask ); - } - - LLTool* current_tool = LLToolMgr::getInstance()->getCurrentTool(); - if (!tool_tip_handled && current_tool) - { - current_tool->screenPointToLocal(x, y, &local_x, &local_y); - tool_tip_handled = current_tool->handleToolTip(local_x, local_y, mask ); - } - } - } - } - else - { // just have tools handle hover when UI is turned off - LLTool *tool = LLToolMgr::getInstance()->getCurrentTool(); - - if(mMouseInWindow && tool) - { - handled = tool->handleHover(x, y, mask); - } - } - - updateLayout(); - - mLastMousePoint = mCurrentMousePoint; - - // cleanup unused selections when no modal dialogs are open - if (LLModalDialog::activeCount() == 0) - { - LLViewerParcelMgr::getInstance()->deselectUnused(); - } - - if (LLModalDialog::activeCount() == 0) - { - LLSelectMgr::getInstance()->deselectUnused(); - } -} - - -void LLViewerWindow::updateLayout() -{ - LLTool* tool = LLToolMgr::getInstance()->getCurrentTool(); - if (gFloaterTools != NULL - && tool != NULL - && tool != gToolNull - && tool != LLToolCompInspect::getInstance() - && tool != LLToolDragAndDrop::getInstance() - && !gSavedSettings.getBOOL("FreezeTime")) - { - // Suppress the toolbox view if our source tool was the pie tool, - // and we've overridden to something else. - bool suppress_toolbox = - (LLToolMgr::getInstance()->getBaseTool() == LLToolPie::getInstance()) && - (LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance()); - - LLMouseHandler *captor = gFocusMgr.getMouseCapture(); - // With the null, inspect, or drag and drop tool, don't muck - // with visibility. - - if (gFloaterTools->isMinimized() - || (tool != LLToolPie::getInstance() // not default tool - && tool != LLToolCompGun::getInstance() // not coming out of mouselook - && !suppress_toolbox // not override in third person - && LLToolMgr::getInstance()->getCurrentToolset() != gFaceEditToolset // not special mode - && LLToolMgr::getInstance()->getCurrentToolset() != gMouselookToolset - && (!captor || dynamic_cast(captor) != NULL))) // not dragging - { - // Force floater tools to be visible (unless minimized) - if (!gFloaterTools->getVisible()) - { - gFloaterTools->openFloater(); - } - // Update the location of the blue box tool popup - LLCoordGL select_center_screen; - gFloaterTools->updatePopup( select_center_screen, gKeyboard->currentMask(TRUE) ); - } - else - { - gFloaterTools->setVisible(FALSE); - } - //gMenuBarView->setItemVisible("BuildTools", gFloaterTools->getVisible()); - } - - // Always update console - if(gConsole) - { - LLRect console_rect = getChatConsoleRect(); - gConsole->reshape(console_rect.getWidth(), console_rect.getHeight()); - gConsole->setRect(console_rect); - } -} - -void LLViewerWindow::updateMouseDelta() -{ - S32 dx = lltrunc((F32) (mCurrentMousePoint.mX - mLastMousePoint.mX) * LLUI::sGLScaleFactor.mV[VX]); - S32 dy = lltrunc((F32) (mCurrentMousePoint.mY - mLastMousePoint.mY) * LLUI::sGLScaleFactor.mV[VY]); - - //RN: fix for asynchronous notification of mouse leaving window not working - LLCoordWindow mouse_pos; - mWindow->getCursorPosition(&mouse_pos); - if (mouse_pos.mX < 0 || - mouse_pos.mY < 0 || - mouse_pos.mX > mWindowRectRaw.getWidth() || - mouse_pos.mY > mWindowRectRaw.getHeight()) - { - mMouseInWindow = FALSE; - } - else - { - mMouseInWindow = TRUE; - } - - LLVector2 mouse_vel; - - if (gSavedSettings.getBOOL("MouseSmooth")) - { - static F32 fdx = 0.f; - static F32 fdy = 0.f; - - F32 amount = 16.f; - fdx = fdx + ((F32) dx - fdx) * llmin(gFrameIntervalSeconds*amount,1.f); - fdy = fdy + ((F32) dy - fdy) * llmin(gFrameIntervalSeconds*amount,1.f); - - mCurrentMouseDelta.set(llround(fdx), llround(fdy)); - mouse_vel.setVec(fdx,fdy); - } - else - { - mCurrentMouseDelta.set(dx, dy); - mouse_vel.setVec((F32) dx, (F32) dy); - } - - mMouseVelocityStat.addValue(mouse_vel.magVec()); -} - -void LLViewerWindow::updateKeyboardFocus() -{ - if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) - { - gFocusMgr.setKeyboardFocus(NULL); - } - - // clean up current focus - LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); - if (cur_focus) - { - if (!cur_focus->isInVisibleChain() || !cur_focus->isInEnabledChain()) - { - // don't release focus, just reassign so that if being given - // to a sibling won't call onFocusLost on all the ancestors - // gFocusMgr.releaseFocusIfNeeded(cur_focus); - - LLUICtrl* parent = cur_focus->getParentUICtrl(); - const LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot(); - bool new_focus_found = false; - while(parent) - { - if (parent->isCtrl() - && (parent->hasTabStop() || parent == focus_root) - && !parent->getIsChrome() - && parent->isInVisibleChain() - && parent->isInEnabledChain()) - { - if (!parent->focusFirstItem()) - { - parent->setFocus(TRUE); - } - new_focus_found = true; - break; - } - parent = parent->getParentUICtrl(); - } - - // if we didn't find a better place to put focus, just release it - // hasFocus() will return true if and only if we didn't touch focus since we - // are only moving focus higher in the hierarchy - if (!new_focus_found) - { - cur_focus->setFocus(FALSE); - } - } - else if (cur_focus->isFocusRoot()) - { - // focus roots keep trying to delegate focus to their first valid descendant - // this assumes that focus roots are not valid focus holders on their own - cur_focus->focusFirstItem(); - } - } - - // last ditch force of edit menu to selection manager - if (LLEditMenuHandler::gEditMenuHandler == NULL && LLSelectMgr::getInstance()->getSelection()->getObjectCount()) - { - LLEditMenuHandler::gEditMenuHandler = LLSelectMgr::getInstance(); - } - - if (gFloaterView->getCycleMode()) - { - // sync all floaters with their focus state - gFloaterView->highlightFocusedFloater(); - gSnapshotFloaterView->highlightFocusedFloater(); - if ((gKeyboard->currentMask(TRUE) & MASK_CONTROL) == 0) - { - // control key no longer held down, finish cycle mode - gFloaterView->setCycleMode(FALSE); - - gFloaterView->syncFloaterTabOrder(); - } - else - { - // user holding down CTRL, don't update tab order of floaters - } - } - else - { - // update focused floater - gFloaterView->highlightFocusedFloater(); - gSnapshotFloaterView->highlightFocusedFloater(); - // make sure floater visible order is in sync with tab order - gFloaterView->syncFloaterTabOrder(); - } - - if(LLSideTray::instanceCreated())//just getInstance will create sidetray. we don't want this - LLSideTray::getInstance()->highlightFocused(); -} - -static LLFastTimer::DeclareTimer FTM_UPDATE_WORLD_VIEW("Update World View"); -void LLViewerWindow::updateWorldViewRect(bool use_full_window) -{ - LLFastTimer ft(FTM_UPDATE_WORLD_VIEW); - - // start off using whole window to render world - LLRect new_world_rect = mWindowRectRaw; - - if (use_full_window == false && mWorldViewPlaceholder.get()) - { - new_world_rect = mWorldViewPlaceholder.get()->calcScreenRect(); - // clamp to at least a 1x1 rect so we don't try to allocate zero width gl buffers - new_world_rect.mTop = llmax(new_world_rect.mTop, new_world_rect.mBottom + 1); - new_world_rect.mRight = llmax(new_world_rect.mRight, new_world_rect.mLeft + 1); - - new_world_rect.mLeft = llround((F32)new_world_rect.mLeft * mDisplayScale.mV[VX]); - new_world_rect.mRight = llround((F32)new_world_rect.mRight * mDisplayScale.mV[VX]); - new_world_rect.mBottom = llround((F32)new_world_rect.mBottom * mDisplayScale.mV[VY]); - new_world_rect.mTop = llround((F32)new_world_rect.mTop * mDisplayScale.mV[VY]); - } - - if (gSavedSettings.getBOOL("SidebarCameraMovement") == FALSE) - { - // use right edge of window, ignoring sidebar - new_world_rect.mRight = mWindowRectRaw.mRight; - } - - if (mWorldViewRectRaw != new_world_rect) - { - mWorldViewRectRaw = new_world_rect; - gResizeScreenTexture = TRUE; - LLViewerCamera::getInstance()->setViewHeightInPixels( mWorldViewRectRaw.getHeight() ); - LLViewerCamera::getInstance()->setAspect( getWorldViewAspectRatio() ); - - LLRect old_world_rect_scaled = mWorldViewRectScaled; - mWorldViewRectScaled = calcScaledRect(mWorldViewRectRaw, mDisplayScale); - - // sending a signal with a new WorldView rect - mOnWorldViewRectUpdated(old_world_rect_scaled, mWorldViewRectScaled); - } -} - -void LLViewerWindow::saveLastMouse(const LLCoordGL &point) -{ - // Store last mouse location. - // If mouse leaves window, pretend last point was on edge of window - if (point.mX < 0) - { - mCurrentMousePoint.mX = 0; - } - else if (point.mX > getWindowWidthScaled()) - { - mCurrentMousePoint.mX = getWindowWidthScaled(); - } - else - { - mCurrentMousePoint.mX = point.mX; - } - - if (point.mY < 0) - { - mCurrentMousePoint.mY = 0; - } - else if (point.mY > getWindowHeightScaled() ) - { - mCurrentMousePoint.mY = getWindowHeightScaled(); - } - else - { - mCurrentMousePoint.mY = point.mY; - } -} - - -// Draws the selection outlines for the currently selected objects -// Must be called after displayObjects is called, which sets the mGLName parameter -// NOTE: This function gets called 3 times: -// render_ui_3d: FALSE, FALSE, TRUE -// render_hud_elements: FALSE, FALSE, FALSE -void LLViewerWindow::renderSelections( BOOL for_gl_pick, BOOL pick_parcel_walls, BOOL for_hud ) -{ - LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); - - if (!for_hud && !for_gl_pick) - { - // Call this once and only once - LLSelectMgr::getInstance()->updateSilhouettes(); - } - - // Draw fence around land selections - if (for_gl_pick) - { - if (pick_parcel_walls) - { - LLViewerParcelMgr::getInstance()->renderParcelCollision(); - } - } - else if (( for_hud && selection->getSelectType() == SELECT_TYPE_HUD) || - (!for_hud && selection->getSelectType() != SELECT_TYPE_HUD)) - { - LLSelectMgr::getInstance()->renderSilhouettes(for_hud); - - stop_glerror(); - - // setup HUD render - if (selection->getSelectType() == SELECT_TYPE_HUD && LLSelectMgr::getInstance()->getSelection()->getObjectCount()) - { - LLBBox hud_bbox = gAgentAvatarp->getHUDBBox(); - - // set up transform to encompass bounding box of HUD - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - F32 depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f); - glOrtho(-0.5f * LLViewerCamera::getInstance()->getAspect(), 0.5f * LLViewerCamera::getInstance()->getAspect(), -0.5f, 0.5f, 0.f, depth); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame - glTranslatef(-hud_bbox.getCenterLocal().mV[VX] + (depth *0.5f), 0.f, 0.f); - } - - // Render light for editing - if (LLSelectMgr::sRenderLightRadius && LLToolMgr::getInstance()->inEdit()) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLGLEnable gls_blend(GL_BLEND); - LLGLEnable gls_cull(GL_CULL_FACE); - LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - if (selection->getSelectType() == SELECT_TYPE_HUD) - { - F32 zoom = gAgentCamera.mHUDCurZoom; - glScalef(zoom, zoom, zoom); - } - - struct f : public LLSelectedObjectFunctor - { - virtual bool apply(LLViewerObject* object) - { - LLDrawable* drawable = object->mDrawable; - if (drawable && drawable->isLight()) - { - LLVOVolume* vovolume = drawable->getVOVolume(); - glPushMatrix(); - - LLVector3 center = drawable->getPositionAgent(); - glTranslatef(center[0], center[1], center[2]); - F32 scale = vovolume->getLightRadius(); - glScalef(scale, scale, scale); - - LLColor4 color(vovolume->getLightColor(), .5f); - glColor4fv(color.mV); - - F32 pixel_area = 100000.f; - // Render Outside - gSphere.render(pixel_area); - - // Render Inside - glCullFace(GL_FRONT); - gSphere.render(pixel_area); - glCullFace(GL_BACK); - - glPopMatrix(); - } - return true; - } - } func; - LLSelectMgr::getInstance()->getSelection()->applyToObjects(&func); - - glPopMatrix(); - } - - // NOTE: The average position for the axis arrows of the selected objects should - // not be recalculated at this time. If they are, then group rotations will break. - - // Draw arrows at average center of all selected objects - LLTool* tool = LLToolMgr::getInstance()->getCurrentTool(); - if (tool) - { - if(tool->isAlwaysRendered()) - { - tool->render(); - } - else - { - if( !LLSelectMgr::getInstance()->getSelection()->isEmpty() ) - { - BOOL moveable_object_selected = FALSE; - BOOL all_selected_objects_move = TRUE; - BOOL all_selected_objects_modify = TRUE; - BOOL selecting_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); - - for (LLObjectSelection::iterator iter = LLSelectMgr::getInstance()->getSelection()->begin(); - iter != LLSelectMgr::getInstance()->getSelection()->end(); iter++) - { - LLSelectNode* nodep = *iter; - LLViewerObject* object = nodep->getObject(); - BOOL this_object_movable = FALSE; - if (object->permMove() && (object->permModify() || selecting_linked_set)) - { - moveable_object_selected = TRUE; - this_object_movable = TRUE; - } - all_selected_objects_move = all_selected_objects_move && this_object_movable; - all_selected_objects_modify = all_selected_objects_modify && object->permModify(); - } - - BOOL draw_handles = TRUE; - - if (tool == LLToolCompTranslate::getInstance() && (!moveable_object_selected || !all_selected_objects_move)) - { - draw_handles = FALSE; - } - - if (tool == LLToolCompRotate::getInstance() && (!moveable_object_selected || !all_selected_objects_move)) - { - draw_handles = FALSE; - } - - if ( !all_selected_objects_modify && tool == LLToolCompScale::getInstance() ) - { - draw_handles = FALSE; - } - - if( draw_handles ) - { - tool->render(); - } - } - } - if (selection->getSelectType() == SELECT_TYPE_HUD && selection->getObjectCount()) - { - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - stop_glerror(); - } - } - } -} - -// Return a point near the clicked object representative of the place the object was clicked. -LLVector3d LLViewerWindow::clickPointInWorldGlobal(S32 x, S32 y_from_bot, LLViewerObject* clicked_object) const -{ - // create a normalized vector pointing from the camera center into the - // world at the location of the mouse click - LLVector3 mouse_direction_global = mouseDirectionGlobal( x, y_from_bot ); - - LLVector3d relative_object = clicked_object->getPositionGlobal() - gAgentCamera.getCameraPositionGlobal(); - - // make mouse vector as long as object vector, so it touchs a point near - // where the user clicked on the object - mouse_direction_global *= (F32) relative_object.magVec(); - - LLVector3d new_pos; - new_pos.setVec(mouse_direction_global); - // transform mouse vector back to world coords - new_pos += gAgentCamera.getCameraPositionGlobal(); - - return new_pos; -} - - -BOOL LLViewerWindow::clickPointOnSurfaceGlobal(const S32 x, const S32 y, LLViewerObject *objectp, LLVector3d &point_global) const -{ - BOOL intersect = FALSE; - -// U8 shape = objectp->mPrimitiveCode & LL_PCODE_BASE_MASK; - if (!intersect) - { - point_global = clickPointInWorldGlobal(x, y, objectp); - llinfos << "approx intersection at " << (objectp->getPositionGlobal() - point_global) << llendl; - } - else - { - llinfos << "good intersection at " << (objectp->getPositionGlobal() - point_global) << llendl; - } - - return intersect; -} - -void LLViewerWindow::pickAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(const LLPickInfo& info), BOOL pick_transparent) -{ - if (gNoRender) - { - return; - } - - BOOL in_build_mode = LLFloaterReg::instanceVisible("build"); - if (in_build_mode || LLDrawPoolAlpha::sShowDebugAlpha) - { - // build mode allows interaction with all transparent objects - // "Show Debug Alpha" means no object actually transparent - pick_transparent = TRUE; - } - - LLPickInfo pick_info(LLCoordGL(x, y_from_bot), mask, pick_transparent, TRUE, callback); - schedulePick(pick_info); -} - -void LLViewerWindow::schedulePick(LLPickInfo& pick_info) -{ - if (mPicks.size() >= 1024 || mWindow->getMinimized()) - { //something went wrong, picks are being scheduled but not processed - - if (pick_info.mPickCallback) - { - pick_info.mPickCallback(pick_info); - } - - return; - } - mPicks.push_back(pick_info); - - // delay further event processing until we receive results of pick - // only do this for async picks so that handleMouseUp won't be called - // until the pick triggered in handleMouseDown has been processed, for example - mWindow->delayInputProcessing(); -} - - -void LLViewerWindow::performPick() -{ - if (gNoRender) - { - return; - } - - if (!mPicks.empty()) - { - std::vector::iterator pick_it; - for (pick_it = mPicks.begin(); pick_it != mPicks.end(); ++pick_it) - { - pick_it->fetchResults(); - } - - mLastPick = mPicks.back(); - mPicks.clear(); - } -} - -void LLViewerWindow::returnEmptyPicks() -{ - std::vector::iterator pick_it; - for (pick_it = mPicks.begin(); pick_it != mPicks.end(); ++pick_it) - { - mLastPick = *pick_it; - // just trigger callback with empty results - if (pick_it->mPickCallback) - { - pick_it->mPickCallback(*pick_it); - } - } - mPicks.clear(); -} - -// Performs the GL object/land pick. -LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot, BOOL pick_transparent) -{ - if (gNoRender) - { - return LLPickInfo(); - } - - BOOL in_build_mode = LLFloaterReg::instanceVisible("build"); - if (in_build_mode || LLDrawPoolAlpha::sShowDebugAlpha) - { - // build mode allows interaction with all transparent objects - // "Show Debug Alpha" means no object actually transparent - pick_transparent = TRUE; - } - - // shortcut queueing in mPicks and just update mLastPick in place - mLastPick = LLPickInfo(LLCoordGL(x, y_from_bot), gKeyboard->currentMask(TRUE), pick_transparent, TRUE, NULL); - mLastPick.fetchResults(); - - return mLastPick; -} - -LLHUDIcon* LLViewerWindow::cursorIntersectIcon(S32 mouse_x, S32 mouse_y, F32 depth, - LLVector3* intersection) -{ - S32 x = mouse_x; - S32 y = mouse_y; - - if ((mouse_x == -1) && (mouse_y == -1)) // use current mouse position - { - x = getCurrentMouseX(); - y = getCurrentMouseY(); - } - - // world coordinates of mouse - LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y); - LLVector3 mouse_point_global = LLViewerCamera::getInstance()->getOrigin(); - LLVector3 mouse_world_start = mouse_point_global; - LLVector3 mouse_world_end = mouse_point_global + mouse_direction_global * depth; - - return LLHUDIcon::lineSegmentIntersectAll(mouse_world_start, mouse_world_end, intersection); - - -} - -LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 depth, - LLViewerObject *this_object, - S32 this_face, - BOOL pick_transparent, - S32* face_hit, - LLVector3 *intersection, - LLVector2 *uv, - LLVector3 *normal, - LLVector3 *binormal) -{ - S32 x = mouse_x; - S32 y = mouse_y; - - if ((mouse_x == -1) && (mouse_y == -1)) // use current mouse position - { - x = getCurrentMouseX(); - y = getCurrentMouseY(); - } - - // HUD coordinates of mouse - LLVector3 mouse_point_hud = mousePointHUD(x, y); - LLVector3 mouse_hud_start = mouse_point_hud - LLVector3(depth, 0, 0); - LLVector3 mouse_hud_end = mouse_point_hud + LLVector3(depth, 0, 0); - - // world coordinates of mouse - LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y); - LLVector3 mouse_point_global = LLViewerCamera::getInstance()->getOrigin(); - - //get near clip plane - LLVector3 n = LLViewerCamera::getInstance()->getAtAxis(); - LLVector3 p = mouse_point_global + n * LLViewerCamera::getInstance()->getNear(); - - //project mouse point onto plane - LLVector3 pos; - line_plane(mouse_point_global, mouse_direction_global, p, n, pos); - mouse_point_global = pos; - - LLVector3 mouse_world_start = mouse_point_global; - LLVector3 mouse_world_end = mouse_point_global + mouse_direction_global * depth; - - - LLViewerObject* found = NULL; - - if (this_object) // check only this object - { - if (this_object->isHUDAttachment()) // is a HUD object? - { - if (this_object->lineSegmentIntersect(mouse_hud_start, mouse_hud_end, this_face, pick_transparent, - face_hit, intersection, uv, normal, binormal)) - { - found = this_object; - } - } - - else // is a world object - { - if (this_object->lineSegmentIntersect(mouse_world_start, mouse_world_end, this_face, pick_transparent, - face_hit, intersection, uv, normal, binormal)) - { - found = this_object; - } - } - } - - else // check ALL objects - { - found = gPipeline.lineSegmentIntersectInHUD(mouse_hud_start, mouse_hud_end, pick_transparent, - face_hit, intersection, uv, normal, binormal); - - if (!found) // if not found in HUD, look in world: - - { - found = gPipeline.lineSegmentIntersectInWorld(mouse_world_start, mouse_world_end, pick_transparent, - face_hit, intersection, uv, normal, binormal); - } - - } - - return found; -} - -// Returns unit vector relative to camera -// indicating direction of point on screen x,y -LLVector3 LLViewerWindow::mouseDirectionGlobal(const S32 x, const S32 y) const -{ - // find vertical field of view - F32 fov = LLViewerCamera::getInstance()->getView(); - - // find world view center in scaled ui coordinates - F32 center_x = getWorldViewRectScaled().getCenterX(); - F32 center_y = getWorldViewRectScaled().getCenterY(); - - // calculate pixel distance to screen - F32 distance = ((F32)getWorldViewHeightScaled() * 0.5f) / (tan(fov / 2.f)); - - // calculate click point relative to middle of screen - F32 click_x = x - center_x; - F32 click_y = y - center_y; - - // compute mouse vector - LLVector3 mouse_vector = distance * LLViewerCamera::getInstance()->getAtAxis() - - click_x * LLViewerCamera::getInstance()->getLeftAxis() - + click_y * LLViewerCamera::getInstance()->getUpAxis(); - - mouse_vector.normVec(); - - return mouse_vector; -} - -LLVector3 LLViewerWindow::mousePointHUD(const S32 x, const S32 y) const -{ - // find screen resolution - S32 height = getWorldViewHeightScaled(); - - // find world view center - F32 center_x = getWorldViewRectScaled().getCenterX(); - F32 center_y = getWorldViewRectScaled().getCenterY(); - - // remap with uniform scale (1/height) so that top is -0.5, bottom is +0.5 - F32 hud_x = -((F32)x - center_x) / height; - F32 hud_y = ((F32)y - center_y) / height; - - return LLVector3(0.f, hud_x/gAgentCamera.mHUDCurZoom, hud_y/gAgentCamera.mHUDCurZoom); -} - -// Returns unit vector relative to camera in camera space -// indicating direction of point on screen x,y -LLVector3 LLViewerWindow::mouseDirectionCamera(const S32 x, const S32 y) const -{ - // find vertical field of view - F32 fov_height = LLViewerCamera::getInstance()->getView(); - F32 fov_width = fov_height * LLViewerCamera::getInstance()->getAspect(); - - // find screen resolution - S32 height = getWorldViewHeightScaled(); - S32 width = getWorldViewWidthScaled(); - - // find world view center - F32 center_x = getWorldViewRectScaled().getCenterX(); - F32 center_y = getWorldViewRectScaled().getCenterY(); - - // calculate click point relative to middle of screen - F32 click_x = (((F32)x - center_x) / (F32)width) * fov_width * -1.f; - F32 click_y = (((F32)y - center_y) / (F32)height) * fov_height; - - // compute mouse vector - LLVector3 mouse_vector = LLVector3(0.f, 0.f, -1.f); - LLQuaternion mouse_rotate; - mouse_rotate.setQuat(click_y, click_x, 0.f); - - mouse_vector = mouse_vector * mouse_rotate; - // project to z = -1 plane; - mouse_vector = mouse_vector * (-1.f / mouse_vector.mV[VZ]); - - return mouse_vector; -} - - - -BOOL LLViewerWindow::mousePointOnPlaneGlobal(LLVector3d& point, const S32 x, const S32 y, - const LLVector3d &plane_point_global, - const LLVector3 &plane_normal_global) -{ - LLVector3d mouse_direction_global_d; - - mouse_direction_global_d.setVec(mouseDirectionGlobal(x,y)); - LLVector3d plane_normal_global_d; - plane_normal_global_d.setVec(plane_normal_global); - F64 plane_mouse_dot = (plane_normal_global_d * mouse_direction_global_d); - LLVector3d plane_origin_camera_rel = plane_point_global - gAgentCamera.getCameraPositionGlobal(); - F64 mouse_look_at_scale = (plane_normal_global_d * plane_origin_camera_rel) - / plane_mouse_dot; - if (llabs(plane_mouse_dot) < 0.00001) - { - // if mouse is parallel to plane, return closest point on line through plane origin - // that is parallel to camera plane by scaling mouse direction vector - // by distance to plane origin, modulated by deviation of mouse direction from plane origin - LLVector3d plane_origin_dir = plane_origin_camera_rel; - plane_origin_dir.normVec(); - - mouse_look_at_scale = plane_origin_camera_rel.magVec() / (plane_origin_dir * mouse_direction_global_d); - } - - point = gAgentCamera.getCameraPositionGlobal() + mouse_look_at_scale * mouse_direction_global_d; - - return mouse_look_at_scale > 0.0; -} - - -// Returns global position -BOOL LLViewerWindow::mousePointOnLandGlobal(const S32 x, const S32 y, LLVector3d *land_position_global) -{ - LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y); - F32 mouse_dir_scale; - BOOL hit_land = FALSE; - LLViewerRegion *regionp; - F32 land_z; - const F32 FIRST_PASS_STEP = 1.0f; // meters - const F32 SECOND_PASS_STEP = 0.1f; // meters - LLVector3d camera_pos_global; - - camera_pos_global = gAgentCamera.getCameraPositionGlobal(); - LLVector3d probe_point_global; - LLVector3 probe_point_region; - - // walk forwards to find the point - for (mouse_dir_scale = FIRST_PASS_STEP; mouse_dir_scale < gAgentCamera.mDrawDistance; mouse_dir_scale += FIRST_PASS_STEP) - { - LLVector3d mouse_direction_global_d; - mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale); - probe_point_global = camera_pos_global + mouse_direction_global_d; - - regionp = LLWorld::getInstance()->resolveRegionGlobal(probe_point_region, probe_point_global); - - if (!regionp) - { - // ...we're outside the world somehow - continue; - } - - S32 i = (S32) (probe_point_region.mV[VX]/regionp->getLand().getMetersPerGrid()); - S32 j = (S32) (probe_point_region.mV[VY]/regionp->getLand().getMetersPerGrid()); - S32 grids_per_edge = (S32) regionp->getLand().mGridsPerEdge; - if ((i >= grids_per_edge) || (j >= grids_per_edge)) - { - //llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl; - continue; - } - - land_z = regionp->getLand().resolveHeightRegion(probe_point_region); - - //llinfos << "mousePointOnLand initial z " << land_z << llendl; - - if (probe_point_region.mV[VZ] < land_z) - { - // ...just went under land - - // cout << "under land at " << probe_point << " scale " << mouse_vec_scale << endl; - - hit_land = TRUE; - break; - } - } - - - if (hit_land) - { - // Don't go more than one step beyond where we stopped above. - // This can't just be "mouse_vec_scale" because floating point error - // will stop the loop before the last increment.... X - 1.0 + 0.1 + 0.1 + ... + 0.1 != X - F32 stop_mouse_dir_scale = mouse_dir_scale + FIRST_PASS_STEP; - - // take a step backwards, then walk forwards again to refine position - for ( mouse_dir_scale -= FIRST_PASS_STEP; mouse_dir_scale <= stop_mouse_dir_scale; mouse_dir_scale += SECOND_PASS_STEP) - { - LLVector3d mouse_direction_global_d; - mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale); - probe_point_global = camera_pos_global + mouse_direction_global_d; - - regionp = LLWorld::getInstance()->resolveRegionGlobal(probe_point_region, probe_point_global); - - if (!regionp) - { - // ...we're outside the world somehow - continue; - } - - /* - i = (S32) (local_probe_point.mV[VX]/regionp->getLand().getMetersPerGrid()); - j = (S32) (local_probe_point.mV[VY]/regionp->getLand().getMetersPerGrid()); - if ((i >= regionp->getLand().mGridsPerEdge) || (j >= regionp->getLand().mGridsPerEdge)) - { - // llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl; - continue; - } - land_z = regionp->getLand().mSurfaceZ[ i + j * (regionp->getLand().mGridsPerEdge) ]; - */ - - land_z = regionp->getLand().resolveHeightRegion(probe_point_region); - - //llinfos << "mousePointOnLand refine z " << land_z << llendl; - - if (probe_point_region.mV[VZ] < land_z) - { - // ...just went under land again - - *land_position_global = probe_point_global; - return TRUE; - } - } - } - - return FALSE; -} - -// Saves an image to the harddrive as "SnapshotX" where X >= 1. -BOOL LLViewerWindow::saveImageNumbered(LLImageFormatted *image) -{ - if (!image) - { - return FALSE; - } - - LLFilePicker::ESaveFilter pick_type; - std::string extension("." + image->getExtension()); - if (extension == ".j2c") - pick_type = LLFilePicker::FFSAVE_J2C; - else if (extension == ".bmp") - pick_type = LLFilePicker::FFSAVE_BMP; - else if (extension == ".jpg") - pick_type = LLFilePicker::FFSAVE_JPEG; - else if (extension == ".png") - pick_type = LLFilePicker::FFSAVE_PNG; - else if (extension == ".tga") - pick_type = LLFilePicker::FFSAVE_TGA; - else - pick_type = LLFilePicker::FFSAVE_ALL; // ??? - - // Get a base file location if needed. - if ( ! isSnapshotLocSet()) - { - std::string proposed_name( sSnapshotBaseName ); - - // getSaveFile will append an appropriate extension to the proposed name, based on the ESaveFilter constant passed in. - - // pick a directory in which to save - LLFilePicker& picker = LLFilePicker::instance(); - if (!picker.getSaveFile(pick_type, proposed_name)) - { - // Clicked cancel - return FALSE; - } - - // Copy the directory + file name - std::string filepath = picker.getFirstFile(); - - LLViewerWindow::sSnapshotBaseName = gDirUtilp->getBaseFileName(filepath, true); - LLViewerWindow::sSnapshotDir = gDirUtilp->getDirName(filepath); - } - - // Look for an unused file name - std::string filepath; - S32 i = 1; - S32 err = 0; - - do - { - filepath = sSnapshotDir; - filepath += gDirUtilp->getDirDelimiter(); - filepath += sSnapshotBaseName; - filepath += llformat("_%.3d",i); - filepath += extension; - - llstat stat_info; - err = LLFile::stat( filepath, &stat_info ); - i++; - } - while( -1 != err ); // search until the file is not found (i.e., stat() gives an error). - - return image->save(filepath); -} - -void LLViewerWindow::resetSnapshotLoc() -{ - sSnapshotDir.clear(); -} - -static S32 BORDERHEIGHT = 0; -static S32 BORDERWIDTH = 0; - -// static -void LLViewerWindow::movieSize(S32 new_width, S32 new_height) -{ - LLCoordScreen size; - gViewerWindow->mWindow->getSize(&size); - if ( (size.mX != new_width + BORDERWIDTH) - ||(size.mY != new_height + BORDERHEIGHT)) - { - // use actual display dimensions, not virtual UI dimensions - S32 x = gViewerWindow->getWindowWidthRaw(); - S32 y = gViewerWindow->getWindowHeightRaw(); - BORDERWIDTH = size.mX - x; - BORDERHEIGHT = size.mY- y; - LLCoordScreen new_size(new_width + BORDERWIDTH, - new_height + BORDERHEIGHT); - gViewerWindow->mWindow->setSize(new_size); - } -} - -BOOL LLViewerWindow::saveSnapshot( const std::string& filepath, S32 image_width, S32 image_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type) -{ - llinfos << "Saving snapshot to: " << filepath << llendl; - - LLPointer raw = new LLImageRaw; - BOOL success = rawSnapshot(raw, image_width, image_height, TRUE, FALSE, show_ui, do_rebuild); - - if (success) - { - LLPointer bmp_image = new LLImageBMP; - success = bmp_image->encode(raw, 0.0f); - if( success ) - { - success = bmp_image->save(filepath); - } - else - { - llwarns << "Unable to encode bmp snapshot" << llendl; - } - } - else - { - llwarns << "Unable to capture raw snapshot" << llendl; - } - - return success; -} - - -void LLViewerWindow::playSnapshotAnimAndSound() -{ - if (gSavedSettings.getBOOL("QuietSnapshotsToDisk")) - { - return; - } - gAgent.sendAnimationRequest(ANIM_AGENT_SNAPSHOT, ANIM_REQUEST_START); - send_sound_trigger(LLUUID(gSavedSettings.getString("UISndSnapshot")), 1.0f); -} - -BOOL LLViewerWindow::thumbnailSnapshot(LLImageRaw *raw, S32 preview_width, S32 preview_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type) -{ - return rawSnapshot(raw, preview_width, preview_height, FALSE, FALSE, show_ui, do_rebuild, type); -} - -// Saves the image from the screen to the specified filename and path. -BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height, - BOOL keep_window_aspect, BOOL is_texture, BOOL show_ui, BOOL do_rebuild, ESnapshotType type, S32 max_size) -{ - if (!raw) - { - return FALSE; - } - - // PRE SNAPSHOT - gDisplaySwapBuffers = FALSE; - - glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - setCursor(UI_CURSOR_WAIT); - - // Hide all the UI widgets first and draw a frame - BOOL prev_draw_ui = gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI) ? TRUE : FALSE; - - show_ui = show_ui ? TRUE : FALSE; - - if ( prev_draw_ui != show_ui) - { - LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI); - } - - BOOL hide_hud = !gSavedSettings.getBOOL("RenderHUDInSnapshot") && LLPipeline::sShowHUDAttachments; - if (hide_hud) - { - LLPipeline::sShowHUDAttachments = FALSE; - } - - // if not showing ui, use full window to render world view - updateWorldViewRect(!show_ui); - - // Copy screen to a buffer - // crop sides or top and bottom, if taking a snapshot of different aspect ratio - // from window - LLRect window_rect = show_ui ? getWindowRectRaw() : getWorldViewRectRaw(); - - S32 snapshot_width = window_rect.getWidth(); - S32 snapshot_height = window_rect.getHeight(); - // SNAPSHOT - S32 window_width = snapshot_width; - S32 window_height = snapshot_height; - - if (show_ui) - { - image_width = llmin(image_width, window_width); - image_height = llmin(image_height, window_height); - } - - F32 scale_factor = 1.0f ; - if(!keep_window_aspect) //image cropping - { - F32 ratio = llmin( (F32)window_width / image_width , (F32)window_height / image_height) ; - snapshot_width = (S32)(ratio * image_width) ; - snapshot_height = (S32)(ratio * image_height) ; - scale_factor = llmax(1.0f, 1.0f / ratio) ; - } - else //the scene(window) proportion needs to be maintained. - { - if(image_width > window_width || image_height > window_height) //need to enlarge the scene - { - F32 ratio = llmin( (F32)window_width / image_width , (F32)window_height / image_height) ; - snapshot_width = (S32)(ratio * image_width) ; - snapshot_height = (S32)(ratio * image_height) ; - scale_factor = llmax(1.0f, 1.0f / ratio) ; - } - } - - if (show_ui && scale_factor > 1.f) - { - llwarns << "over scaling UI not supported." << llendl; - } - - S32 buffer_x_offset = llfloor(((window_width - snapshot_width) * scale_factor) / 2.f); - S32 buffer_y_offset = llfloor(((window_height - snapshot_height) * scale_factor) / 2.f); - - S32 image_buffer_x = llfloor(snapshot_width*scale_factor) ; - S32 image_buffer_y = llfloor(snapshot_height *scale_factor) ; - - if(image_buffer_x > max_size || image_buffer_y > max_size) //boundary check to avoid memory overflow - { - scale_factor *= llmin((F32)max_size / image_buffer_x, (F32)max_size / image_buffer_y) ; - image_buffer_x = llfloor(snapshot_width*scale_factor) ; - image_buffer_y = llfloor(snapshot_height *scale_factor) ; - } - if(image_buffer_x > 0 && image_buffer_y > 0) - { - raw->resize(image_buffer_x, image_buffer_y, 3); - } - else - { - return FALSE ; - } - if(raw->isBufferInvalid()) - { - return FALSE ; - } - - BOOL high_res = scale_factor >= 2.f; // Font scaling is slow, only do so if rez is much higher - if (high_res && show_ui) - { - llwarns << "High res UI snapshot not supported. " << llendl; - /*send_agent_pause(); - //rescale fonts - initFonts(scale_factor); - LLHUDObject::reshapeAll();*/ - } - - S32 output_buffer_offset_y = 0; - - F32 depth_conversion_factor_1 = (LLViewerCamera::getInstance()->getFar() + LLViewerCamera::getInstance()->getNear()) / (2.f * LLViewerCamera::getInstance()->getFar() * LLViewerCamera::getInstance()->getNear()); - F32 depth_conversion_factor_2 = (LLViewerCamera::getInstance()->getFar() - LLViewerCamera::getInstance()->getNear()) / (2.f * LLViewerCamera::getInstance()->getFar() * LLViewerCamera::getInstance()->getNear()); - - gObjectList.generatePickList(*LLViewerCamera::getInstance()); - - for (int subimage_y = 0; subimage_y < scale_factor; ++subimage_y) - { - S32 subimage_y_offset = llclamp(buffer_y_offset - (subimage_y * window_height), 0, window_height);; - // handle fractional columns - U32 read_height = llmax(0, (window_height - subimage_y_offset) - - llmax(0, (window_height * (subimage_y + 1)) - (buffer_y_offset + raw->getHeight()))); - - S32 output_buffer_offset_x = 0; - for (int subimage_x = 0; subimage_x < scale_factor; ++subimage_x) - { - gDisplaySwapBuffers = FALSE; - gDepthDirty = TRUE; - - const U32 subfield = subimage_x+(subimage_y*llceil(scale_factor)); - - if (LLPipeline::sRenderDeferred) - { - display(do_rebuild, scale_factor, subfield, TRUE); - } - else - { - display(do_rebuild, scale_factor, subfield, TRUE); - // Required for showing the GUI in snapshots and performing bloom composite overlay - // Call even if show_ui is FALSE - render_ui(scale_factor, subfield); - } - - S32 subimage_x_offset = llclamp(buffer_x_offset - (subimage_x * window_width), 0, window_width); - // handle fractional rows - U32 read_width = llmax(0, (window_width - subimage_x_offset) - - llmax(0, (window_width * (subimage_x + 1)) - (buffer_x_offset + raw->getWidth()))); - for(U32 out_y = 0; out_y < read_height ; out_y++) - { - S32 output_buffer_offset = ( - (out_y * (raw->getWidth())) // ...plus iterated y... - + (window_width * subimage_x) // ...plus subimage start in x... - + (raw->getWidth() * window_height * subimage_y) // ...plus subimage start in y... - - output_buffer_offset_x // ...minus buffer padding x... - - (output_buffer_offset_y * (raw->getWidth())) // ...minus buffer padding y... - ) * raw->getComponents(); - - // Ping the wathdog thread every 100 lines to keep us alive (arbitrary number, feel free to change) - if (out_y % 100 == 0) - { - LLAppViewer::instance()->pingMainloopTimeout("LLViewerWindow::rawSnapshot"); - } - - if (type == SNAPSHOT_TYPE_COLOR) - { - glReadPixels( - subimage_x_offset, out_y + subimage_y_offset, - read_width, 1, - GL_RGB, GL_UNSIGNED_BYTE, - raw->getData() + output_buffer_offset - ); - } - else // SNAPSHOT_TYPE_DEPTH - { - LLPointer depth_line_buffer = new LLImageRaw(read_width, 1, sizeof(GL_FLOAT)); // need to store floating point values - glReadPixels( - subimage_x_offset, out_y + subimage_y_offset, - read_width, 1, - GL_DEPTH_COMPONENT, GL_FLOAT, - depth_line_buffer->getData()// current output pixel is beginning of buffer... - ); - - for (S32 i = 0; i < (S32)read_width; i++) - { - F32 depth_float = *(F32*)(depth_line_buffer->getData() + (i * sizeof(F32))); - - F32 linear_depth_float = 1.f / (depth_conversion_factor_1 - (depth_float * depth_conversion_factor_2)); - U8 depth_byte = F32_to_U8(linear_depth_float, LLViewerCamera::getInstance()->getNear(), LLViewerCamera::getInstance()->getFar()); - //write converted scanline out to result image - for(S32 j = 0; j < raw->getComponents(); j++) - { - *(raw->getData() + output_buffer_offset + (i * raw->getComponents()) + j) = depth_byte; - } - } - } - } - output_buffer_offset_x += subimage_x_offset; - stop_glerror(); - } - output_buffer_offset_y += subimage_y_offset; - } - - gDisplaySwapBuffers = FALSE; - gDepthDirty = TRUE; - - // POST SNAPSHOT - if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) - { - LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI); - } - - if (hide_hud) - { - LLPipeline::sShowHUDAttachments = TRUE; - } - - /*if (high_res) - { - initFonts(1.f); - LLHUDObject::reshapeAll(); - }*/ - - // Pre-pad image to number of pixels such that the line length is a multiple of 4 bytes (for BMP encoding) - // Note: this formula depends on the number of components being 3. Not obvious, but it's correct. - image_width += (image_width * 3) % 4; - - BOOL ret = TRUE ; - // Resize image - if(llabs(image_width - image_buffer_x) > 4 || llabs(image_height - image_buffer_y) > 4) - { - ret = raw->scale( image_width, image_height ); - } - else if(image_width != image_buffer_x || image_height != image_buffer_y) - { - ret = raw->scale( image_width, image_height, FALSE ); - } - - - setCursor(UI_CURSOR_ARROW); - - if (do_rebuild) - { - // If we had to do a rebuild, that means that the lists of drawables to be rendered - // was empty before we started. - // Need to reset these, otherwise we call state sort on it again when render gets called the next time - // and we stand a good chance of crashing on rebuild because the render drawable arrays have multiple copies of - // objects on them. - gPipeline.resetDrawOrders(); - } - - if (high_res) - { - send_agent_resume(); - } - - return ret; -} - -void LLViewerWindow::destroyWindow() -{ - if (mWindow) - { - LLWindowManager::destroyWindow(mWindow); - } - mWindow = NULL; -} - - -void LLViewerWindow::drawMouselookInstructions() -{ - // Draw instructions for mouselook ("Press ESC to return to World View" partially transparent at the bottom of the screen.) - const std::string instructions = LLTrans::getString("LeaveMouselook"); - const LLFontGL* font = LLFontGL::getFont(LLFontDescriptor("SansSerif", "Large", LLFontGL::BOLD)); - - //to be on top of Bottom bar when it is opened - const S32 INSTRUCTIONS_PAD = 50; - - font->renderUTF8( - instructions, 0, - getWorldViewRectScaled().getCenterX(), - getWorldViewRectScaled().mBottom + INSTRUCTIONS_PAD, - LLColor4( 1.0f, 1.0f, 1.0f, 0.5f ), - LLFontGL::HCENTER, LLFontGL::TOP, - LLFontGL::NORMAL,LLFontGL::DROP_SHADOW); -} - -void* LLViewerWindow::getPlatformWindow() const -{ - return mWindow->getPlatformWindow(); -} - -void* LLViewerWindow::getMediaWindow() const -{ - return mWindow->getMediaWindow(); -} - -void LLViewerWindow::focusClient() const -{ - return mWindow->focusClient(); -} - -LLRootView* LLViewerWindow::getRootView() const -{ - return mRootView; -} - -LLRect LLViewerWindow::getWorldViewRectScaled() const -{ - return mWorldViewRectScaled; -} - -S32 LLViewerWindow::getWorldViewHeightScaled() const -{ - return mWorldViewRectScaled.getHeight(); -} - -S32 LLViewerWindow::getWorldViewWidthScaled() const -{ - return mWorldViewRectScaled.getWidth(); -} - - -S32 LLViewerWindow::getWorldViewHeightRaw() const -{ - return mWorldViewRectRaw.getHeight(); -} - -S32 LLViewerWindow::getWorldViewWidthRaw() const -{ - return mWorldViewRectRaw.getWidth(); -} - -S32 LLViewerWindow::getWindowHeightScaled() const -{ - return mWindowRectScaled.getHeight(); -} - -S32 LLViewerWindow::getWindowWidthScaled() const -{ - return mWindowRectScaled.getWidth(); -} - -S32 LLViewerWindow::getWindowHeightRaw() const -{ - return mWindowRectRaw.getHeight(); -} - -S32 LLViewerWindow::getWindowWidthRaw() const -{ - return mWindowRectRaw.getWidth(); -} - -void LLViewerWindow::setup2DRender() -{ - // setup ortho camera - gl_state_for_2d(mWindowRectRaw.getWidth(), mWindowRectRaw.getHeight()); - setup2DViewport(); -} - -void LLViewerWindow::setup2DViewport(S32 x_offset, S32 y_offset) -{ - gGLViewport[0] = mWindowRectRaw.mLeft + x_offset; - gGLViewport[1] = mWindowRectRaw.mBottom + y_offset; - gGLViewport[2] = mWindowRectRaw.getWidth(); - gGLViewport[3] = mWindowRectRaw.getHeight(); - glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); -} - - -void LLViewerWindow::setup3DRender() -{ - // setup perspective camera - LLViewerCamera::getInstance()->setPerspective(NOT_FOR_SELECTION, mWorldViewRectRaw.mLeft, mWorldViewRectRaw.mBottom, mWorldViewRectRaw.getWidth(), mWorldViewRectRaw.getHeight(), FALSE, LLViewerCamera::getInstance()->getNear(), MAX_FAR_CLIP*2.f); - setup3DViewport(); -} - -void LLViewerWindow::setup3DViewport(S32 x_offset, S32 y_offset) -{ - gGLViewport[0] = mWorldViewRectRaw.mLeft + x_offset; - gGLViewport[1] = mWorldViewRectRaw.mBottom + y_offset; - gGLViewport[2] = mWorldViewRectRaw.getWidth(); - gGLViewport[3] = mWorldViewRectRaw.getHeight(); - glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); -} - -void LLViewerWindow::setShowProgress(const BOOL show) -{ - if (mProgressView) - { - mProgressView->setVisible(show); - } -} - -BOOL LLViewerWindow::getShowProgress() const -{ - return (mProgressView && mProgressView->getVisible()); -} - -void LLViewerWindow::setProgressString(const std::string& string) -{ - if (mProgressView) - { - mProgressView->setText(string); - } -} - -void LLViewerWindow::setProgressMessage(const std::string& msg) -{ - if(mProgressView) - { - mProgressView->setMessage(msg); - } -} - -void LLViewerWindow::setProgressPercent(const F32 percent) -{ - if (mProgressView) - { - mProgressView->setPercent(percent); - } -} - -void LLViewerWindow::setProgressCancelButtonVisible( BOOL b, const std::string& label ) -{ - if (mProgressView) - { - mProgressView->setCancelButtonVisible( b, label ); - } -} - - -LLProgressView *LLViewerWindow::getProgressView() const -{ - return mProgressView; -} - -void LLViewerWindow::dumpState() -{ - llinfos << "LLViewerWindow Active " << S32(mActive) << llendl; - llinfos << "mWindow visible " << S32(mWindow->getVisible()) - << " minimized " << S32(mWindow->getMinimized()) - << llendl; -} - -void LLViewerWindow::stopGL(BOOL save_state) -{ - //Note: --bao - //if not necessary, do not change the order of the function calls in this function. - //if change something, make sure it will not break anything. - //especially be careful to put anything behind gTextureList.destroyGL(save_state); - if (!gGLManager.mIsDisabled) - { - llinfos << "Shutting down GL..." << llendl; - - // Pause texture decode threads (will get unpaused during main loop) - LLAppViewer::getTextureCache()->pause(); - LLAppViewer::getImageDecodeThread()->pause(); - LLAppViewer::getTextureFetch()->pause(); - - gSky.destroyGL(); - stop_glerror(); - - LLManipTranslate::destroyGL() ; - stop_glerror(); - - gBumpImageList.destroyGL(); - stop_glerror(); - - LLFontGL::destroyAllGL(); - stop_glerror(); - - LLVOAvatar::destroyGL(); - stop_glerror(); - - LLViewerDynamicTexture::destroyGL(); - stop_glerror(); - - if (gPipeline.isInit()) - { - gPipeline.destroyGL(); - } - - gCone.cleanupGL(); - gBox.cleanupGL(); - gSphere.cleanupGL(); - gCylinder.cleanupGL(); - - if(gPostProcess) - { - gPostProcess->invalidate(); - } - - gTextureList.destroyGL(save_state); - stop_glerror(); - - gGLManager.mIsDisabled = TRUE; - stop_glerror(); - - llinfos << "Remaining allocated texture memory: " << LLImageGL::sGlobalTextureMemoryInBytes << " bytes" << llendl; - } -} - -void LLViewerWindow::restoreGL(const std::string& progress_message) -{ - //Note: --bao - //if not necessary, do not change the order of the function calls in this function. - //if change something, make sure it will not break anything. - //especially, be careful to put something before gTextureList.restoreGL(); - if (gGLManager.mIsDisabled) - { - llinfos << "Restoring GL..." << llendl; - gGLManager.mIsDisabled = FALSE; - - initGLDefaults(); - LLGLState::restoreGL(); - - gTextureList.restoreGL(); - - // for future support of non-square pixels, and fonts that are properly stretched - //LLFontGL::destroyDefaultFonts(); - initFonts(); - - gSky.restoreGL(); - gPipeline.restoreGL(); - LLDrawPoolWater::restoreGL(); - LLManipTranslate::restoreGL(); - - gBumpImageList.restoreGL(); - LLViewerDynamicTexture::restoreGL(); - LLVOAvatar::restoreGL(); - - gResizeScreenTexture = TRUE; - gWindowResized = TRUE; - - if (isAgentAvatarValid() && !gAgentAvatarp->isUsingBakedTextures()) - { - LLVisualParamHint::requestHintUpdates(); - } - - if (!progress_message.empty()) - { - gRestoreGLTimer.reset(); - gRestoreGL = TRUE; - setShowProgress(TRUE); - setProgressString(progress_message); - } - llinfos << "...Restoring GL done" << llendl; - if(!LLAppViewer::instance()->restoreErrorTrap()) - { - llwarns << " Someone took over my signal/exception handler (post restoreGL)!" << llendl; - } - - } -} - -void LLViewerWindow::initFonts(F32 zoom_factor) -{ - LLFontGL::destroyAllGL(); - // Initialize with possibly different zoom factor - LLFontGL::initClass( gSavedSettings.getF32("FontScreenDPI"), - mDisplayScale.mV[VX] * zoom_factor, - mDisplayScale.mV[VY] * zoom_factor, - gDirUtilp->getAppRODataDir(), - LLUI::getXUIPaths()); - // Force font reloads, which can be very slow - LLFontGL::loadDefaultFonts(); -} - -void LLViewerWindow::requestResolutionUpdate() -{ - mResDirty = true; -} - -void LLViewerWindow::checkSettings() -{ - if (mStatesDirty) - { - gGL.refreshState(); - LLViewerShaderMgr::instance()->setShaders(); - mStatesDirty = false; - } - - // We want to update the resolution AFTER the states getting refreshed not before. - if (mResDirty) - { - reshape(getWindowWidthRaw(), getWindowHeightRaw()); - mResDirty = false; - } -} - -void LLViewerWindow::restartDisplay(BOOL show_progress_bar) -{ - llinfos << "Restaring GL" << llendl; - stopGL(); - if (show_progress_bar) - { - restoreGL(LLTrans::getString("ProgressChangingResolution")); - } - else - { - restoreGL(); - } -} - -BOOL LLViewerWindow::changeDisplaySettings(LLCoordScreen size, BOOL disable_vsync, BOOL show_progress_bar) -{ - //BOOL was_maximized = gSavedSettings.getBOOL("WindowMaximized"); - - //gResizeScreenTexture = TRUE; - - //U32 fsaa = gSavedSettings.getU32("RenderFSAASamples"); - //U32 old_fsaa = mWindow->getFSAASamples(); - - // if not maximized, use the request size - if (!mWindow->getMaximized()) - { - mWindow->setSize(size); - } - - //if (fsaa == old_fsaa) - { - return TRUE; - } - -/* - - // Close floaters that don't handle settings change - LLFloaterReg::hideInstance("snapshot"); - - BOOL result_first_try = FALSE; - BOOL result_second_try = FALSE; - - LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); - send_agent_pause(); - llinfos << "Stopping GL during changeDisplaySettings" << llendl; - stopGL(); - mIgnoreActivate = TRUE; - LLCoordScreen old_size; - LLCoordScreen old_pos; - mWindow->getSize(&old_size); - - //mWindow->setFSAASamples(fsaa); - - result_first_try = mWindow->switchContext(false, size, disable_vsync); - if (!result_first_try) - { - // try to switch back - //mWindow->setFSAASamples(old_fsaa); - result_second_try = mWindow->switchContext(false, old_size, disable_vsync); - - if (!result_second_try) - { - // we are stuck...try once again with a minimal resolution? - send_agent_resume(); - mIgnoreActivate = FALSE; - return FALSE; - } - } - send_agent_resume(); - - llinfos << "Restoring GL during resolution change" << llendl; - if (show_progress_bar) - { - restoreGL(LLTrans::getString("ProgressChangingResolution")); - } - else - { - restoreGL(); - } - - if (!result_first_try) - { - LLSD args; - args["RESX"] = llformat("%d",size.mX); - args["RESY"] = llformat("%d",size.mY); - LLNotificationsUtil::add("ResolutionSwitchFail", args); - size = old_size; // for reshape below - } - - BOOL success = result_first_try || result_second_try; - - if (success) - { - // maximize window if was maximized, else reposition - if (was_maximized) - { - mWindow->maximize(); - } - else - { - S32 windowX = gSavedSettings.getS32("WindowX"); - S32 windowY = gSavedSettings.getS32("WindowY"); - - mWindow->setPosition(LLCoordScreen ( windowX, windowY ) ); - } - } - - mIgnoreActivate = FALSE; - gFocusMgr.setKeyboardFocus(keyboard_focus); - - return success; - - */ -} - -F32 LLViewerWindow::getWorldViewAspectRatio() const -{ - F32 world_aspect = (F32)mWorldViewRectRaw.getWidth() / (F32)mWorldViewRectRaw.getHeight(); - return world_aspect; -} - -void LLViewerWindow::calcDisplayScale() -{ - F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor"); - LLVector2 display_scale; - display_scale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f)); - display_scale *= ui_scale_factor; - - // limit minimum display scale - if (display_scale.mV[VX] < MIN_DISPLAY_SCALE || display_scale.mV[VY] < MIN_DISPLAY_SCALE) - { - display_scale *= MIN_DISPLAY_SCALE / llmin(display_scale.mV[VX], display_scale.mV[VY]); - } - - if (display_scale != mDisplayScale) - { - llinfos << "Setting display scale to " << display_scale << llendl; - - mDisplayScale = display_scale; - // Init default fonts - initFonts(); - } -} - -//static -LLRect LLViewerWindow::calcScaledRect(const LLRect & rect, const LLVector2& display_scale) -{ - LLRect res = rect; - res.mLeft = llround((F32)res.mLeft / display_scale.mV[VX]); - res.mRight = llround((F32)res.mRight / display_scale.mV[VX]); - res.mBottom = llround((F32)res.mBottom / display_scale.mV[VY]); - res.mTop = llround((F32)res.mTop / display_scale.mV[VY]); - - return res; -} - -S32 LLViewerWindow::getChatConsoleBottomPad() -{ - S32 offset = 0; - - if(LLBottomTray::instanceExists()) - offset += LLBottomTray::getInstance()->getRect().getHeight(); - - return offset; -} - -LLRect LLViewerWindow::getChatConsoleRect() -{ - LLRect full_window(0, getWindowHeightScaled(), getWindowWidthScaled(), 0); - LLRect console_rect = full_window; - - const S32 CONSOLE_PADDING_TOP = 24; - const S32 CONSOLE_PADDING_LEFT = 24; - const S32 CONSOLE_PADDING_RIGHT = 10; - - console_rect.mTop -= CONSOLE_PADDING_TOP; - console_rect.mBottom += getChatConsoleBottomPad(); - - console_rect.mLeft += CONSOLE_PADDING_LEFT; - - static const BOOL CHAT_FULL_WIDTH = gSavedSettings.getBOOL("ChatFullWidth"); - - if (CHAT_FULL_WIDTH) - { - console_rect.mRight -= CONSOLE_PADDING_RIGHT; - } - else - { - // Make console rect somewhat narrow so having inventory open is - // less of a problem. - console_rect.mRight = console_rect.mLeft + 2 * getWindowWidthScaled() / 3; - } - - return console_rect; -} -//---------------------------------------------------------------------------- - - -//static -bool LLViewerWindow::onAlert(const LLSD& notify) -{ - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - if (gNoRender) - { - llinfos << "Alert: " << notification->getName() << llendl; - notification->respond(LLSD::emptyMap()); - LLNotifications::instance().cancel(notification); - return false; - } - - // If we're in mouselook, the mouse is hidden and so the user can't click - // the dialog buttons. In that case, change to First Person instead. - if( gAgentCamera.cameraMouselook() ) - { - gAgentCamera.changeCameraToDefault(); - } - return false; -} - -//////////////////////////////////////////////////////////////////////////// -// -// LLPickInfo -// -LLPickInfo::LLPickInfo() - : mKeyMask(MASK_NONE), - mPickCallback(NULL), - mPickType(PICK_INVALID), - mWantSurfaceInfo(FALSE), - mObjectFace(-1), - mUVCoords(-1.f, -1.f), - mSTCoords(-1.f, -1.f), - mXYCoords(-1, -1), - mIntersection(), - mNormal(), - mBinormal(), - mHUDIcon(NULL), - mPickTransparent(FALSE) -{ -} - -LLPickInfo::LLPickInfo(const LLCoordGL& mouse_pos, - MASK keyboard_mask, - BOOL pick_transparent, - BOOL pick_uv_coords, - void (*pick_callback)(const LLPickInfo& pick_info)) - : mMousePt(mouse_pos), - mKeyMask(keyboard_mask), - mPickCallback(pick_callback), - mPickType(PICK_INVALID), - mWantSurfaceInfo(pick_uv_coords), - mObjectFace(-1), - mUVCoords(-1.f, -1.f), - mSTCoords(-1.f, -1.f), - mXYCoords(-1, -1), - mNormal(), - mBinormal(), - mHUDIcon(NULL), - mPickTransparent(pick_transparent) -{ -} - -void LLPickInfo::fetchResults() -{ - - S32 face_hit = -1; - LLVector3 intersection, normal, binormal; - LLVector2 uv; - - LLHUDIcon* hit_icon = gViewerWindow->cursorIntersectIcon(mMousePt.mX, mMousePt.mY, 512.f, &intersection); - - F32 icon_dist = 0.f; - if (hit_icon) - { - icon_dist = (LLViewerCamera::getInstance()->getOrigin()-intersection).magVec(); - } - LLViewerObject* hit_object = gViewerWindow->cursorIntersect(mMousePt.mX, mMousePt.mY, 512.f, - NULL, -1, mPickTransparent, &face_hit, - &intersection, &uv, &normal, &binormal); - - mPickPt = mMousePt; - - U32 te_offset = face_hit > -1 ? face_hit : 0; - - //unproject relative clicked coordinate from window coordinate using GL - - LLViewerObject* objectp = hit_object; - - if (hit_icon && - (!objectp || - icon_dist < (LLViewerCamera::getInstance()->getOrigin()-intersection).magVec())) - { - // was this name referring to a hud icon? - mHUDIcon = hit_icon; - mPickType = PICK_ICON; - mPosGlobal = mHUDIcon->getPositionGlobal(); - } - else if (objectp) - { - if( objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH ) - { - // Hit land - mPickType = PICK_LAND; - mObjectID.setNull(); // land has no id - - // put global position into land_pos - LLVector3d land_pos; - if (!gViewerWindow->mousePointOnLandGlobal(mPickPt.mX, mPickPt.mY, &land_pos)) - { - // The selected point is beyond the draw distance or is otherwise - // not selectable. Return before calling mPickCallback(). - return; - } - - // Fudge the land focus a little bit above ground. - mPosGlobal = land_pos + LLVector3d::z_axis * 0.1f; - } - else - { - if(isFlora(objectp)) - { - mPickType = PICK_FLORA; - } - else - { - mPickType = PICK_OBJECT; - } - mObjectOffset = gAgentCamera.calcFocusOffset(objectp, intersection, mPickPt.mX, mPickPt.mY); - mObjectID = objectp->mID; - mObjectFace = (te_offset == NO_FACE) ? -1 : (S32)te_offset; - - mPosGlobal = gAgent.getPosGlobalFromAgent(intersection); - - if (mWantSurfaceInfo) - { - getSurfaceInfo(); - } - } - } - - if (mPickCallback) - { - mPickCallback(*this); - } -} - -LLPointer LLPickInfo::getObject() const -{ - return gObjectList.findObject( mObjectID ); -} - -void LLPickInfo::updateXYCoords() -{ - if (mObjectFace > -1) - { - const LLTextureEntry* tep = getObject()->getTE(mObjectFace); - LLPointer imagep = LLViewerTextureManager::getFetchedTexture(tep->getID()); - if(mUVCoords.mV[VX] >= 0.f && mUVCoords.mV[VY] >= 0.f && imagep.notNull()) - { - mXYCoords.mX = llround(mUVCoords.mV[VX] * (F32)imagep->getWidth()); - mXYCoords.mY = llround((1.f - mUVCoords.mV[VY]) * (F32)imagep->getHeight()); - } - } -} - -void LLPickInfo::getSurfaceInfo() -{ - // set values to uninitialized - this is what we return if no intersection is found - mObjectFace = -1; - mUVCoords = LLVector2(-1, -1); - mSTCoords = LLVector2(-1, -1); - mXYCoords = LLCoordScreen(-1, -1); - mIntersection = LLVector3(0,0,0); - mNormal = LLVector3(0,0,0); - mBinormal = LLVector3(0,0,0); - - LLViewerObject* objectp = getObject(); - - if (objectp) - { - if (gViewerWindow->cursorIntersect(llround((F32)mMousePt.mX), llround((F32)mMousePt.mY), 1024.f, - objectp, -1, mPickTransparent, - &mObjectFace, - &mIntersection, - &mSTCoords, - &mNormal, - &mBinormal)) - { - // if we succeeded with the intersect above, compute the texture coordinates: - - if (objectp->mDrawable.notNull() && mObjectFace > -1) - { - LLFace* facep = objectp->mDrawable->getFace(mObjectFace); - - mUVCoords = facep->surfaceToTexture(mSTCoords, mIntersection, mNormal); - } - - // and XY coords: - updateXYCoords(); - - } - } -} - - -/* code to get UV via a special UV render - removed in lieu of raycast method -LLVector2 LLPickInfo::pickUV() -{ - LLVector2 result(-1.f, -1.f); - - LLViewerObject* objectp = getObject(); - if (!objectp) - { - return result; - } - - if (mObjectFace > -1 && - objectp->mDrawable.notNull() && objectp->getPCode() == LL_PCODE_VOLUME && - mObjectFace < objectp->mDrawable->getNumFaces()) - { - S32 scaled_x = llround((F32)mPickPt.mX * gViewerWindow->getDisplayScale().mV[VX]); - S32 scaled_y = llround((F32)mPickPt.mY * gViewerWindow->getDisplayScale().mV[VY]); - const S32 UV_PICK_WIDTH = 5; - const S32 UV_PICK_HALF_WIDTH = (UV_PICK_WIDTH - 1) / 2; - U8 uv_pick_buffer[UV_PICK_WIDTH * UV_PICK_WIDTH * 4]; - LLFace* facep = objectp->mDrawable->getFace(mObjectFace); - if (facep) - { - LLGLState scissor_state(GL_SCISSOR_TEST); - scissor_state.enable(); - LLViewerCamera::getInstance()->setPerspective(FOR_SELECTION, scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, FALSE); - //glViewport(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH); - glScissor(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH); - - glClear(GL_DEPTH_BUFFER_BIT); - - facep->renderSelectedUV(); - - glReadPixels(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, GL_RGBA, GL_UNSIGNED_BYTE, uv_pick_buffer); - U8* center_pixel = &uv_pick_buffer[4 * ((UV_PICK_WIDTH * UV_PICK_HALF_WIDTH) + UV_PICK_HALF_WIDTH + 1)]; - - result.mV[VX] = (F32)((center_pixel[VGREEN] & 0xf) + (16.f * center_pixel[VRED])) / 4095.f; - result.mV[VY] = (F32)((center_pixel[VGREEN] >> 4) + (16.f * center_pixel[VBLUE])) / 4095.f; - } - } - - return result; -} */ - - -//static -bool LLPickInfo::isFlora(LLViewerObject* object) -{ - if (!object) return false; - - LLPCode pcode = object->getPCode(); - - if( (LL_PCODE_LEGACY_GRASS == pcode) - || (LL_PCODE_LEGACY_TREE == pcode) - || (LL_PCODE_TREE_NEW == pcode)) - { - return true; - } - return false; -} +/** + * @file llviewerwindow.cpp + * @brief Implementation of the LLViewerWindow class. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llviewerwindow.h" + +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + +// system library includes +#include +#include +#include +#include + +#include "llagent.h" +#include "llagentcamera.h" +#include "llfloaterreg.h" +#include "llpanellogin.h" +#include "llviewerkeyboard.h" +#include "llviewermenu.h" + +#include "llviewquery.h" +#include "llxmltree.h" +#include "llslurl.h" +//#include "llviewercamera.h" +#include "llrender.h" + +#include "llvoiceclient.h" // for push-to-talk button handling + +// +// TODO: Many of these includes are unnecessary. Remove them. +// + +// linden library includes +#include "llaudioengine.h" // mute on minimize +#include "indra_constants.h" +#include "llassetstorage.h" +#include "llerrorcontrol.h" +#include "llfontgl.h" +#include "llmousehandler.h" +#include "llrect.h" +#include "llsky.h" +#include "llstring.h" +#include "llui.h" +#include "lluuid.h" +#include "llview.h" +#include "llxfermanager.h" +#include "message.h" +#include "object_flags.h" +#include "lltimer.h" +#include "timing.h" +#include "llviewermenu.h" +#include "lltooltip.h" +#include "llmediaentry.h" +#include "llurldispatcher.h" + +// newview includes +#include "llagent.h" +#include "llbox.h" +#include "llconsole.h" +#include "llviewercontrol.h" +#include "llcylinder.h" +#include "lldebugview.h" +#include "lldir.h" +#include "lldrawable.h" +#include "lldrawpoolalpha.h" +#include "lldrawpoolbump.h" +#include "lldrawpoolwater.h" +#include "llmaniptranslate.h" +#include "llface.h" +#include "llfeaturemanager.h" +#include "llfilepicker.h" +#include "llfirstuse.h" +#include "llfloater.h" +#include "llfloaterbuildoptions.h" +#include "llfloaterbuyland.h" +#include "llfloatercamera.h" +#include "llfloaterland.h" +#include "llfloaterinspect.h" +#include "llfloatermap.h" +#include "llfloaternamedesc.h" +#include "llfloaterpreference.h" +#include "llfloatersnapshot.h" +#include "llfloatertools.h" +#include "llfloaterworldmap.h" +#include "llfocusmgr.h" +#include "llfontfreetype.h" +#include "llgesturemgr.h" +#include "llglheaders.h" +#include "lltooltip.h" +#include "llhudmanager.h" +#include "llhudobject.h" +#include "llhudview.h" +#include "llimagebmp.h" +#include "llimagej2c.h" +#include "llimageworker.h" +#include "llkeyboard.h" +#include "lllineeditor.h" +#include "llmenugl.h" +#include "llmodaldialog.h" +#include "llmorphview.h" +#include "llmoveview.h" +#include "llnavigationbar.h" +#include "llpopupview.h" +#include "llpreviewtexture.h" +#include "llprogressview.h" +#include "llresmgr.h" +#include "llsidetray.h" +#include "llselectmgr.h" +#include "llrootview.h" +#include "llrendersphere.h" +#include "llstartup.h" +#include "llstatusbar.h" +#include "llstatview.h" +#include "llsurface.h" +#include "llsurfacepatch.h" +#include "lltexlayer.h" +#include "lltextbox.h" +#include "lltexturecache.h" +#include "lltexturefetch.h" +#include "lltextureview.h" +#include "lltool.h" +#include "lltoolcomp.h" +#include "lltooldraganddrop.h" +#include "lltoolface.h" +#include "lltoolfocus.h" +#include "lltoolgrab.h" +#include "lltoolmgr.h" +#include "lltoolmorph.h" +#include "lltoolpie.h" +#include "lltoolselectland.h" +#include "lltrans.h" +#include "lluictrlfactory.h" +#include "llurldispatcher.h" // SLURL from other app instance +#include "llversioninfo.h" +#include "llvieweraudio.h" +#include "llviewercamera.h" +#include "llviewergesture.h" +#include "llviewertexturelist.h" +#include "llviewerinventory.h" +#include "llviewerkeyboard.h" +#include "llviewermedia.h" +#include "llviewermediafocus.h" +#include "llviewermenu.h" +#include "llviewermessage.h" +#include "llviewerobjectlist.h" +#include "llviewerparcelmgr.h" +#include "llviewerregion.h" +#include "llviewershadermgr.h" +#include "llviewerstats.h" +#include "llvoavatarself.h" +#include "llvovolume.h" +#include "llworld.h" +#include "llworldmapview.h" +#include "pipeline.h" +#include "llappviewer.h" +#include "llviewerdisplay.h" +#include "llspatialpartition.h" +#include "llviewerjoystick.h" +#include "llviewernetwork.h" +#include "llpostprocess.h" +#include "llbottomtray.h" +#include "llnearbychatbar.h" +#include "llagentui.h" +#include "llwearablelist.h" + +#include "llnotifications.h" +#include "llnotificationsutil.h" +#include "llnotificationmanager.h" + +#include "llfloaternotificationsconsole.h" + +#include "llnearbychat.h" +#include "llviewerwindowlistener.h" +#include "llpaneltopinfobar.h" + +#if LL_WINDOWS +#include // For Unicode conversion methods +#endif + +// +// Globals +// +void render_ui(F32 zoom_factor = 1.f, int subfield = 0); + +extern BOOL gDebugClicks; +extern BOOL gDisplaySwapBuffers; +extern BOOL gDepthDirty; +extern BOOL gResizeScreenTexture; + +LLViewerWindow *gViewerWindow = NULL; + +LLFrameTimer gAwayTimer; +LLFrameTimer gAwayTriggerTimer; + +BOOL gShowOverlayTitle = FALSE; + +LLViewerObject* gDebugRaycastObject = NULL; +LLVector3 gDebugRaycastIntersection; +LLVector2 gDebugRaycastTexCoord; +LLVector3 gDebugRaycastNormal; +LLVector3 gDebugRaycastBinormal; +S32 gDebugRaycastFaceHit; + +// HUD display lines in lower right +BOOL gDisplayWindInfo = FALSE; +BOOL gDisplayCameraPos = FALSE; +BOOL gDisplayFOV = FALSE; +BOOL gDisplayBadge = FALSE; + +S32 CHAT_BAR_HEIGHT = 28; +S32 OVERLAY_BAR_HEIGHT = 20; + +const U8 NO_FACE = 255; +BOOL gQuietSnapshot = FALSE; + +const F32 MIN_AFK_TIME = 2.f; // minimum time after setting away state before coming back +const F32 MAX_FAST_FRAME_TIME = 0.5f; +const F32 FAST_FRAME_INCREMENT = 0.1f; + +const F32 MIN_DISPLAY_SCALE = 0.75f; + +std::string LLViewerWindow::sSnapshotBaseName; +std::string LLViewerWindow::sSnapshotDir; + +std::string LLViewerWindow::sMovieBaseName; + +class RecordToChatConsole : public LLError::Recorder, public LLSingleton +{ +public: + virtual void recordMessage(LLError::ELevel level, + const std::string& message) + { + //FIXME: this is NOT thread safe, and will do bad things when a warning is issued from a non-UI thread + + // only log warnings to chat console + //if (level == LLError::LEVEL_WARN) + //{ + //LLFloaterChat* chat_floater = LLFloaterReg::findTypedInstance("chat"); + //if (chat_floater && gSavedSettings.getBOOL("WarningsAsChat")) + //{ + // LLChat chat; + // chat.mText = message; + // chat.mSourceType = CHAT_SOURCE_SYSTEM; + + // chat_floater->addChat(chat, FALSE, FALSE); + //} + //} + } +}; + +//////////////////////////////////////////////////////////////////////////// +// +// LLDebugText +// + +class LLDebugText +{ +private: + struct Line + { + Line(const std::string& in_text, S32 in_x, S32 in_y) : text(in_text), x(in_x), y(in_y) {} + std::string text; + S32 x,y; + }; + + LLViewerWindow *mWindow; + + typedef std::vector line_list_t; + line_list_t mLineList; + LLColor4 mTextColor; + + void addText(S32 x, S32 y, const std::string &text) + { + mLineList.push_back(Line(text, x, y)); + } + + void clearText() { mLineList.clear(); } + +public: + LLDebugText(LLViewerWindow* window) : mWindow(window) {} + + void update() + { + static LLCachedControl log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; + + std::string wind_vel_text; + std::string wind_vector_text; + std::string rwind_vel_text; + std::string rwind_vector_text; + std::string audio_text; + + static const std::string beacon_particle = LLTrans::getString("BeaconParticle"); + static const std::string beacon_physical = LLTrans::getString("BeaconPhysical"); + static const std::string beacon_scripted = LLTrans::getString("BeaconScripted"); + static const std::string beacon_scripted_touch = LLTrans::getString("BeaconScriptedTouch"); + static const std::string beacon_sound = LLTrans::getString("BeaconSound"); + static const std::string beacon_media = LLTrans::getString("BeaconMedia"); + static const std::string particle_hiding = LLTrans::getString("ParticleHiding"); + + // Draw the statistics in a light gray + // and in a thin font + mTextColor = LLColor4( 0.86f, 0.86f, 0.86f, 1.f ); + + // Draw stuff growing up from right lower corner of screen + U32 xpos = mWindow->getWindowWidthScaled() - 350; + U32 ypos = 64; + const U32 y_inc = 20; + + clearText(); + + if (gSavedSettings.getBOOL("DebugShowTime")) + { + const U32 y_inc2 = 15; + for (std::map::reverse_iterator iter = gDebugTimers.rbegin(); + iter != gDebugTimers.rend(); ++iter) + { + S32 idx = iter->first; + LLFrameTimer& timer = iter->second; + F32 time = timer.getElapsedTimeF32(); + S32 hours = (S32)(time / (60*60)); + S32 mins = (S32)((time - hours*(60*60)) / 60); + S32 secs = (S32)((time - hours*(60*60) - mins*60)); + std::string label = gDebugTimerLabel[idx]; + if (label.empty()) label = llformat("Debug: %d", idx); + addText(xpos, ypos, llformat(" %s: %d:%02d:%02d", label.c_str(), hours,mins,secs)); ypos += y_inc2; + } + + F32 time = gFrameTimeSeconds; + S32 hours = (S32)(time / (60*60)); + S32 mins = (S32)((time - hours*(60*60)) / 60); + S32 secs = (S32)((time - hours*(60*60) - mins*60)); + addText(xpos, ypos, llformat("Time: %d:%02d:%02d", hours,mins,secs)); ypos += y_inc; + } + +#if LL_WINDOWS + if (gSavedSettings.getBOOL("DebugShowMemory")) + { + addText(xpos, ypos, llformat("Memory: %d (KB)", LLMemory::getWorkingSetSize() / 1024)); + ypos += y_inc; + } +#endif + + if (gDisplayCameraPos) + { + std::string camera_view_text; + std::string camera_center_text; + std::string agent_view_text; + std::string agent_left_text; + std::string agent_center_text; + std::string agent_root_center_text; + + LLVector3d tvector; // Temporary vector to hold data for printing. + + // Update camera center, camera view, wind info every other frame + tvector = gAgent.getPositionGlobal(); + agent_center_text = llformat("AgentCenter %f %f %f", + (F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ])); + + if (isAgentAvatarValid()) + { + tvector = gAgent.getPosGlobalFromAgent(gAgentAvatarp->mRoot.getWorldPosition()); + agent_root_center_text = llformat("AgentRootCenter %f %f %f", + (F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ])); + } + else + { + agent_root_center_text = "---"; + } + + + tvector = LLVector4(gAgent.getFrameAgent().getAtAxis()); + agent_view_text = llformat("AgentAtAxis %f %f %f", + (F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ])); + + tvector = LLVector4(gAgent.getFrameAgent().getLeftAxis()); + agent_left_text = llformat("AgentLeftAxis %f %f %f", + (F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ])); + + tvector = gAgentCamera.getCameraPositionGlobal(); + camera_center_text = llformat("CameraCenter %f %f %f", + (F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ])); + + tvector = LLVector4(LLViewerCamera::getInstance()->getAtAxis()); + camera_view_text = llformat("CameraAtAxis %f %f %f", + (F32)(tvector.mdV[VX]), (F32)(tvector.mdV[VY]), (F32)(tvector.mdV[VZ])); + + addText(xpos, ypos, agent_center_text); ypos += y_inc; + addText(xpos, ypos, agent_root_center_text); ypos += y_inc; + addText(xpos, ypos, agent_view_text); ypos += y_inc; + addText(xpos, ypos, agent_left_text); ypos += y_inc; + addText(xpos, ypos, camera_center_text); ypos += y_inc; + addText(xpos, ypos, camera_view_text); ypos += y_inc; + } + + if (gDisplayWindInfo) + { + wind_vel_text = llformat("Wind velocity %.2f m/s", gWindVec.magVec()); + wind_vector_text = llformat("Wind vector %.2f %.2f %.2f", gWindVec.mV[0], gWindVec.mV[1], gWindVec.mV[2]); + rwind_vel_text = llformat("RWind vel %.2f m/s", gRelativeWindVec.magVec()); + rwind_vector_text = llformat("RWind vec %.2f %.2f %.2f", gRelativeWindVec.mV[0], gRelativeWindVec.mV[1], gRelativeWindVec.mV[2]); + + addText(xpos, ypos, wind_vel_text); ypos += y_inc; + addText(xpos, ypos, wind_vector_text); ypos += y_inc; + addText(xpos, ypos, rwind_vel_text); ypos += y_inc; + addText(xpos, ypos, rwind_vector_text); ypos += y_inc; + } + if (gDisplayWindInfo) + { + if (gAudiop) + { + audio_text= llformat("Audio for wind: %d", gAudiop->isWindEnabled()); + } + addText(xpos, ypos, audio_text); ypos += y_inc; + } + if (gDisplayFOV) + { + addText(xpos, ypos, llformat("FOV: %2.1f deg", RAD_TO_DEG * LLViewerCamera::getInstance()->getView())); + ypos += y_inc; + } + if (gDisplayBadge) + { + addText(xpos, ypos+(y_inc/2), llformat("Hippos!", RAD_TO_DEG * LLViewerCamera::getInstance()->getView())); + ypos += y_inc * 2; + } + + /*if (LLViewerJoystick::getInstance()->getOverrideCamera()) + { + addText(xpos + 200, ypos, llformat("Flycam")); + ypos += y_inc; + }*/ + + if (gSavedSettings.getBOOL("DebugShowRenderInfo")) + { + if (gPipeline.getUseVertexShaders() == 0) + { + addText(xpos, ypos, "Shaders Disabled"); + ypos += y_inc; + } + addText(xpos, ypos, llformat("%d MB Vertex Data", LLVertexBuffer::sAllocatedBytes/(1024*1024))); + ypos += y_inc; + + addText(xpos, ypos, llformat("%d Vertex Buffers", LLVertexBuffer::sGLCount)); + ypos += y_inc; + + addText(xpos, ypos, llformat("%d Mapped Buffers", LLVertexBuffer::sMappedCount)); + ypos += y_inc; + + addText(xpos, ypos, llformat("%d Vertex Buffer Binds", LLVertexBuffer::sBindCount)); + ypos += y_inc; + + addText(xpos, ypos, llformat("%d Vertex Buffer Sets", LLVertexBuffer::sSetCount)); + ypos += y_inc; + + addText(xpos, ypos, llformat("%d Texture Binds", LLImageGL::sBindCount)); + ypos += y_inc; + + addText(xpos, ypos, llformat("%d Unique Textures", LLImageGL::sUniqueCount)); + ypos += y_inc; + + addText(xpos, ypos, llformat("%d Render Calls", gPipeline.mBatchCount)); + ypos += y_inc; + + addText(xpos, ypos, llformat("%d Matrix Ops", gPipeline.mMatrixOpCount)); + ypos += y_inc; + + addText(xpos, ypos, llformat("%d Texture Matrix Ops", gPipeline.mTextureMatrixOps)); + ypos += y_inc; + + gPipeline.mTextureMatrixOps = 0; + gPipeline.mMatrixOpCount = 0; + + if (gPipeline.mBatchCount > 0) + { + addText(xpos, ypos, llformat("Batch min/max/mean: %d/%d/%d", gPipeline.mMinBatchSize, gPipeline.mMaxBatchSize, + gPipeline.mTrianglesDrawn/gPipeline.mBatchCount)); + + gPipeline.mMinBatchSize = gPipeline.mMaxBatchSize; + gPipeline.mMaxBatchSize = 0; + gPipeline.mBatchCount = 0; + } + ypos += y_inc; + + addText(xpos, ypos, llformat("UI Verts/Calls: %d/%d", LLRender::sUIVerts, LLRender::sUICalls)); + LLRender::sUICalls = LLRender::sUIVerts = 0; + ypos += y_inc; + + addText(xpos,ypos, llformat("%d/%d Nodes visible", gPipeline.mNumVisibleNodes, LLSpatialGroup::sNodeCount)); + + ypos += y_inc; + + + addText(xpos,ypos, llformat("%d Avatars visible", LLVOAvatar::sNumVisibleAvatars)); + + ypos += y_inc; + + addText(xpos,ypos, llformat("%d Lights visible", LLPipeline::sVisibleLightCount)); + + ypos += y_inc; + + LLVertexBuffer::sBindCount = LLImageGL::sBindCount = + LLVertexBuffer::sSetCount = LLImageGL::sUniqueCount = + gPipeline.mNumVisibleNodes = LLPipeline::sVisibleLightCount = 0; + } + if (gSavedSettings.getBOOL("DebugShowRenderMatrices")) + { + addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLProjection[12], gGLProjection[13], gGLProjection[14], gGLProjection[15])); + ypos += y_inc; + + addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLProjection[8], gGLProjection[9], gGLProjection[10], gGLProjection[11])); + ypos += y_inc; + + addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLProjection[4], gGLProjection[5], gGLProjection[6], gGLProjection[7])); + ypos += y_inc; + + addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLProjection[0], gGLProjection[1], gGLProjection[2], gGLProjection[3])); + ypos += y_inc; + + addText(xpos, ypos, "Projection Matrix"); + ypos += y_inc; + + + addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLModelView[12], gGLModelView[13], gGLModelView[14], gGLModelView[15])); + ypos += y_inc; + + addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLModelView[8], gGLModelView[9], gGLModelView[10], gGLModelView[11])); + ypos += y_inc; + + addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLModelView[4], gGLModelView[5], gGLModelView[6], gGLModelView[7])); + ypos += y_inc; + + addText(xpos, ypos, llformat("%.4f .%4f %.4f %.4f", gGLModelView[0], gGLModelView[1], gGLModelView[2], gGLModelView[3])); + ypos += y_inc; + + addText(xpos, ypos, "View Matrix"); + ypos += y_inc; + } + if (gSavedSettings.getBOOL("DebugShowColor")) + { + U8 color[4]; + LLCoordGL coord = gViewerWindow->getCurrentMouse(); + glReadPixels(coord.mX, coord.mY, 1,1,GL_RGBA, GL_UNSIGNED_BYTE, color); + addText(xpos, ypos, llformat("%d %d %d %d", color[0], color[1], color[2], color[3])); + ypos += y_inc; + } + // only display these messages if we are actually rendering beacons at this moment + if (LLPipeline::getRenderBeacons(NULL) && LLFloaterReg::instanceVisible("beacons")) + { + if (LLPipeline::getRenderParticleBeacons(NULL)) + { + addText(xpos, ypos, beacon_particle); + ypos += y_inc; + } + if (LLPipeline::toggleRenderTypeControlNegated((void*)LLPipeline::RENDER_TYPE_PARTICLES)) + { + addText(xpos, ypos, particle_hiding); + ypos += y_inc; + } + if (LLPipeline::getRenderPhysicalBeacons(NULL)) + { + addText(xpos, ypos, beacon_physical); + ypos += y_inc; + } + if (LLPipeline::getRenderScriptedBeacons(NULL)) + { + addText(xpos, ypos, beacon_scripted); + ypos += y_inc; + } + else + if (LLPipeline::getRenderScriptedTouchBeacons(NULL)) + { + addText(xpos, ypos, beacon_scripted_touch); + ypos += y_inc; + } + if (LLPipeline::getRenderSoundBeacons(NULL)) + { + addText(xpos, ypos, beacon_sound); + ypos += y_inc; + } + } + if(log_texture_traffic) + { + U32 old_y = ypos ; + for(S32 i = LLViewerTexture::BOOST_NONE; i < LLViewerTexture::MAX_GL_IMAGE_CATEGORY; i++) + { + if(gTotalTextureBytesPerBoostLevel[i] > 0) + { + addText(xpos, ypos, llformat("Boost_Level %d: %.3f MB", i, (F32)gTotalTextureBytesPerBoostLevel[i] / (1024 * 1024))); + ypos += y_inc; + } + } + if(ypos != old_y) + { + addText(xpos, ypos, "Network traffic for textures:"); + ypos += y_inc; + } + } + + if (gSavedSettings.getBOOL("DebugShowTextureInfo")) + { + LLViewerObject* objectp = NULL ; + //objectp = = gAgentCamera.getFocusObject(); + + LLSelectNode* nodep = LLSelectMgr::instance().getHoverNode(); + if (nodep) + { + objectp = nodep->getObject(); + } + if (objectp && !objectp->isDead()) + { + S32 num_faces = objectp->mDrawable->getNumFaces() ; + + for(S32 i = 0 ; i < num_faces; i++) + { + LLFace* facep = objectp->mDrawable->getFace(i) ; + if(facep) + { + //addText(xpos, ypos, llformat("ts_min: %.3f ts_max: %.3f tt_min: %.3f tt_max: %.3f", facep->mTexExtents[0].mV[0], facep->mTexExtents[1].mV[0], + // facep->mTexExtents[0].mV[1], facep->mTexExtents[1].mV[1])); + //ypos += y_inc; + + addText(xpos, ypos, llformat("v_size: %.3f: p_size: %.3f", facep->getVirtualSize(), facep->getPixelArea())); + ypos += y_inc; + + //const LLTextureEntry *tep = facep->getTextureEntry(); + //if(tep) + //{ + // addText(xpos, ypos, llformat("scale_s: %.3f: scale_t: %.3f", tep->mScaleS, tep->mScaleT)) ; + // ypos += y_inc; + //} + + LLViewerTexture* tex = facep->getTexture() ; + if(tex) + { + addText(xpos, ypos, llformat("ID: %s v_size: %.3f", tex->getID().asString().c_str(), tex->getMaxVirtualSize())); + ypos += y_inc; + } + } + } + } + } + } + + void draw() + { + for (line_list_t::iterator iter = mLineList.begin(); + iter != mLineList.end(); ++iter) + { + const Line& line = *iter; + LLFontGL::getFontMonospace()->renderUTF8(line.text, 0, (F32)line.x, (F32)line.y, mTextColor, + LLFontGL::LEFT, LLFontGL::TOP, + LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); + } + mLineList.clear(); + } + +}; + +void LLViewerWindow::updateDebugText() +{ + mDebugText->update(); +} + +//////////////////////////////////////////////////////////////////////////// +// +// LLViewerWindow +// + +BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) +{ + const char* buttonname = ""; + const char* buttonstatestr = ""; + S32 x = pos.mX; + S32 y = pos.mY; + x = llround((F32)x / mDisplayScale.mV[VX]); + y = llround((F32)y / mDisplayScale.mV[VY]); + + // only send mouse clicks to UI if UI is visible + if(gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + + if (down) + { + buttonstatestr = "down" ; + } + else + { + buttonstatestr = "up" ; + } + + switch (clicktype) + { + case LLMouseHandler::CLICK_LEFT: + mLeftMouseDown = down; + buttonname = "Left"; + break; + case LLMouseHandler::CLICK_RIGHT: + mRightMouseDown = down; + buttonname = "Right"; + break; + case LLMouseHandler::CLICK_MIDDLE: + mMiddleMouseDown = down; + buttonname = "Middle"; + break; + case LLMouseHandler::CLICK_DOUBLELEFT: + mLeftMouseDown = down; + buttonname = "Left Double Click"; + break; + } + + LLView::sMouseHandlerMessage.clear(); + + if (gMenuBarView) + { + // stop ALT-key access to menu + gMenuBarView->resetMenuTrigger(); + } + + if (gDebugClicks) + { + llinfos << "ViewerWindow " << buttonname << " mouse " << buttonstatestr << " at " << x << "," << y << llendl; + } + + // Make sure we get a corresponding mouseup event, even if the mouse leaves the window + if (down) + mWindow->captureMouse(); + else + mWindow->releaseMouse(); + + // Indicate mouse was active + LLUI::resetMouseIdleTimer(); + + // Don't let the user move the mouse out of the window until mouse up. + if( LLToolMgr::getInstance()->getCurrentTool()->clipMouseWhenDown() ) + { + mWindow->setMouseClipping(down); + } + + LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); + if( mouse_captor ) + { + S32 local_x; + S32 local_y; + mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); + if (LLView::sDebugMouseHandling) + { + llinfos << buttonname << " Mouse " << buttonstatestr << " handled by captor " << mouse_captor->getName() << llendl; + } + return mouse_captor->handleAnyMouseClick(local_x, local_y, mask, clicktype, down); + } + + // Topmost view gets a chance before the hierarchy + //LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); + //if (top_ctrl) + //{ + // S32 local_x, local_y; + // top_ctrl->screenPointToLocal( x, y, &local_x, &local_y ); + // if (top_ctrl->pointInView(local_x, local_y)) + // { + // return top_ctrl->handleAnyMouseClick(local_x, local_y, mask, clicktype, down) ; + // } + // else + // { + // if (down) + // { + // gFocusMgr.setTopCtrl(NULL); + // } + // } + //} + + // Give the UI views a chance to process the click + if( mRootView->handleAnyMouseClick(x, y, mask, clicktype, down) ) + { + if (LLView::sDebugMouseHandling) + { + llinfos << buttonname << " Mouse " << buttonstatestr << " " << LLView::sMouseHandlerMessage << llendl; + } + return TRUE; + } + else if (LLView::sDebugMouseHandling) + { + llinfos << buttonname << " Mouse " << buttonstatestr << " not handled by view" << llendl; + } + } + + // Do not allow tool manager to handle mouseclicks if we have disconnected + if(!gDisconnected && LLToolMgr::getInstance()->getCurrentTool()->handleAnyMouseClick( x, y, mask, clicktype, down ) ) + { + return TRUE; + } + + + // If we got this far on a down-click, it wasn't handled. + // Up-clicks, though, are always handled as far as the OS is concerned. + BOOL default_rtn = !down; + return default_rtn; +} + +BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) +{ + BOOL down = TRUE; + return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down); +} + +BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask) +{ + // try handling as a double-click first, then a single-click if that + // wasn't handled. + BOOL down = TRUE; + if (handleAnyMouseClick(window, pos, mask, + LLMouseHandler::CLICK_DOUBLELEFT, down)) + { + return TRUE; + } + return handleMouseDown(window, pos, mask); +} + +BOOL LLViewerWindow::handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) +{ + BOOL down = FALSE; + return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down); +} + + +BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) +{ + S32 x = pos.mX; + S32 y = pos.mY; + x = llround((F32)x / mDisplayScale.mV[VX]); + y = llround((F32)y / mDisplayScale.mV[VY]); + + BOOL down = TRUE; + BOOL handle = handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down); + if (handle) + return handle; + + // *HACK: this should be rolled into the composite tool logic, not + // hardcoded at the top level. + if (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgentCamera.getCameraMode() && LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance()) + { + // If the current tool didn't process the click, we should show + // the pie menu. This can be done by passing the event to the pie + // menu tool. + LLToolPie::getInstance()->handleRightMouseDown(x, y, mask); + // show_context_menu( x, y, mask ); + } + + return TRUE; +} + +BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) +{ + BOOL down = FALSE; + return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down); +} + +BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) +{ + BOOL down = TRUE; + LLVoiceClient::getInstance()->middleMouseState(true); + handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); + + // Always handled as far as the OS is concerned. + return TRUE; +} + +LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data) +{ + LLWindowCallbacks::DragNDropResult result = LLWindowCallbacks::DND_NONE; + + const bool prim_media_dnd_enabled = gSavedSettings.getBOOL("PrimMediaDragNDrop"); + const bool slurl_dnd_enabled = gSavedSettings.getBOOL("SLURLDragNDrop"); + + if ( prim_media_dnd_enabled || slurl_dnd_enabled ) + { + switch(action) + { + // Much of the handling for these two cases is the same. + case LLWindowCallbacks::DNDA_TRACK: + case LLWindowCallbacks::DNDA_DROPPED: + case LLWindowCallbacks::DNDA_START_TRACKING: + { + bool drop = (LLWindowCallbacks::DNDA_DROPPED == action); + + if (slurl_dnd_enabled) + { + LLSLURL dropped_slurl(data); + if(dropped_slurl.isSpatial()) + { + if (drop) + { + LLURLDispatcher::dispatch( dropped_slurl.getSLURLString(), NULL, true ); + return LLWindowCallbacks::DND_MOVE; + } + return LLWindowCallbacks::DND_COPY; + } + } + + if (prim_media_dnd_enabled) + { + LLPickInfo pick_info = pickImmediate( pos.mX, pos.mY, TRUE /*BOOL pick_transparent*/ ); + + LLUUID object_id = pick_info.getObjectID(); + S32 object_face = pick_info.mObjectFace; + std::string url = data; + + lldebugs << "Object: picked at " << pos.mX << ", " << pos.mY << " - face = " << object_face << " - URL = " << url << llendl; + + LLVOVolume *obj = dynamic_cast(static_cast(pick_info.getObject())); + + if (obj && !obj->getRegion()->getCapability("ObjectMedia").empty()) + { + LLTextureEntry *te = obj->getTE(object_face); + + // can modify URL if we can modify the object or we have navigate permissions + bool allow_modify_url = obj->permModify() || obj->hasMediaPermission( te->getMediaData(), LLVOVolume::MEDIA_PERM_INTERACT ); + + if (te && allow_modify_url ) + { + if (drop) + { + // object does NOT have media already + if ( ! te->hasMedia() ) + { + // we are allowed to modify the object + if ( obj->permModify() ) + { + // Create new media entry + LLSD media_data; + // XXX Should we really do Home URL too? + media_data[LLMediaEntry::HOME_URL_KEY] = url; + media_data[LLMediaEntry::CURRENT_URL_KEY] = url; + media_data[LLMediaEntry::AUTO_PLAY_KEY] = true; + obj->syncMediaData(object_face, media_data, true, true); + // XXX This shouldn't be necessary, should it ?!? + if (obj->getMediaImpl(object_face)) + obj->getMediaImpl(object_face)->navigateReload(); + obj->sendMediaDataUpdate(); + + result = LLWindowCallbacks::DND_COPY; + } + } + else + // object HAS media already + { + // URL passes the whitelist + if (te->getMediaData()->checkCandidateUrl( url ) ) + { + // just navigate to the URL + if (obj->getMediaImpl(object_face)) + { + obj->getMediaImpl(object_face)->navigateTo(url); + } + else + { + // This is very strange. Navigation should + // happen via the Impl, but we don't have one. + // This sends it to the server, which /should/ + // trigger us getting it. Hopefully. + LLSD media_data; + media_data[LLMediaEntry::CURRENT_URL_KEY] = url; + obj->syncMediaData(object_face, media_data, true, true); + obj->sendMediaDataUpdate(); + } + result = LLWindowCallbacks::DND_LINK; + + } + } + LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); + mDragHoveredObject = NULL; + + } + else + { + // Check the whitelist, if there's media (otherwise just show it) + if (te->getMediaData() == NULL || te->getMediaData()->checkCandidateUrl(url)) + { + if ( obj != mDragHoveredObject) + { + // Highlight the dragged object + LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); + mDragHoveredObject = obj; + LLSelectMgr::getInstance()->highlightObjectOnly(mDragHoveredObject); + } + result = (! te->hasMedia()) ? LLWindowCallbacks::DND_COPY : LLWindowCallbacks::DND_LINK; + + } + } + } + } + } + } + break; + + case LLWindowCallbacks::DNDA_STOP_TRACKING: + // The cleanup case below will make sure things are unhilighted if necessary. + break; + } + + if (prim_media_dnd_enabled && + result == LLWindowCallbacks::DND_NONE && !mDragHoveredObject.isNull()) + { + LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); + mDragHoveredObject = NULL; + } + } + + return result; +} + +BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) +{ + BOOL down = FALSE; + LLVoiceClient::getInstance()->middleMouseState(false); + handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); + + // Always handled as far as the OS is concerned. + return TRUE; +} + +// WARNING: this is potentially called multiple times per frame +void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask) +{ + S32 x = pos.mX; + S32 y = pos.mY; + + x = llround((F32)x / mDisplayScale.mV[VX]); + y = llround((F32)y / mDisplayScale.mV[VY]); + + mMouseInWindow = TRUE; + + // Save mouse point for access during idle() and display() + + LLCoordGL mouse_point(x, y); + + if (mouse_point != mCurrentMousePoint) + { + LLUI::resetMouseIdleTimer(); + } + + saveLastMouse(mouse_point); + + mWindow->showCursorFromMouseMove(); + + if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME) + { + gAgent.clearAFK(); + } +} + +void LLViewerWindow::handleMouseLeave(LLWindow *window) +{ + // Note: we won't get this if we have captured the mouse. + llassert( gFocusMgr.getMouseCapture() == NULL ); + mMouseInWindow = FALSE; + LLToolTipMgr::instance().blockToolTips(); +} + +BOOL LLViewerWindow::handleCloseRequest(LLWindow *window) +{ + // User has indicated they want to close, but we may need to ask + // about modified documents. + LLAppViewer::instance()->userQuit(); + // Don't quit immediately + return FALSE; +} + +void LLViewerWindow::handleQuit(LLWindow *window) +{ + LLAppViewer::instance()->forceQuit(); +} + +void LLViewerWindow::handleResize(LLWindow *window, S32 width, S32 height) +{ + reshape(width, height); + mResDirty = true; +} + +// The top-level window has gained focus (e.g. via ALT-TAB) +void LLViewerWindow::handleFocus(LLWindow *window) +{ + gFocusMgr.setAppHasFocus(TRUE); + LLModalDialog::onAppFocusGained(); + + gAgent.onAppFocusGained(); + LLToolMgr::getInstance()->onAppFocusGained(); + + // See if we're coming in with modifier keys held down + if (gKeyboard) + { + gKeyboard->resetMaskKeys(); + } + + // resume foreground running timer + // since we artifically limit framerate when not frontmost + gForegroundTime.unpause(); +} + +// The top-level window has lost focus (e.g. via ALT-TAB) +void LLViewerWindow::handleFocusLost(LLWindow *window) +{ + gFocusMgr.setAppHasFocus(FALSE); + //LLModalDialog::onAppFocusLost(); + LLToolMgr::getInstance()->onAppFocusLost(); + gFocusMgr.setMouseCapture( NULL ); + + if (gMenuBarView) + { + // stop ALT-key access to menu + gMenuBarView->resetMenuTrigger(); + } + + // restore mouse cursor + showCursor(); + getWindow()->setMouseClipping(FALSE); + + // If losing focus while keys are down, reset them. + if (gKeyboard) + { + gKeyboard->resetKeys(); + } + + // pause timer that tracks total foreground running time + gForegroundTime.pause(); +} + + +BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) +{ + // Let the voice chat code check for its PTT key. Note that this never affects event processing. + LLVoiceClient::getInstance()->keyDown(key, mask); + + if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME) + { + gAgent.clearAFK(); + } + + // *NOTE: We want to interpret KEY_RETURN later when it arrives as + // a Unicode char, not as a keydown. Otherwise when client frame + // rate is really low, hitting return sends your chat text before + // it's all entered/processed. + if (key == KEY_RETURN && mask == MASK_NONE) + { + return FALSE; + } + + return gViewerKeyboard.handleKey(key, mask, repeated); +} + +BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask) +{ + // Let the voice chat code check for its PTT key. Note that this never affects event processing. + LLVoiceClient::getInstance()->keyUp(key, mask); + + return FALSE; +} + + +void LLViewerWindow::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) +{ + LLViewerJoystick::getInstance()->setCameraNeedsUpdate(true); + return gViewerKeyboard.scanKey(key, key_down, key_up, key_level); +} + + + + +BOOL LLViewerWindow::handleActivate(LLWindow *window, BOOL activated) +{ + if (activated) + { + mActive = TRUE; + send_agent_resume(); + gAgent.clearAFK(); + + // Unmute audio + audio_update_volume(); + } + else + { + mActive = FALSE; + + if (gSavedSettings.getS32("AFKTimeout")) + { + gAgent.setAFK(); + } + + // SL-53351: Make sure we're not in mouselook when minimised, to prevent control issues + if (gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK) + { + gAgentCamera.changeCameraToDefault(); + } + + send_agent_pause(); + + // Mute audio + audio_update_volume(); + } + return TRUE; +} + +BOOL LLViewerWindow::handleActivateApp(LLWindow *window, BOOL activating) +{ + //if (!activating) gAgentCamera.changeCameraToDefault(); + + LLViewerJoystick::getInstance()->setNeedsReset(true); + return FALSE; +} + + +void LLViewerWindow::handleMenuSelect(LLWindow *window, S32 menu_item) +{ +} + + +BOOL LLViewerWindow::handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S32 height) +{ +#if LL_WINDOWS + if (gNoRender) + { + HWND window_handle = (HWND)window->getPlatformWindow(); + PAINTSTRUCT ps; + HDC hdc; + + RECT wnd_rect; + wnd_rect.left = 0; + wnd_rect.top = 0; + wnd_rect.bottom = 200; + wnd_rect.right = 500; + + hdc = BeginPaint(window_handle, &ps); + //SetBKColor(hdc, RGB(255, 255, 255)); + FillRect(hdc, &wnd_rect, CreateSolidBrush(RGB(255, 255, 255))); + + std::string temp_str; + temp_str = llformat( "FPS %3.1f Phy FPS %2.1f Time Dil %1.3f", /* Flawfinder: ignore */ + LLViewerStats::getInstance()->mFPSStat.getMeanPerSec(), + LLViewerStats::getInstance()->mSimPhysicsFPS.getPrev(0), + LLViewerStats::getInstance()->mSimTimeDilation.getPrev(0)); + S32 len = temp_str.length(); + TextOutA(hdc, 0, 0, temp_str.c_str(), len); + + + LLVector3d pos_global = gAgent.getPositionGlobal(); + temp_str = llformat( "Avatar pos %6.1lf %6.1lf %6.1lf", pos_global.mdV[0], pos_global.mdV[1], pos_global.mdV[2]); + len = temp_str.length(); + TextOutA(hdc, 0, 25, temp_str.c_str(), len); + + TextOutA(hdc, 0, 50, "Set \"DisableRendering FALSE\" in settings.ini file to reenable", 61); + EndPaint(window_handle, &ps); + return TRUE; + } +#endif + return FALSE; +} + + +void LLViewerWindow::handleScrollWheel(LLWindow *window, S32 clicks) +{ + handleScrollWheel( clicks ); +} + +void LLViewerWindow::handleWindowBlock(LLWindow *window) +{ + send_agent_pause(); +} + +void LLViewerWindow::handleWindowUnblock(LLWindow *window) +{ + send_agent_resume(); +} + +void LLViewerWindow::handleDataCopy(LLWindow *window, S32 data_type, void *data) +{ + const S32 SLURL_MESSAGE_TYPE = 0; + switch (data_type) + { + case SLURL_MESSAGE_TYPE: + // received URL + std::string url = (const char*)data; + LLMediaCtrl* web = NULL; + const bool trusted_browser = false; + if (LLURLDispatcher::dispatch(url, web, trusted_browser)) + { + // bring window to foreground, as it has just been "launched" from a URL + mWindow->bringToFront(); + } + break; + } +} + +BOOL LLViewerWindow::handleTimerEvent(LLWindow *window) +{ + if (LLViewerJoystick::getInstance()->getOverrideCamera()) + { + LLViewerJoystick::getInstance()->updateStatus(); + return TRUE; + } + return FALSE; +} + +BOOL LLViewerWindow::handleDeviceChange(LLWindow *window) +{ + // give a chance to use a joystick after startup (hot-plugging) + if (!LLViewerJoystick::getInstance()->isJoystickInitialized() ) + { + LLViewerJoystick::getInstance()->init(true); + return TRUE; + } + return FALSE; +} + +void LLViewerWindow::handlePingWatchdog(LLWindow *window, const char * msg) +{ + LLAppViewer::instance()->pingMainloopTimeout(msg); +} + + +void LLViewerWindow::handleResumeWatchdog(LLWindow *window) +{ + LLAppViewer::instance()->resumeMainloopTimeout(); +} + +void LLViewerWindow::handlePauseWatchdog(LLWindow *window) +{ + LLAppViewer::instance()->pauseMainloopTimeout(); +} + +//virtual +std::string LLViewerWindow::translateString(const char* tag) +{ + return LLTrans::getString( std::string(tag) ); +} + +//virtual +std::string LLViewerWindow::translateString(const char* tag, + const std::map& args) +{ + // LLTrans uses a special subclass of std::string for format maps, + // but we must use std::map<> in these callbacks, otherwise we create + // a dependency between LLWindow and LLFormatMapString. So copy the data. + LLStringUtil::format_map_t args_copy; + std::map::const_iterator it = args.begin(); + for ( ; it != args.end(); ++it) + { + args_copy[it->first] = it->second; + } + return LLTrans::getString( std::string(tag), args_copy); +} + +// +// Classes +// +LLViewerWindow::LLViewerWindow( + const std::string& title, const std::string& name, + S32 x, S32 y, + S32 width, S32 height, + BOOL fullscreen, BOOL ignore_pixel_depth) // fullscreen is no longer used + : + mWindow(NULL), + mActive(TRUE), + mWindowRectRaw(0, height, width, 0), + mWindowRectScaled(0, height, width, 0), + mWorldViewRectRaw(0, height, width, 0), + mLeftMouseDown(FALSE), + mMiddleMouseDown(FALSE), + mRightMouseDown(FALSE), + mMouseInWindow( FALSE ), + mLastMask( MASK_NONE ), + mToolStored( NULL ), + mHideCursorPermanent( FALSE ), + mCursorHidden(FALSE), + mIgnoreActivate( FALSE ), + mResDirty(false), + mStatesDirty(false), + mCurrResolutionIndex(0), + mViewerWindowListener(new LLViewerWindowListener(this)), + mProgressView(NULL) +{ + LLNotificationChannel::buildChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "alert")); + LLNotificationChannel::buildChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "alertmodal")); + + LLNotifications::instance().getChannel("VW_alerts")->connectChanged(&LLViewerWindow::onAlert); + LLNotifications::instance().getChannel("VW_alertmodal")->connectChanged(&LLViewerWindow::onAlert); + LLNotifications::instance().setIgnoreAllNotifications(gSavedSettings.getBOOL("IgnoreAllNotifications")); + llinfos << "NOTE: ALL NOTIFICATIONS THAT OCCUR WILL GET ADDED TO IGNORE LIST FOR LATER RUNS." << llendl; + + // Default to application directory. + LLViewerWindow::sSnapshotBaseName = "Snapshot"; + LLViewerWindow::sMovieBaseName = "SLmovie"; + resetSnapshotLoc(); + + // create window + mWindow = LLWindowManager::createWindow(this, + title, name, x, y, width, height, 0, + fullscreen, + gNoRender, + gSavedSettings.getBOOL("DisableVerticalSync"), + !gNoRender, + ignore_pixel_depth, + gSavedSettings.getBOOL("RenderUseFBO") ? 0 : gSavedSettings.getU32("RenderFSAASamples")); //don't use window level anti-aliasing if FBOs are enabled + + if (!LLAppViewer::instance()->restoreErrorTrap()) + { + LL_WARNS("Window") << " Someone took over my signal/exception handler (post createWindow)!" << LL_ENDL; + } + + LLCoordScreen scr; + mWindow->getSize(&scr); + + if(fullscreen && ( scr.mX!=width || scr.mY!=height)) + { + llwarns << "Fullscreen has forced us in to a different resolution now using "<Display->Settings" + << LL_ENDL; +#endif + LLAppViewer::instance()->fastQuit(1); + } + + // Get the real window rect the window was created with (since there are various OS-dependent reasons why + // the size of a window or fullscreen context may have been adjusted slightly...) + F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor"); + + mDisplayScale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f)); + mDisplayScale *= ui_scale_factor; + LLUI::setScaleFactor(mDisplayScale); + + { + LLCoordWindow size; + mWindow->getSize(&size); + mWindowRectRaw.set(0, size.mY, size.mX, 0); + mWindowRectScaled.set(0, llround((F32)size.mY / mDisplayScale.mV[VY]), llround((F32)size.mX / mDisplayScale.mV[VX]), 0); + } + + LLFontManager::initClass(); + + // + // We want to set this stuff up BEFORE we initialize the pipeline, so we can turn off + // stuff like AGP if we think that it'll crash the viewer. + // + LL_DEBUGS("Window") << "Loading feature tables." << LL_ENDL; + + LLFeatureManager::getInstance()->init(); + + // Initialize OpenGL Renderer + if (!LLFeatureManager::getInstance()->isFeatureAvailable("RenderVBOEnable") || + !gGLManager.mHasVertexBufferObject) + { + gSavedSettings.setBOOL("RenderVBOEnable", FALSE); + } + LLVertexBuffer::initClass(gSavedSettings.getBOOL("RenderVBOEnable"), gSavedSettings.getBOOL("RenderVBOMappingDisable")); + + if (LLFeatureManager::getInstance()->isSafe() + || (gSavedSettings.getS32("LastFeatureVersion") != LLFeatureManager::getInstance()->getVersion()) + || (gSavedSettings.getS32("LastGPUClass") != LLFeatureManager::getInstance()->getGPUClass()) + || (gSavedSettings.getBOOL("ProbeHardwareOnStartup"))) + { + LLFeatureManager::getInstance()->applyRecommendedSettings(); + gSavedSettings.setBOOL("ProbeHardwareOnStartup", FALSE); + } + + if (!gGLManager.mHasDepthClamp) + { + LL_INFOS("RenderInit") << "Missing feature GL_ARB_depth_clamp. Void water might disappear in rare cases." << LL_ENDL; + } + + // If we crashed while initializng GL stuff last time, disable certain features + if (gSavedSettings.getBOOL("RenderInitError")) + { + mInitAlert = "DisplaySettingsNoShaders"; + LLFeatureManager::getInstance()->setGraphicsLevel(0, false); + gSavedSettings.setU32("RenderQualityPerformance", 0); + } + + // Init the image list. Must happen after GL is initialized and before the images that + // LLViewerWindow needs are requested. + LLImageGL::initClass(LLViewerTexture::MAX_GL_IMAGE_CATEGORY) ; + gTextureList.init(); + LLViewerTextureManager::init() ; + gBumpImageList.init(); + + // Init font system, but don't actually load the fonts yet + // because our window isn't onscreen and they take several + // seconds to parse. + LLFontGL::initClass( gSavedSettings.getF32("FontScreenDPI"), + mDisplayScale.mV[VX], + mDisplayScale.mV[VY], + gDirUtilp->getAppRODataDir(), + LLUI::getXUIPaths()); + + // Create container for all sub-views + LLView::Params rvp; + rvp.name("root"); + rvp.rect(mWindowRectScaled); + rvp.mouse_opaque(false); + rvp.follows.flags(FOLLOWS_NONE); + mRootView = LLUICtrlFactory::create(rvp); + LLUI::setRootView(mRootView); + + // Make avatar head look forward at start + mCurrentMousePoint.mX = getWindowWidthScaled() / 2; + mCurrentMousePoint.mY = getWindowHeightScaled() / 2; + + gShowOverlayTitle = gSavedSettings.getBOOL("ShowOverlayTitle"); + mOverlayTitle = gSavedSettings.getString("OverlayTitle"); + // Can't have spaces in settings.ini strings, so use underscores instead and convert them. + LLStringUtil::replaceChar(mOverlayTitle, '_', ' '); + + // sync the keyboard's setting with the saved setting + gSavedSettings.getControl("NumpadControl")->firePropertyChanged(); + + mDebugText = new LLDebugText(this); + + mWorldViewRectScaled = calcScaledRect(mWorldViewRectRaw, mDisplayScale); +} + +void LLViewerWindow::initGLDefaults() +{ + gGL.setSceneBlendType(LLRender::BT_ALPHA); + glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ); + + F32 ambient[4] = {0.f,0.f,0.f,0.f }; + F32 diffuse[4] = {1.f,1.f,1.f,1.f }; + glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambient); + glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuse); + + glPixelStorei(GL_PACK_ALIGNMENT,1); + glPixelStorei(GL_UNPACK_ALIGNMENT,1); + + gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); + + // lights for objects + glShadeModel( GL_SMOOTH ); + + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); + + gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); + + glCullFace(GL_BACK); + + // RN: Need this for translation and stretch manip. + gCone.prerender(); + gBox.prerender(); + gSphere.prerender(); + gCylinder.prerender(); +} + +struct MainPanel : public LLPanel +{ +}; + +void LLViewerWindow::initBase() +{ + S32 height = getWindowHeightScaled(); + S32 width = getWindowWidthScaled(); + + LLRect full_window(0, height, width, 0); + + //////////////////// + // + // Set the gamma + // + + F32 gamma = gSavedSettings.getF32("RenderGamma"); + if (gamma != 0.0f) + { + getWindow()->setGamma(gamma); + } + + // Create global views + + // Create the floater view at the start so that other views can add children to it. + // (But wait to add it as a child of the root view so that it will be in front of the + // other views.) + MainPanel* main_view = new MainPanel(); + main_view->buildFromFile("main_view.xml"); + main_view->setShape(full_window); + getRootView()->addChild(main_view); + + // placeholder widget that controls where "world" is rendered + mWorldViewPlaceholder = main_view->getChildView("world_view_rect")->getHandle(); + mNonSideTrayView = main_view->getChildView("non_side_tray_view")->getHandle(); + mFloaterViewHolder = main_view->getChildView("floater_view_holder")->getHandle(); + mPopupView = main_view->getChild("popup_holder"); + mHintHolder = main_view->getChild("hint_holder")->getHandle(); + mLoginPanelHolder = main_view->getChild("login_panel_holder")->getHandle(); + + // Constrain floaters to inside the menu and status bar regions. + gFloaterView = main_view->getChild("Floater View"); + gFloaterView->setFloaterSnapView(main_view->getChild("floater_snap_region")->getHandle()); + gSnapshotFloaterView = main_view->getChild("Snapshot Floater View"); + + + // Console + llassert( !gConsole ); + LLConsole::Params cp; + cp.name("console"); + cp.max_lines(gSavedSettings.getS32("ConsoleBufferSize")); + cp.rect(getChatConsoleRect()); + cp.persist_time(gSavedSettings.getF32("ChatPersistTime")); + cp.font_size_index(gSavedSettings.getS32("ChatFontSize")); + cp.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM); + gConsole = LLUICtrlFactory::create(cp); + getRootView()->addChild(gConsole); + + // optionally forward warnings to chat console/chat floater + // for qa runs and dev builds +#if !LL_RELEASE_FOR_DOWNLOAD + LLError::addRecorder(RecordToChatConsole::getInstance()); +#else + if(gSavedSettings.getBOOL("QAMode")) + { + LLError::addRecorder(RecordToChatConsole::getInstance()); + } +#endif + + gDebugView = getRootView()->getChild("DebugView"); + gDebugView->init(); + gToolTipView = getRootView()->getChild("tooltip view"); + + // Initialize busy response message when logged in + LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLFloaterPreference::initBusyResponse)); + + // Add the progress bar view (startup view), which overrides everything + mProgressView = getRootView()->findChild("progress_view"); + setShowProgress(FALSE); + setProgressCancelButtonVisible(FALSE); + + gMenuHolder = getRootView()->getChild("Menu Holder"); + + LLMenuGL::sMenuContainer = gMenuHolder; + +} + +void LLViewerWindow::initWorldUI() +{ + S32 height = mRootView->getRect().getHeight(); + S32 width = mRootView->getRect().getWidth(); + LLRect full_window(0, height, width, 0); + + + gIMMgr = LLIMMgr::getInstance(); + + //getRootView()->sendChildToFront(gFloaterView); + //getRootView()->sendChildToFront(gSnapshotFloaterView); + + // new bottom panel + LLPanel* bottom_tray_container = getRootView()->getChild("bottom_tray_container"); + LLBottomTray* bottom_tray = LLBottomTray::getInstance(); + bottom_tray->setShape(bottom_tray_container->getLocalRect()); + bottom_tray->setFollowsAll(); + bottom_tray_container->addChild(bottom_tray); + bottom_tray_container->setVisible(TRUE); + + LLRect morph_view_rect = full_window; + morph_view_rect.stretch( -STATUS_BAR_HEIGHT ); + morph_view_rect.mTop = full_window.mTop - 32; + LLMorphView::Params mvp; + mvp.name("MorphView"); + mvp.rect(morph_view_rect); + mvp.visible(false); + gMorphView = LLUICtrlFactory::create(mvp); + getRootView()->addChild(gMorphView); + + LLWorldMapView::initClass(); + + // Force gFloaterWorldMap to initialize + LLFloaterReg::getInstance("world_map"); + + // Force gFloaterTools to initialize + LLFloaterReg::getInstance("build"); + LLFloaterReg::hideInstance("build"); + + // Status bar + LLPanel* status_bar_container = getRootView()->getChild("status_bar_container"); + gStatusBar = new LLStatusBar(status_bar_container->getLocalRect()); + gStatusBar->setFollowsAll(); + gStatusBar->setShape(status_bar_container->getLocalRect()); + // sync bg color with menu bar + gStatusBar->setBackgroundColor( gMenuBarView->getBackgroundColor().get() ); + status_bar_container->addChild(gStatusBar); + status_bar_container->setVisible(TRUE); + + // Navigation bar + LLPanel* nav_bar_container = getRootView()->getChild("nav_bar_container"); + + LLNavigationBar* navbar = LLNavigationBar::getInstance(); + navbar->setShape(nav_bar_container->getLocalRect()); + navbar->setBackgroundColor(gMenuBarView->getBackgroundColor().get()); + nav_bar_container->addChild(navbar); + nav_bar_container->setVisible(TRUE); + + if (!gSavedSettings.getBOOL("ShowNavbarNavigationPanel")) + { + navbar->showNavigationPanel(FALSE); + } + + if (!gSavedSettings.getBOOL("ShowNavbarFavoritesPanel")) + { + navbar->showFavoritesPanel(FALSE); + } + + // Top Info bar + LLPanel* topinfo_bar_container = getRootView()->getChild("topinfo_bar_container"); + LLPanelTopInfoBar* topinfo_bar = LLPanelTopInfoBar::getInstance(); + + topinfo_bar->setShape(topinfo_bar_container->getLocalRect()); + + topinfo_bar_container->addChild(topinfo_bar); + topinfo_bar_container->setVisible(TRUE); + + if (!gSavedSettings.getBOOL("ShowMiniLocationPanel")) + { + topinfo_bar->setVisible(FALSE); + } + + if ( gHUDView == NULL ) + { + LLRect hud_rect = full_window; + hud_rect.mBottom += 50; + if (gMenuBarView && gMenuBarView->isInVisibleChain()) + { + hud_rect.mTop -= gMenuBarView->getRect().getHeight(); + } + gHUDView = new LLHUDView(hud_rect); + // put behind everything else in the UI + getRootView()->addChildInBack(gHUDView); + } + + LLPanel* panel_ssf_container = getRootView()->getChild("stand_stop_flying_container"); + LLPanelStandStopFlying* panel_stand_stop_flying = LLPanelStandStopFlying::getInstance(); + panel_ssf_container->addChild(panel_stand_stop_flying); + panel_ssf_container->setVisible(TRUE); + + // put sidetray in container + LLPanel* side_tray_container = getRootView()->getChild("side_tray_container"); + LLSideTray* sidetrayp = LLSideTray::getInstance(); + sidetrayp->setShape(side_tray_container->getLocalRect()); + // don't follow right edge to avoid spurious resizes, since we are using a fixed width layout + sidetrayp->setFollows(FOLLOWS_LEFT|FOLLOWS_TOP|FOLLOWS_BOTTOM); + side_tray_container->addChild(sidetrayp); + side_tray_container->setVisible(FALSE); + + // put sidetray buttons in their own panel + LLPanel* buttons_panel = sidetrayp->getButtonsPanel(); + LLPanel* buttons_panel_container = getRootView()->getChild("side_bar_tabs"); + buttons_panel->setShape(buttons_panel_container->getLocalRect()); + buttons_panel->setFollowsAll(); + buttons_panel_container->addChild(buttons_panel); + + LLView* avatar_picker_destination_guide_container = gViewerWindow->getRootView()->getChild("avatar_picker_and_destination_guide_container"); + avatar_picker_destination_guide_container->getChild("close")->setCommitCallback(boost::bind(toggle_destination_and_avatar_picker, LLSD())); + LLMediaCtrl* destinations = avatar_picker_destination_guide_container->findChild("destination_guide_contents"); + LLMediaCtrl* avatar_picker = avatar_picker_destination_guide_container->findChild("avatar_picker_contents"); + if (destinations) + { + destinations->navigateTo(gSavedSettings.getString("DestinationGuideURL"), "text/html"); + } + + if (avatar_picker) + { + avatar_picker->navigateTo(gSavedSettings.getString("AvatarPickerURL"), "text/html"); + } + +} + +// Destroy the UI +void LLViewerWindow::shutdownViews() +{ + // clean up warning logger + LLError::removeRecorder(RecordToChatConsole::getInstance()); + + delete mDebugText; + mDebugText = NULL; + + // Cleanup global views + if (gMorphView) + { + gMorphView->setVisible(FALSE); + } + + // DEV-40930: Clear sModalStack. Otherwise, any LLModalDialog left open + // will crump with LL_ERRS. + LLModalDialog::shutdownModals(); + + // destroy the nav bar, not currently part of gViewerWindow + // *TODO: Make LLNavigationBar part of gViewerWindow + delete LLNavigationBar::getInstance(); + + // destroy menus after instantiating navbar above, as it needs + // access to gMenuHolder + cleanup_menus(); + + // Delete all child views. + delete mRootView; + mRootView = NULL; + + // Automatically deleted as children of mRootView. Fix the globals. + gStatusBar = NULL; + gIMMgr = NULL; + gToolTipView = NULL; + + gFloaterView = NULL; + gMorphView = NULL; + + gHUDView = NULL; +} + +void LLViewerWindow::shutdownGL() +{ + //-------------------------------------------------------- + // Shutdown GL cleanly. Order is very important here. + //-------------------------------------------------------- + LLFontGL::destroyDefaultFonts(); + LLFontManager::cleanupClass(); + stop_glerror(); + + gSky.cleanup(); + stop_glerror(); + + LLWearableList::instance().cleanup() ; + + gTextureList.shutdown(); + stop_glerror(); + + gBumpImageList.shutdown(); + stop_glerror(); + + LLWorldMapView::cleanupTextures(); + + llinfos << "Cleaning up pipeline" << llendl; + gPipeline.cleanup(); + stop_glerror(); + + LLViewerTextureManager::cleanup() ; + LLImageGL::cleanupClass() ; + + llinfos << "All textures and llimagegl images are destroyed!" << llendl ; + + llinfos << "Cleaning up select manager" << llendl; + LLSelectMgr::getInstance()->cleanup(); + + LLVertexBuffer::cleanupClass(); + + llinfos << "Stopping GL during shutdown" << llendl; + if (!gNoRender) + { + stopGL(FALSE); + stop_glerror(); + } + + gGL.shutdown(); +} + +// shutdownViews() and shutdownGL() need to be called first +LLViewerWindow::~LLViewerWindow() +{ + llinfos << "Destroying Window" << llendl; + destroyWindow(); + + delete mDebugText; + mDebugText = NULL; +} + + +void LLViewerWindow::setCursor( ECursorType c ) +{ + mWindow->setCursor( c ); +} + +void LLViewerWindow::showCursor() +{ + mWindow->showCursor(); + + mCursorHidden = FALSE; +} + +void LLViewerWindow::hideCursor() +{ + // And hide the cursor + mWindow->hideCursor(); + + mCursorHidden = TRUE; +} + +void LLViewerWindow::sendShapeToSim() +{ + LLMessageSystem* msg = gMessageSystem; + if(!msg) return; + msg->newMessageFast(_PREHASH_AgentHeightWidth); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addU32Fast(_PREHASH_CircuitCode, gMessageSystem->mOurCircuitCode); + msg->nextBlockFast(_PREHASH_HeightWidthBlock); + msg->addU32Fast(_PREHASH_GenCounter, 0); + U16 height16 = (U16) mWorldViewRectRaw.getHeight(); + U16 width16 = (U16) mWorldViewRectRaw.getWidth(); + msg->addU16Fast(_PREHASH_Height, height16); + msg->addU16Fast(_PREHASH_Width, width16); + gAgent.sendReliableMessage(); +} + +// Must be called after window is created to set up agent +// camera variables and UI variables. +void LLViewerWindow::reshape(S32 width, S32 height) +{ + // Destroying the window at quit time generates spurious + // reshape messages. We don't care about these, and we + // don't want to send messages because the message system + // may have been destructed. + if (!LLApp::isExiting()) + { + if (gNoRender) + { + return; + } + + gWindowResized = TRUE; + + // update our window rectangle + mWindowRectRaw.mRight = mWindowRectRaw.mLeft + width; + mWindowRectRaw.mTop = mWindowRectRaw.mBottom + height; + + //glViewport(0, 0, width, height ); + + if (height > 0) + { + LLViewerCamera::getInstance()->setViewHeightInPixels( mWorldViewRectRaw.getHeight() ); + LLViewerCamera::getInstance()->setAspect( getWorldViewAspectRatio() ); + } + + calcDisplayScale(); + + BOOL display_scale_changed = mDisplayScale != LLUI::sGLScaleFactor; + LLUI::setScaleFactor(mDisplayScale); + + // update our window rectangle + mWindowRectScaled.mRight = mWindowRectScaled.mLeft + llround((F32)width / mDisplayScale.mV[VX]); + mWindowRectScaled.mTop = mWindowRectScaled.mBottom + llround((F32)height / mDisplayScale.mV[VY]); + + setup2DViewport(); + + // Inform lower views of the change + // round up when converting coordinates to make sure there are no gaps at edge of window + LLView::sForceReshape = display_scale_changed; + mRootView->reshape(llceil((F32)width / mDisplayScale.mV[VX]), llceil((F32)height / mDisplayScale.mV[VY])); + LLView::sForceReshape = FALSE; + + // clear font width caches + if (display_scale_changed) + { + LLHUDObject::reshapeAll(); + } + + sendShapeToSim(); + + // store new settings for the mode we are in, regardless + // Only save size if not maximized + BOOL maximized = mWindow->getMaximized(); + gSavedSettings.setBOOL("WindowMaximized", maximized); + + LLCoordScreen window_size; + if (!maximized + && mWindow->getSize(&window_size)) + { + gSavedSettings.setS32("WindowWidth", window_size.mX); + gSavedSettings.setS32("WindowHeight", window_size.mY); + } + + LLViewerStats::getInstance()->setStat(LLViewerStats::ST_WINDOW_WIDTH, (F64)width); + LLViewerStats::getInstance()->setStat(LLViewerStats::ST_WINDOW_HEIGHT, (F64)height); + } +} + + +// Hide normal UI when a logon fails +void LLViewerWindow::setNormalControlsVisible( BOOL visible ) +{ + if(LLBottomTray::instanceExists()) + { + LLBottomTray::getInstance()->setVisible(visible); + LLBottomTray::getInstance()->setEnabled(visible); + } + + if ( gMenuBarView ) + { + gMenuBarView->setVisible( visible ); + gMenuBarView->setEnabled( visible ); + + // ...and set the menu color appropriately. + setMenuBackgroundColor(gAgent.getGodLevel() > GOD_NOT, + LLGridManager::getInstance()->isInProductionGrid()); + } + + if ( gStatusBar ) + { + gStatusBar->setVisible( visible ); + gStatusBar->setEnabled( visible ); + } + + LLNavigationBar* navbarp = LLUI::getRootView()->findChild("navigation_bar"); + if (navbarp) + { + navbarp->setVisible( visible ); + } +} + +void LLViewerWindow::setMenuBackgroundColor(bool god_mode, bool dev_grid) +{ + LLSD args; + LLColor4 new_bg_color; + + // no l10n problem because channel is always an english string + std::string channel = LLVersionInfo::getChannel(); + bool isProject = (channel.find("Project") != std::string::npos); + + // god more important than project, proj more important than grid + if(god_mode && LLGridManager::getInstance()->isInProductionGrid()) + { + new_bg_color = LLUIColorTable::instance().getColor( "MenuBarGodBgColor" ); + } + else if(god_mode && !LLGridManager::getInstance()->isInProductionGrid()) + { + new_bg_color = LLUIColorTable::instance().getColor( "MenuNonProductionGodBgColor" ); + } + else if (!god_mode && isProject) + { + new_bg_color = LLUIColorTable::instance().getColor( "MenuBarProjectBgColor" ); + } + else if(!god_mode && !LLGridManager::getInstance()->isInProductionGrid()) + { + new_bg_color = LLUIColorTable::instance().getColor( "MenuNonProductionBgColor" ); + } + else + { + new_bg_color = LLUIColorTable::instance().getColor( "MenuBarBgColor" ); + } + + if(gMenuBarView) + { + gMenuBarView->setBackgroundColor( new_bg_color ); + } + + if(gStatusBar) + { + gStatusBar->setBackgroundColor( new_bg_color ); + } +} + +void LLViewerWindow::drawDebugText() +{ + gGL.color4f(1,1,1,1); + gGL.pushMatrix(); + gGL.pushUIMatrix(); + { + // scale view by UI global scale factor and aspect ratio correction factor + gGL.scaleUI(mDisplayScale.mV[VX], mDisplayScale.mV[VY], 1.f); + mDebugText->draw(); + } + gGL.popUIMatrix(); + gGL.popMatrix(); + + gGL.flush(); +} + +void LLViewerWindow::draw() +{ + +//#if LL_DEBUG + LLView::sIsDrawing = TRUE; +//#endif + stop_glerror(); + + LLUI::setLineWidth(1.f); + + LLUI::setLineWidth(1.f); + // Reset any left-over transforms + glMatrixMode(GL_MODELVIEW); + + glLoadIdentity(); + + //S32 screen_x, screen_y; + + if (!gSavedSettings.getBOOL("RenderUIBuffer")) + { + LLUI::sDirtyRect = getWindowRectScaled(); + } + + // HACK for timecode debugging + if (gSavedSettings.getBOOL("DisplayTimecode")) + { + // draw timecode block + std::string text; + + glLoadIdentity(); + + microsecondsToTimecodeString(gFrameTime,text); + const LLFontGL* font = LLFontGL::getFontSansSerif(); + font->renderUTF8(text, 0, + llround((getWindowWidthScaled()/2)-100.f), + llround((getWindowHeightScaled()-60.f)), + LLColor4( 1.f, 1.f, 1.f, 1.f ), + LLFontGL::LEFT, LLFontGL::TOP); + } + + // Draw all nested UI views. + // No translation needed, this view is glued to 0,0 + + gGL.pushMatrix(); + LLUI::pushMatrix(); + { + + // scale view by UI global scale factor and aspect ratio correction factor + gGL.scaleUI(mDisplayScale.mV[VX], mDisplayScale.mV[VY], 1.f); + + LLVector2 old_scale_factor = LLUI::sGLScaleFactor; + // apply camera zoom transform (for high res screenshots) + F32 zoom_factor = LLViewerCamera::getInstance()->getZoomFactor(); + S16 sub_region = LLViewerCamera::getInstance()->getZoomSubRegion(); + if (zoom_factor > 1.f) + { + //decompose subregion number to x and y values + int pos_y = sub_region / llceil(zoom_factor); + int pos_x = sub_region - (pos_y*llceil(zoom_factor)); + // offset for this tile + glTranslatef((F32)getWindowWidthScaled() * -(F32)pos_x, + (F32)getWindowHeightScaled() * -(F32)pos_y, + 0.f); + glScalef(zoom_factor, zoom_factor, 1.f); + LLUI::sGLScaleFactor *= zoom_factor; + } + + // Draw tool specific overlay on world + LLToolMgr::getInstance()->getCurrentTool()->draw(); + + if( gAgentCamera.cameraMouselook() || LLFloaterCamera::inFreeCameraMode() ) + { + drawMouselookInstructions(); + stop_glerror(); + } + + // Draw all nested UI views. + // No translation needed, this view is glued to 0,0 + mRootView->draw(); + + if (LLView::sDebugRects) + { + gToolTipView->drawStickyRect(); + } + + // Draw optional on-top-of-everyone view + LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); + if (top_ctrl && top_ctrl->getVisible()) + { + S32 screen_x, screen_y; + top_ctrl->localPointToScreen(0, 0, &screen_x, &screen_y); + + glMatrixMode(GL_MODELVIEW); + LLUI::pushMatrix(); + LLUI::translate( (F32) screen_x, (F32) screen_y, 0.f); + top_ctrl->draw(); + LLUI::popMatrix(); + } + + + if( gShowOverlayTitle && !mOverlayTitle.empty() ) + { + // Used for special titles such as "Second Life - Special E3 2003 Beta" + const S32 DIST_FROM_TOP = 20; + LLFontGL::getFontSansSerifBig()->renderUTF8( + mOverlayTitle, 0, + llround( getWindowWidthScaled() * 0.5f), + getWindowHeightScaled() - DIST_FROM_TOP, + LLColor4(1, 1, 1, 0.4f), + LLFontGL::HCENTER, LLFontGL::TOP); + } + + LLUI::sGLScaleFactor = old_scale_factor; + } + LLUI::popMatrix(); + gGL.popMatrix(); + +//#if LL_DEBUG + LLView::sIsDrawing = FALSE; +//#endif +} + +// Takes a single keydown event, usually when UI is visible +BOOL LLViewerWindow::handleKey(KEY key, MASK mask) +{ + // hide tooltips on keypress + LLToolTipMgr::instance().blockToolTips(); + + if (gFocusMgr.getKeyboardFocus() + && !(mask & (MASK_CONTROL | MASK_ALT)) + && !gFocusMgr.getKeystrokesOnly()) + { + // We have keyboard focus, and it's not an accelerator + if (key < 0x80) + { + // Not a special key, so likely (we hope) to generate a character. Let it fall through to character handler first. + return (gFocusMgr.getKeyboardFocus() != NULL); + } + } + + // let menus handle navigation keys for navigation + if ((gMenuBarView && gMenuBarView->handleKey(key, mask, TRUE)) + ||(gLoginMenuBarView && gLoginMenuBarView->handleKey(key, mask, TRUE)) + ||(gMenuHolder && gMenuHolder->handleKey(key, mask, TRUE))) + { + return TRUE; + } + + LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); + + // give menus a chance to handle modified (Ctrl, Alt) shortcut keys before current focus + // as long as focus isn't locked + if (mask & (MASK_CONTROL | MASK_ALT) && !gFocusMgr.focusLocked()) + { + // Check the current floater's menu first, if it has one. + if (gFocusMgr.keyboardFocusHasAccelerators() + && keyboard_focus + && keyboard_focus->handleKey(key,mask,FALSE)) + { + return TRUE; + } + + if ((gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask)) + ||(gLoginMenuBarView && gLoginMenuBarView->handleAcceleratorKey(key, mask))) + { + return TRUE; + } + } + + // give floaters first chance to handle TAB key + // so frontmost floater gets focus + // if nothing has focus, go to first or last UI element as appropriate + if (key == KEY_TAB && (mask & MASK_CONTROL || gFocusMgr.getKeyboardFocus() == NULL)) + { + if (gMenuHolder) gMenuHolder->hideMenus(); + + // if CTRL-tabbing (and not just TAB with no focus), go into window cycle mode + gFloaterView->setCycleMode((mask & MASK_CONTROL) != 0); + + // do CTRL-TAB and CTRL-SHIFT-TAB logic + if (mask & MASK_SHIFT) + { + mRootView->focusPrevRoot(); + } + else + { + mRootView->focusNextRoot(); + } + return TRUE; + } + // hidden edit menu for cut/copy/paste + if (gEditMenu && gEditMenu->handleAcceleratorKey(key, mask)) + { + return TRUE; + } + + // Traverses up the hierarchy + if( keyboard_focus ) + { + LLLineEditor* chat_editor = LLBottomTray::instanceExists() ? LLBottomTray::getInstance()->getNearbyChatBar()->getChatBox() : NULL; + // arrow keys move avatar while chatting hack + if (chat_editor && chat_editor->hasFocus()) + { + // If text field is empty, there's no point in trying to move + // cursor with arrow keys, so allow movement + if (chat_editor->getText().empty() + || gSavedSettings.getBOOL("ArrowKeysAlwaysMove")) + { + // let Control-Up and Control-Down through for chat line history, + if (!(key == KEY_UP && mask == MASK_CONTROL) + && !(key == KEY_DOWN && mask == MASK_CONTROL)) + { + switch(key) + { + case KEY_LEFT: + case KEY_RIGHT: + case KEY_UP: + case KEY_DOWN: + case KEY_PAGE_UP: + case KEY_PAGE_DOWN: + case KEY_HOME: + // when chatbar is empty or ArrowKeysAlwaysMove set, + // pass arrow keys on to avatar... + return FALSE; + default: + break; + } + } + } + } + + if (keyboard_focus->handleKey(key, mask, FALSE)) + { + return TRUE; + } + } + + if( LLToolMgr::getInstance()->getCurrentTool()->handleKey(key, mask) ) + { + return TRUE; + } + + // Try for a new-format gesture + if (LLGestureMgr::instance().triggerGesture(key, mask)) + { + return TRUE; + } + + // See if this is a gesture trigger. If so, eat the key and + // don't pass it down to the menus. + if (gGestureList.trigger(key, mask)) + { + return TRUE; + } + + // If "Pressing letter keys starts local chat" option is selected, we are not in mouselook, + // no view has keyboard focus, this is a printable character key (and no modifier key is + // pressed except shift), then give focus to nearby chat (STORM-560) + if ( gSavedSettings.getS32("LetterKeysFocusChatBar") && !gAgentCamera.cameraMouselook() && + !keyboard_focus && key < 0x80 && (mask == MASK_NONE || mask == MASK_SHIFT) ) + { + LLLineEditor* chat_editor = LLBottomTray::instanceExists() ? LLBottomTray::getInstance()->getNearbyChatBar()->getChatBox() : NULL; + if (chat_editor) + { + // passing NULL here, character will be added later when it is handled by character handler. + LLBottomTray::getInstance()->getNearbyChatBar()->startChat(NULL); + return TRUE; + } + } + + // give menus a chance to handle unmodified accelerator keys + if ((gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask)) + ||(gLoginMenuBarView && gLoginMenuBarView->handleAcceleratorKey(key, mask))) + { + return TRUE; + } + + // don't pass keys on to world when something in ui has focus + return gFocusMgr.childHasKeyboardFocus(mRootView) + || LLMenuGL::getKeyboardMode() + || (gMenuBarView && gMenuBarView->getHighlightedItem() && gMenuBarView->getHighlightedItem()->isActive()); +} + + +BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask) +{ + // HACK: We delay processing of return keys until they arrive as a Unicode char, + // so that if you're typing chat text at low frame rate, we don't send the chat + // until all keystrokes have been entered. JC + // HACK: Numeric keypad on Mac is Unicode 3 + // HACK: Control-M on Windows is Unicode 13 + if ((uni_char == 13 && mask != MASK_CONTROL) + || (uni_char == 3 && mask == MASK_NONE)) + { + return gViewerKeyboard.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN)); + } + + // let menus handle navigation (jump) keys + if (gMenuBarView && gMenuBarView->handleUnicodeChar(uni_char, TRUE)) + { + return TRUE; + } + + // Traverses up the hierarchy + LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); + if( keyboard_focus ) + { + if (keyboard_focus->handleUnicodeChar(uni_char, FALSE)) + { + return TRUE; + } + + //// Topmost view gets a chance before the hierarchy + //LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); + //if (top_ctrl && top_ctrl->handleUnicodeChar( uni_char, FALSE ) ) + //{ + // return TRUE; + //} + + return TRUE; + } + + return FALSE; +} + + +void LLViewerWindow::handleScrollWheel(S32 clicks) +{ + LLView::sMouseHandlerMessage.clear(); + + LLUI::resetMouseIdleTimer(); + + LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); + if( mouse_captor ) + { + S32 local_x; + S32 local_y; + mouse_captor->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y ); + mouse_captor->handleScrollWheel(local_x, local_y, clicks); + if (LLView::sDebugMouseHandling) + { + llinfos << "Scroll Wheel handled by captor " << mouse_captor->getName() << llendl; + } + return; + } + + LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); + if (top_ctrl) + { + S32 local_x; + S32 local_y; + top_ctrl->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y ); + if (top_ctrl->handleScrollWheel(local_x, local_y, clicks)) return; + } + + if (mRootView->handleScrollWheel(mCurrentMousePoint.mX, mCurrentMousePoint.mY, clicks) ) + { + if (LLView::sDebugMouseHandling) + { + llinfos << "Scroll Wheel" << LLView::sMouseHandlerMessage << llendl; + } + return; + } + else if (LLView::sDebugMouseHandling) + { + llinfos << "Scroll Wheel not handled by view" << llendl; + } + + // Zoom the camera in and out behavior + + if(top_ctrl == 0 + && getWorldViewRectScaled().pointInRect(mCurrentMousePoint.mX, mCurrentMousePoint.mY) + && gAgentCamera.isInitialized()) + gAgentCamera.handleScrollWheel(clicks); + + return; +} + +void LLViewerWindow::addPopup(LLView* popup) +{ + if (mPopupView) + { + mPopupView->addPopup(popup); + } +} + +void LLViewerWindow::removePopup(LLView* popup) +{ + if (mPopupView) + { + mPopupView->removePopup(popup); + } +} + +void LLViewerWindow::clearPopups() +{ + if (mPopupView) + { + mPopupView->clearPopups(); + } +} + +void LLViewerWindow::moveCursorToCenter() +{ + if (! gSavedSettings.getBOOL("DisableMouseWarp")) + { + S32 x = getWorldViewWidthScaled() / 2; + S32 y = getWorldViewHeightScaled() / 2; + + //on a forced move, all deltas get zeroed out to prevent jumping + mCurrentMousePoint.set(x,y); + mLastMousePoint.set(x,y); + mCurrentMouseDelta.set(0,0); + + LLUI::setMousePositionScreen(x, y); + } +} + + +////////////////////////////////////////////////////////////////////// +// +// Hover handlers +// + +void append_xui_tooltip(LLView* viewp, LLToolTip::Params& params) +{ + if (viewp) + { + if (!params.styled_message.empty()) + { + params.styled_message.add().text("\n---------\n"); + } + LLView::root_to_view_iterator_t end_tooltip_it = viewp->endRootToView(); + // NOTE: we skip "root" since it is assumed + for (LLView::root_to_view_iterator_t tooltip_it = ++viewp->beginRootToView(); + tooltip_it != end_tooltip_it; + ++tooltip_it) + { + LLView* viewp = *tooltip_it; + + params.styled_message.add().text(viewp->getName()); + + LLPanel* panelp = dynamic_cast(viewp); + if (panelp && !panelp->getXMLFilename().empty()) + { + params.styled_message.add() + .text("(" + panelp->getXMLFilename() + ")") + .style.color(LLColor4(0.7f, 0.7f, 1.f, 1.f)); + } + params.styled_message.add().text("/"); + } + } +} + +// Update UI based on stored mouse position from mouse-move +// event processing. +void LLViewerWindow::updateUI() +{ + static LLFastTimer::DeclareTimer ftm("Update UI"); + LLFastTimer t(ftm); + + static std::string last_handle_msg; + + if (gLoggedInTime.getStarted()) + { + if (gLoggedInTime.getElapsedTimeF32() > gSavedSettings.getF32("DestinationGuideHintTimeout")) + { + LLFirstUse::notUsingDestinationGuide(); + } + if (gLoggedInTime.getElapsedTimeF32() > gSavedSettings.getF32("SidePanelHintTimeout")) + { + LLFirstUse::notUsingSidePanel(); + } + } + + LLConsole::updateClass(); + + // animate layout stacks so we have up to date rect for world view + LLLayoutStack::updateClass(); + + // use full window for world view when not rendering UI + bool world_view_uses_full_window = gAgentCamera.cameraMouselook() || !gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI); + updateWorldViewRect(world_view_uses_full_window); + + LLView::sMouseHandlerMessage.clear(); + + S32 x = mCurrentMousePoint.mX; + S32 y = mCurrentMousePoint.mY; + MASK mask = gKeyboard->currentMask(TRUE); + + if (gNoRender) + { + return; + } + + if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST)) + { + gDebugRaycastFaceHit = -1; + gDebugRaycastObject = cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE, + &gDebugRaycastFaceHit, + &gDebugRaycastIntersection, + &gDebugRaycastTexCoord, + &gDebugRaycastNormal, + &gDebugRaycastBinormal); + } + + updateMouseDelta(); + updateKeyboardFocus(); + + BOOL handled = FALSE; + + BOOL handled_by_top_ctrl = FALSE; + LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); + LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); + LLView* captor_view = dynamic_cast(mouse_captor); + + //FIXME: only include captor and captor's ancestors if mouse is truly over them --RN + + //build set of views containing mouse cursor by traversing UI hierarchy and testing + //screen rect against mouse cursor + view_handle_set_t mouse_hover_set; + + // constraint mouse enter events to children of mouse captor + LLView* root_view = captor_view; + + // if mouse captor doesn't exist or isn't a LLView + // then allow mouse enter events on entire UI hierarchy + if (!root_view) + { + root_view = mRootView; + } + + // only update mouse hover set when UI is visible (since we shouldn't send hover events to invisible UI + if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + // include all ancestors of captor_view as automatically having mouse + if (captor_view) + { + LLView* captor_parent_view = captor_view->getParent(); + while(captor_parent_view) + { + mouse_hover_set.insert(captor_parent_view->getHandle()); + captor_parent_view = captor_parent_view->getParent(); + } + } + + // aggregate visible views that contain mouse cursor in display order + LLPopupView::popup_list_t popups = mPopupView->getCurrentPopups(); + + for(LLPopupView::popup_list_t::iterator popup_it = popups.begin(); popup_it != popups.end(); ++popup_it) + { + LLView* popup = popup_it->get(); + if (popup && popup->calcScreenBoundingRect().pointInRect(x, y)) + { + // iterator over contents of top_ctrl, and throw into mouse_hover_set + for (LLView::tree_iterator_t it = popup->beginTreeDFS(); + it != popup->endTreeDFS(); + ++it) + { + LLView* viewp = *it; + if (viewp->getVisible() + && viewp->calcScreenBoundingRect().pointInRect(x, y)) + { + // we have a view that contains the mouse, add it to the set + mouse_hover_set.insert(viewp->getHandle()); + } + else + { + // skip this view and all of its children + it.skipDescendants(); + } + } + } + } + + // while the top_ctrl contains the mouse cursor, only it and its descendants will receive onMouseEnter events + if (top_ctrl && top_ctrl->calcScreenBoundingRect().pointInRect(x, y)) + { + // iterator over contents of top_ctrl, and throw into mouse_hover_set + for (LLView::tree_iterator_t it = top_ctrl->beginTreeDFS(); + it != top_ctrl->endTreeDFS(); + ++it) + { + LLView* viewp = *it; + if (viewp->getVisible() + && viewp->calcScreenBoundingRect().pointInRect(x, y)) + { + // we have a view that contains the mouse, add it to the set + mouse_hover_set.insert(viewp->getHandle()); + } + else + { + // skip this view and all of its children + it.skipDescendants(); + } + } + } + else + { + // walk UI tree in depth-first order + for (LLView::tree_iterator_t it = root_view->beginTreeDFS(); + it != root_view->endTreeDFS(); + ++it) + { + LLView* viewp = *it; + // calculating the screen rect involves traversing the parent, so this is less than optimal + if (viewp->getVisible() + && viewp->calcScreenBoundingRect().pointInRect(x, y)) + { + + // if this view is mouse opaque, nothing behind it should be in mouse_hover_set + if (viewp->getMouseOpaque()) + { + // constrain further iteration to children of this widget + it = viewp->beginTreeDFS(); + } + + // we have a view that contains the mouse, add it to the set + mouse_hover_set.insert(viewp->getHandle()); + } + else + { + // skip this view and all of its children + it.skipDescendants(); + } + } + } + } + + typedef std::vector > view_handle_list_t; + + // call onMouseEnter() on all views which contain the mouse cursor but did not before + view_handle_list_t mouse_enter_views; + std::set_difference(mouse_hover_set.begin(), mouse_hover_set.end(), + mMouseHoverViews.begin(), mMouseHoverViews.end(), + std::back_inserter(mouse_enter_views)); + for (view_handle_list_t::iterator it = mouse_enter_views.begin(); + it != mouse_enter_views.end(); + ++it) + { + LLView* viewp = it->get(); + if (viewp) + { + LLRect view_screen_rect = viewp->calcScreenRect(); + viewp->onMouseEnter(x - view_screen_rect.mLeft, y - view_screen_rect.mBottom, mask); + } + } + + // call onMouseLeave() on all views which no longer contain the mouse cursor + view_handle_list_t mouse_leave_views; + std::set_difference(mMouseHoverViews.begin(), mMouseHoverViews.end(), + mouse_hover_set.begin(), mouse_hover_set.end(), + std::back_inserter(mouse_leave_views)); + for (view_handle_list_t::iterator it = mouse_leave_views.begin(); + it != mouse_leave_views.end(); + ++it) + { + LLView* viewp = it->get(); + if (viewp) + { + LLRect view_screen_rect = viewp->calcScreenRect(); + viewp->onMouseLeave(x - view_screen_rect.mLeft, y - view_screen_rect.mBottom, mask); + } + } + + // store resulting hover set for next frame + swap(mMouseHoverViews, mouse_hover_set); + + // only handle hover events when UI is enabled + if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + + if( mouse_captor ) + { + // Pass hover events to object capturing mouse events. + S32 local_x; + S32 local_y; + mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); + handled = mouse_captor->handleHover(local_x, local_y, mask); + if (LLView::sDebugMouseHandling) + { + llinfos << "Hover handled by captor " << mouse_captor->getName() << llendl; + } + + if( !handled ) + { + lldebugst(LLERR_USER_INPUT) << "hover not handled by mouse captor" << llendl; + } + } + else + { + if (top_ctrl) + { + S32 local_x, local_y; + top_ctrl->screenPointToLocal( x, y, &local_x, &local_y ); + handled = top_ctrl->pointInView(local_x, local_y) && top_ctrl->handleHover(local_x, local_y, mask); + handled_by_top_ctrl = TRUE; + } + + if ( !handled ) + { + // x and y are from last time mouse was in window + // mMouseInWindow tracks *actual* mouse location + if (mMouseInWindow && mRootView->handleHover(x, y, mask) ) + { + if (LLView::sDebugMouseHandling && LLView::sMouseHandlerMessage != last_handle_msg) + { + last_handle_msg = LLView::sMouseHandlerMessage; + llinfos << "Hover" << LLView::sMouseHandlerMessage << llendl; + } + handled = TRUE; + } + else if (LLView::sDebugMouseHandling) + { + if (last_handle_msg != LLStringUtil::null) + { + last_handle_msg.clear(); + llinfos << "Hover not handled by view" << llendl; + } + } + } + + if (!handled) + { + LLTool *tool = LLToolMgr::getInstance()->getCurrentTool(); + + if(mMouseInWindow && tool) + { + handled = tool->handleHover(x, y, mask); + } + } + } + + // Show a new tool tip (or update one that is already shown) + BOOL tool_tip_handled = FALSE; + std::string tool_tip_msg; + if( handled + && !mWindow->isCursorHidden()) + { + LLRect screen_sticky_rect = mRootView->getLocalRect(); + S32 local_x, local_y; + + if (gSavedSettings.getBOOL("DebugShowXUINames")) + { + LLToolTip::Params params; + + LLView* tooltip_view = mRootView; + LLView::tree_iterator_t end_it = mRootView->endTreeDFS(); + for (LLView::tree_iterator_t it = mRootView->beginTreeDFS(); it != end_it; ++it) + { + LLView* viewp = *it; + LLRect screen_rect; + viewp->localRectToScreen(viewp->getLocalRect(), &screen_rect); + if (!(viewp->getVisible() + && screen_rect.pointInRect(x, y))) + { + it.skipDescendants(); + } + // only report xui names for LLUICtrls, + // and blacklist the various containers we don't care about + else if (dynamic_cast(viewp) + && viewp != gMenuHolder + && viewp != gFloaterView + && viewp != gConsole) + { + if (dynamic_cast(viewp)) + { + // constrain search to descendants of this (frontmost) floater + // by resetting iterator + it = viewp->beginTreeDFS(); + } + + // if we are in a new part of the tree (not a descendent of current tooltip_view) + // then push the results for tooltip_view and start with a new potential view + // NOTE: this emulates visiting only the leaf nodes that meet our criteria + if (!viewp->hasAncestor(tooltip_view)) + { + append_xui_tooltip(tooltip_view, params); + screen_sticky_rect.intersectWith(tooltip_view->calcScreenRect()); + } + tooltip_view = viewp; + } + } + + append_xui_tooltip(tooltip_view, params); + screen_sticky_rect.intersectWith(tooltip_view->calcScreenRect()); + + params.sticky_rect = screen_sticky_rect; + params.max_width = 400; + + LLToolTipMgr::instance().show(params); + } + // if there is a mouse captor, nothing else gets a tooltip + else if (mouse_captor) + { + mouse_captor->screenPointToLocal(x, y, &local_x, &local_y); + tool_tip_handled = mouse_captor->handleToolTip(local_x, local_y, mask); + } + else + { + // next is top_ctrl + if (!tool_tip_handled && top_ctrl) + { + top_ctrl->screenPointToLocal(x, y, &local_x, &local_y); + tool_tip_handled = top_ctrl->handleToolTip(local_x, local_y, mask ); + } + + if (!tool_tip_handled) + { + local_x = x; local_y = y; + tool_tip_handled = mRootView->handleToolTip(local_x, local_y, mask ); + } + + LLTool* current_tool = LLToolMgr::getInstance()->getCurrentTool(); + if (!tool_tip_handled && current_tool) + { + current_tool->screenPointToLocal(x, y, &local_x, &local_y); + tool_tip_handled = current_tool->handleToolTip(local_x, local_y, mask ); + } + } + } + } + else + { // just have tools handle hover when UI is turned off + LLTool *tool = LLToolMgr::getInstance()->getCurrentTool(); + + if(mMouseInWindow && tool) + { + handled = tool->handleHover(x, y, mask); + } + } + + updateLayout(); + + mLastMousePoint = mCurrentMousePoint; + + // cleanup unused selections when no modal dialogs are open + if (LLModalDialog::activeCount() == 0) + { + LLViewerParcelMgr::getInstance()->deselectUnused(); + } + + if (LLModalDialog::activeCount() == 0) + { + LLSelectMgr::getInstance()->deselectUnused(); + } +} + + +void LLViewerWindow::updateLayout() +{ + LLTool* tool = LLToolMgr::getInstance()->getCurrentTool(); + if (gFloaterTools != NULL + && tool != NULL + && tool != gToolNull + && tool != LLToolCompInspect::getInstance() + && tool != LLToolDragAndDrop::getInstance() + && !gSavedSettings.getBOOL("FreezeTime")) + { + // Suppress the toolbox view if our source tool was the pie tool, + // and we've overridden to something else. + bool suppress_toolbox = + (LLToolMgr::getInstance()->getBaseTool() == LLToolPie::getInstance()) && + (LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance()); + + LLMouseHandler *captor = gFocusMgr.getMouseCapture(); + // With the null, inspect, or drag and drop tool, don't muck + // with visibility. + + if (gFloaterTools->isMinimized() + || (tool != LLToolPie::getInstance() // not default tool + && tool != LLToolCompGun::getInstance() // not coming out of mouselook + && !suppress_toolbox // not override in third person + && LLToolMgr::getInstance()->getCurrentToolset() != gFaceEditToolset // not special mode + && LLToolMgr::getInstance()->getCurrentToolset() != gMouselookToolset + && (!captor || dynamic_cast(captor) != NULL))) // not dragging + { + // Force floater tools to be visible (unless minimized) + if (!gFloaterTools->getVisible()) + { + gFloaterTools->openFloater(); + } + // Update the location of the blue box tool popup + LLCoordGL select_center_screen; + gFloaterTools->updatePopup( select_center_screen, gKeyboard->currentMask(TRUE) ); + } + else + { + gFloaterTools->setVisible(FALSE); + } + //gMenuBarView->setItemVisible("BuildTools", gFloaterTools->getVisible()); + } + + // Always update console + if(gConsole) + { + LLRect console_rect = getChatConsoleRect(); + gConsole->reshape(console_rect.getWidth(), console_rect.getHeight()); + gConsole->setRect(console_rect); + } +} + +void LLViewerWindow::updateMouseDelta() +{ + S32 dx = lltrunc((F32) (mCurrentMousePoint.mX - mLastMousePoint.mX) * LLUI::sGLScaleFactor.mV[VX]); + S32 dy = lltrunc((F32) (mCurrentMousePoint.mY - mLastMousePoint.mY) * LLUI::sGLScaleFactor.mV[VY]); + + //RN: fix for asynchronous notification of mouse leaving window not working + LLCoordWindow mouse_pos; + mWindow->getCursorPosition(&mouse_pos); + if (mouse_pos.mX < 0 || + mouse_pos.mY < 0 || + mouse_pos.mX > mWindowRectRaw.getWidth() || + mouse_pos.mY > mWindowRectRaw.getHeight()) + { + mMouseInWindow = FALSE; + } + else + { + mMouseInWindow = TRUE; + } + + LLVector2 mouse_vel; + + if (gSavedSettings.getBOOL("MouseSmooth")) + { + static F32 fdx = 0.f; + static F32 fdy = 0.f; + + F32 amount = 16.f; + fdx = fdx + ((F32) dx - fdx) * llmin(gFrameIntervalSeconds*amount,1.f); + fdy = fdy + ((F32) dy - fdy) * llmin(gFrameIntervalSeconds*amount,1.f); + + mCurrentMouseDelta.set(llround(fdx), llround(fdy)); + mouse_vel.setVec(fdx,fdy); + } + else + { + mCurrentMouseDelta.set(dx, dy); + mouse_vel.setVec((F32) dx, (F32) dy); + } + + mMouseVelocityStat.addValue(mouse_vel.magVec()); +} + +void LLViewerWindow::updateKeyboardFocus() +{ + if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + gFocusMgr.setKeyboardFocus(NULL); + } + + // clean up current focus + LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); + if (cur_focus) + { + if (!cur_focus->isInVisibleChain() || !cur_focus->isInEnabledChain()) + { + // don't release focus, just reassign so that if being given + // to a sibling won't call onFocusLost on all the ancestors + // gFocusMgr.releaseFocusIfNeeded(cur_focus); + + LLUICtrl* parent = cur_focus->getParentUICtrl(); + const LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot(); + bool new_focus_found = false; + while(parent) + { + if (parent->isCtrl() + && (parent->hasTabStop() || parent == focus_root) + && !parent->getIsChrome() + && parent->isInVisibleChain() + && parent->isInEnabledChain()) + { + if (!parent->focusFirstItem()) + { + parent->setFocus(TRUE); + } + new_focus_found = true; + break; + } + parent = parent->getParentUICtrl(); + } + + // if we didn't find a better place to put focus, just release it + // hasFocus() will return true if and only if we didn't touch focus since we + // are only moving focus higher in the hierarchy + if (!new_focus_found) + { + cur_focus->setFocus(FALSE); + } + } + else if (cur_focus->isFocusRoot()) + { + // focus roots keep trying to delegate focus to their first valid descendant + // this assumes that focus roots are not valid focus holders on their own + cur_focus->focusFirstItem(); + } + } + + // last ditch force of edit menu to selection manager + if (LLEditMenuHandler::gEditMenuHandler == NULL && LLSelectMgr::getInstance()->getSelection()->getObjectCount()) + { + LLEditMenuHandler::gEditMenuHandler = LLSelectMgr::getInstance(); + } + + if (gFloaterView->getCycleMode()) + { + // sync all floaters with their focus state + gFloaterView->highlightFocusedFloater(); + gSnapshotFloaterView->highlightFocusedFloater(); + if ((gKeyboard->currentMask(TRUE) & MASK_CONTROL) == 0) + { + // control key no longer held down, finish cycle mode + gFloaterView->setCycleMode(FALSE); + + gFloaterView->syncFloaterTabOrder(); + } + else + { + // user holding down CTRL, don't update tab order of floaters + } + } + else + { + // update focused floater + gFloaterView->highlightFocusedFloater(); + gSnapshotFloaterView->highlightFocusedFloater(); + // make sure floater visible order is in sync with tab order + gFloaterView->syncFloaterTabOrder(); + } + + if(LLSideTray::instanceCreated())//just getInstance will create sidetray. we don't want this + LLSideTray::getInstance()->highlightFocused(); +} + +static LLFastTimer::DeclareTimer FTM_UPDATE_WORLD_VIEW("Update World View"); +void LLViewerWindow::updateWorldViewRect(bool use_full_window) +{ + LLFastTimer ft(FTM_UPDATE_WORLD_VIEW); + + // start off using whole window to render world + LLRect new_world_rect = mWindowRectRaw; + + if (use_full_window == false && mWorldViewPlaceholder.get()) + { + new_world_rect = mWorldViewPlaceholder.get()->calcScreenRect(); + // clamp to at least a 1x1 rect so we don't try to allocate zero width gl buffers + new_world_rect.mTop = llmax(new_world_rect.mTop, new_world_rect.mBottom + 1); + new_world_rect.mRight = llmax(new_world_rect.mRight, new_world_rect.mLeft + 1); + + new_world_rect.mLeft = llround((F32)new_world_rect.mLeft * mDisplayScale.mV[VX]); + new_world_rect.mRight = llround((F32)new_world_rect.mRight * mDisplayScale.mV[VX]); + new_world_rect.mBottom = llround((F32)new_world_rect.mBottom * mDisplayScale.mV[VY]); + new_world_rect.mTop = llround((F32)new_world_rect.mTop * mDisplayScale.mV[VY]); + } + + if (gSavedSettings.getBOOL("SidebarCameraMovement") == FALSE) + { + // use right edge of window, ignoring sidebar + new_world_rect.mRight = mWindowRectRaw.mRight; + } + + if (mWorldViewRectRaw != new_world_rect) + { + mWorldViewRectRaw = new_world_rect; + gResizeScreenTexture = TRUE; + LLViewerCamera::getInstance()->setViewHeightInPixels( mWorldViewRectRaw.getHeight() ); + LLViewerCamera::getInstance()->setAspect( getWorldViewAspectRatio() ); + + LLRect old_world_rect_scaled = mWorldViewRectScaled; + mWorldViewRectScaled = calcScaledRect(mWorldViewRectRaw, mDisplayScale); + + // sending a signal with a new WorldView rect + mOnWorldViewRectUpdated(old_world_rect_scaled, mWorldViewRectScaled); + } +} + +void LLViewerWindow::saveLastMouse(const LLCoordGL &point) +{ + // Store last mouse location. + // If mouse leaves window, pretend last point was on edge of window + if (point.mX < 0) + { + mCurrentMousePoint.mX = 0; + } + else if (point.mX > getWindowWidthScaled()) + { + mCurrentMousePoint.mX = getWindowWidthScaled(); + } + else + { + mCurrentMousePoint.mX = point.mX; + } + + if (point.mY < 0) + { + mCurrentMousePoint.mY = 0; + } + else if (point.mY > getWindowHeightScaled() ) + { + mCurrentMousePoint.mY = getWindowHeightScaled(); + } + else + { + mCurrentMousePoint.mY = point.mY; + } +} + + +// Draws the selection outlines for the currently selected objects +// Must be called after displayObjects is called, which sets the mGLName parameter +// NOTE: This function gets called 3 times: +// render_ui_3d: FALSE, FALSE, TRUE +// render_hud_elements: FALSE, FALSE, FALSE +void LLViewerWindow::renderSelections( BOOL for_gl_pick, BOOL pick_parcel_walls, BOOL for_hud ) +{ + LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); + + if (!for_hud && !for_gl_pick) + { + // Call this once and only once + LLSelectMgr::getInstance()->updateSilhouettes(); + } + + // Draw fence around land selections + if (for_gl_pick) + { + if (pick_parcel_walls) + { + LLViewerParcelMgr::getInstance()->renderParcelCollision(); + } + } + else if (( for_hud && selection->getSelectType() == SELECT_TYPE_HUD) || + (!for_hud && selection->getSelectType() != SELECT_TYPE_HUD)) + { + LLSelectMgr::getInstance()->renderSilhouettes(for_hud); + + stop_glerror(); + + // setup HUD render + if (selection->getSelectType() == SELECT_TYPE_HUD && LLSelectMgr::getInstance()->getSelection()->getObjectCount()) + { + LLBBox hud_bbox = gAgentAvatarp->getHUDBBox(); + + // set up transform to encompass bounding box of HUD + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + F32 depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f); + glOrtho(-0.5f * LLViewerCamera::getInstance()->getAspect(), 0.5f * LLViewerCamera::getInstance()->getAspect(), -0.5f, 0.5f, 0.f, depth); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame + glTranslatef(-hud_bbox.getCenterLocal().mV[VX] + (depth *0.5f), 0.f, 0.f); + } + + // Render light for editing + if (LLSelectMgr::sRenderLightRadius && LLToolMgr::getInstance()->inEdit()) + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + LLGLEnable gls_blend(GL_BLEND); + LLGLEnable gls_cull(GL_CULL_FACE); + LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + if (selection->getSelectType() == SELECT_TYPE_HUD) + { + F32 zoom = gAgentCamera.mHUDCurZoom; + glScalef(zoom, zoom, zoom); + } + + struct f : public LLSelectedObjectFunctor + { + virtual bool apply(LLViewerObject* object) + { + LLDrawable* drawable = object->mDrawable; + if (drawable && drawable->isLight()) + { + LLVOVolume* vovolume = drawable->getVOVolume(); + glPushMatrix(); + + LLVector3 center = drawable->getPositionAgent(); + glTranslatef(center[0], center[1], center[2]); + F32 scale = vovolume->getLightRadius(); + glScalef(scale, scale, scale); + + LLColor4 color(vovolume->getLightColor(), .5f); + glColor4fv(color.mV); + + F32 pixel_area = 100000.f; + // Render Outside + gSphere.render(pixel_area); + + // Render Inside + glCullFace(GL_FRONT); + gSphere.render(pixel_area); + glCullFace(GL_BACK); + + glPopMatrix(); + } + return true; + } + } func; + LLSelectMgr::getInstance()->getSelection()->applyToObjects(&func); + + glPopMatrix(); + } + + // NOTE: The average position for the axis arrows of the selected objects should + // not be recalculated at this time. If they are, then group rotations will break. + + // Draw arrows at average center of all selected objects + LLTool* tool = LLToolMgr::getInstance()->getCurrentTool(); + if (tool) + { + if(tool->isAlwaysRendered()) + { + tool->render(); + } + else + { + if( !LLSelectMgr::getInstance()->getSelection()->isEmpty() ) + { + BOOL moveable_object_selected = FALSE; + BOOL all_selected_objects_move = TRUE; + BOOL all_selected_objects_modify = TRUE; + BOOL selecting_linked_set = !gSavedSettings.getBOOL("EditLinkedParts"); + + for (LLObjectSelection::iterator iter = LLSelectMgr::getInstance()->getSelection()->begin(); + iter != LLSelectMgr::getInstance()->getSelection()->end(); iter++) + { + LLSelectNode* nodep = *iter; + LLViewerObject* object = nodep->getObject(); + BOOL this_object_movable = FALSE; + if (object->permMove() && (object->permModify() || selecting_linked_set)) + { + moveable_object_selected = TRUE; + this_object_movable = TRUE; + } + all_selected_objects_move = all_selected_objects_move && this_object_movable; + all_selected_objects_modify = all_selected_objects_modify && object->permModify(); + } + + BOOL draw_handles = TRUE; + + if (tool == LLToolCompTranslate::getInstance() && (!moveable_object_selected || !all_selected_objects_move)) + { + draw_handles = FALSE; + } + + if (tool == LLToolCompRotate::getInstance() && (!moveable_object_selected || !all_selected_objects_move)) + { + draw_handles = FALSE; + } + + if ( !all_selected_objects_modify && tool == LLToolCompScale::getInstance() ) + { + draw_handles = FALSE; + } + + if( draw_handles ) + { + tool->render(); + } + } + } + if (selection->getSelectType() == SELECT_TYPE_HUD && selection->getObjectCount()) + { + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + stop_glerror(); + } + } + } +} + +// Return a point near the clicked object representative of the place the object was clicked. +LLVector3d LLViewerWindow::clickPointInWorldGlobal(S32 x, S32 y_from_bot, LLViewerObject* clicked_object) const +{ + // create a normalized vector pointing from the camera center into the + // world at the location of the mouse click + LLVector3 mouse_direction_global = mouseDirectionGlobal( x, y_from_bot ); + + LLVector3d relative_object = clicked_object->getPositionGlobal() - gAgentCamera.getCameraPositionGlobal(); + + // make mouse vector as long as object vector, so it touchs a point near + // where the user clicked on the object + mouse_direction_global *= (F32) relative_object.magVec(); + + LLVector3d new_pos; + new_pos.setVec(mouse_direction_global); + // transform mouse vector back to world coords + new_pos += gAgentCamera.getCameraPositionGlobal(); + + return new_pos; +} + + +BOOL LLViewerWindow::clickPointOnSurfaceGlobal(const S32 x, const S32 y, LLViewerObject *objectp, LLVector3d &point_global) const +{ + BOOL intersect = FALSE; + +// U8 shape = objectp->mPrimitiveCode & LL_PCODE_BASE_MASK; + if (!intersect) + { + point_global = clickPointInWorldGlobal(x, y, objectp); + llinfos << "approx intersection at " << (objectp->getPositionGlobal() - point_global) << llendl; + } + else + { + llinfos << "good intersection at " << (objectp->getPositionGlobal() - point_global) << llendl; + } + + return intersect; +} + +void LLViewerWindow::pickAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(const LLPickInfo& info), BOOL pick_transparent) +{ + if (gNoRender) + { + return; + } + + BOOL in_build_mode = LLFloaterReg::instanceVisible("build"); + if (in_build_mode || LLDrawPoolAlpha::sShowDebugAlpha) + { + // build mode allows interaction with all transparent objects + // "Show Debug Alpha" means no object actually transparent + pick_transparent = TRUE; + } + + LLPickInfo pick_info(LLCoordGL(x, y_from_bot), mask, pick_transparent, TRUE, callback); + schedulePick(pick_info); +} + +void LLViewerWindow::schedulePick(LLPickInfo& pick_info) +{ + if (mPicks.size() >= 1024 || mWindow->getMinimized()) + { //something went wrong, picks are being scheduled but not processed + + if (pick_info.mPickCallback) + { + pick_info.mPickCallback(pick_info); + } + + return; + } + mPicks.push_back(pick_info); + + // delay further event processing until we receive results of pick + // only do this for async picks so that handleMouseUp won't be called + // until the pick triggered in handleMouseDown has been processed, for example + mWindow->delayInputProcessing(); +} + + +void LLViewerWindow::performPick() +{ + if (gNoRender) + { + return; + } + + if (!mPicks.empty()) + { + std::vector::iterator pick_it; + for (pick_it = mPicks.begin(); pick_it != mPicks.end(); ++pick_it) + { + pick_it->fetchResults(); + } + + mLastPick = mPicks.back(); + mPicks.clear(); + } +} + +void LLViewerWindow::returnEmptyPicks() +{ + std::vector::iterator pick_it; + for (pick_it = mPicks.begin(); pick_it != mPicks.end(); ++pick_it) + { + mLastPick = *pick_it; + // just trigger callback with empty results + if (pick_it->mPickCallback) + { + pick_it->mPickCallback(*pick_it); + } + } + mPicks.clear(); +} + +// Performs the GL object/land pick. +LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot, BOOL pick_transparent) +{ + if (gNoRender) + { + return LLPickInfo(); + } + + BOOL in_build_mode = LLFloaterReg::instanceVisible("build"); + if (in_build_mode || LLDrawPoolAlpha::sShowDebugAlpha) + { + // build mode allows interaction with all transparent objects + // "Show Debug Alpha" means no object actually transparent + pick_transparent = TRUE; + } + + // shortcut queueing in mPicks and just update mLastPick in place + mLastPick = LLPickInfo(LLCoordGL(x, y_from_bot), gKeyboard->currentMask(TRUE), pick_transparent, TRUE, NULL); + mLastPick.fetchResults(); + + return mLastPick; +} + +LLHUDIcon* LLViewerWindow::cursorIntersectIcon(S32 mouse_x, S32 mouse_y, F32 depth, + LLVector3* intersection) +{ + S32 x = mouse_x; + S32 y = mouse_y; + + if ((mouse_x == -1) && (mouse_y == -1)) // use current mouse position + { + x = getCurrentMouseX(); + y = getCurrentMouseY(); + } + + // world coordinates of mouse + LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y); + LLVector3 mouse_point_global = LLViewerCamera::getInstance()->getOrigin(); + LLVector3 mouse_world_start = mouse_point_global; + LLVector3 mouse_world_end = mouse_point_global + mouse_direction_global * depth; + + return LLHUDIcon::lineSegmentIntersectAll(mouse_world_start, mouse_world_end, intersection); + + +} + +LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 depth, + LLViewerObject *this_object, + S32 this_face, + BOOL pick_transparent, + S32* face_hit, + LLVector3 *intersection, + LLVector2 *uv, + LLVector3 *normal, + LLVector3 *binormal) +{ + S32 x = mouse_x; + S32 y = mouse_y; + + if ((mouse_x == -1) && (mouse_y == -1)) // use current mouse position + { + x = getCurrentMouseX(); + y = getCurrentMouseY(); + } + + // HUD coordinates of mouse + LLVector3 mouse_point_hud = mousePointHUD(x, y); + LLVector3 mouse_hud_start = mouse_point_hud - LLVector3(depth, 0, 0); + LLVector3 mouse_hud_end = mouse_point_hud + LLVector3(depth, 0, 0); + + // world coordinates of mouse + LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y); + LLVector3 mouse_point_global = LLViewerCamera::getInstance()->getOrigin(); + + //get near clip plane + LLVector3 n = LLViewerCamera::getInstance()->getAtAxis(); + LLVector3 p = mouse_point_global + n * LLViewerCamera::getInstance()->getNear(); + + //project mouse point onto plane + LLVector3 pos; + line_plane(mouse_point_global, mouse_direction_global, p, n, pos); + mouse_point_global = pos; + + LLVector3 mouse_world_start = mouse_point_global; + LLVector3 mouse_world_end = mouse_point_global + mouse_direction_global * depth; + + + LLViewerObject* found = NULL; + + if (this_object) // check only this object + { + if (this_object->isHUDAttachment()) // is a HUD object? + { + if (this_object->lineSegmentIntersect(mouse_hud_start, mouse_hud_end, this_face, pick_transparent, + face_hit, intersection, uv, normal, binormal)) + { + found = this_object; + } + } + + else // is a world object + { + if (this_object->lineSegmentIntersect(mouse_world_start, mouse_world_end, this_face, pick_transparent, + face_hit, intersection, uv, normal, binormal)) + { + found = this_object; + } + } + } + + else // check ALL objects + { + found = gPipeline.lineSegmentIntersectInHUD(mouse_hud_start, mouse_hud_end, pick_transparent, + face_hit, intersection, uv, normal, binormal); + + if (!found) // if not found in HUD, look in world: + + { + found = gPipeline.lineSegmentIntersectInWorld(mouse_world_start, mouse_world_end, pick_transparent, + face_hit, intersection, uv, normal, binormal); + } + + } + + return found; +} + +// Returns unit vector relative to camera +// indicating direction of point on screen x,y +LLVector3 LLViewerWindow::mouseDirectionGlobal(const S32 x, const S32 y) const +{ + // find vertical field of view + F32 fov = LLViewerCamera::getInstance()->getView(); + + // find world view center in scaled ui coordinates + F32 center_x = getWorldViewRectScaled().getCenterX(); + F32 center_y = getWorldViewRectScaled().getCenterY(); + + // calculate pixel distance to screen + F32 distance = ((F32)getWorldViewHeightScaled() * 0.5f) / (tan(fov / 2.f)); + + // calculate click point relative to middle of screen + F32 click_x = x - center_x; + F32 click_y = y - center_y; + + // compute mouse vector + LLVector3 mouse_vector = distance * LLViewerCamera::getInstance()->getAtAxis() + - click_x * LLViewerCamera::getInstance()->getLeftAxis() + + click_y * LLViewerCamera::getInstance()->getUpAxis(); + + mouse_vector.normVec(); + + return mouse_vector; +} + +LLVector3 LLViewerWindow::mousePointHUD(const S32 x, const S32 y) const +{ + // find screen resolution + S32 height = getWorldViewHeightScaled(); + + // find world view center + F32 center_x = getWorldViewRectScaled().getCenterX(); + F32 center_y = getWorldViewRectScaled().getCenterY(); + + // remap with uniform scale (1/height) so that top is -0.5, bottom is +0.5 + F32 hud_x = -((F32)x - center_x) / height; + F32 hud_y = ((F32)y - center_y) / height; + + return LLVector3(0.f, hud_x/gAgentCamera.mHUDCurZoom, hud_y/gAgentCamera.mHUDCurZoom); +} + +// Returns unit vector relative to camera in camera space +// indicating direction of point on screen x,y +LLVector3 LLViewerWindow::mouseDirectionCamera(const S32 x, const S32 y) const +{ + // find vertical field of view + F32 fov_height = LLViewerCamera::getInstance()->getView(); + F32 fov_width = fov_height * LLViewerCamera::getInstance()->getAspect(); + + // find screen resolution + S32 height = getWorldViewHeightScaled(); + S32 width = getWorldViewWidthScaled(); + + // find world view center + F32 center_x = getWorldViewRectScaled().getCenterX(); + F32 center_y = getWorldViewRectScaled().getCenterY(); + + // calculate click point relative to middle of screen + F32 click_x = (((F32)x - center_x) / (F32)width) * fov_width * -1.f; + F32 click_y = (((F32)y - center_y) / (F32)height) * fov_height; + + // compute mouse vector + LLVector3 mouse_vector = LLVector3(0.f, 0.f, -1.f); + LLQuaternion mouse_rotate; + mouse_rotate.setQuat(click_y, click_x, 0.f); + + mouse_vector = mouse_vector * mouse_rotate; + // project to z = -1 plane; + mouse_vector = mouse_vector * (-1.f / mouse_vector.mV[VZ]); + + return mouse_vector; +} + + + +BOOL LLViewerWindow::mousePointOnPlaneGlobal(LLVector3d& point, const S32 x, const S32 y, + const LLVector3d &plane_point_global, + const LLVector3 &plane_normal_global) +{ + LLVector3d mouse_direction_global_d; + + mouse_direction_global_d.setVec(mouseDirectionGlobal(x,y)); + LLVector3d plane_normal_global_d; + plane_normal_global_d.setVec(plane_normal_global); + F64 plane_mouse_dot = (plane_normal_global_d * mouse_direction_global_d); + LLVector3d plane_origin_camera_rel = plane_point_global - gAgentCamera.getCameraPositionGlobal(); + F64 mouse_look_at_scale = (plane_normal_global_d * plane_origin_camera_rel) + / plane_mouse_dot; + if (llabs(plane_mouse_dot) < 0.00001) + { + // if mouse is parallel to plane, return closest point on line through plane origin + // that is parallel to camera plane by scaling mouse direction vector + // by distance to plane origin, modulated by deviation of mouse direction from plane origin + LLVector3d plane_origin_dir = plane_origin_camera_rel; + plane_origin_dir.normVec(); + + mouse_look_at_scale = plane_origin_camera_rel.magVec() / (plane_origin_dir * mouse_direction_global_d); + } + + point = gAgentCamera.getCameraPositionGlobal() + mouse_look_at_scale * mouse_direction_global_d; + + return mouse_look_at_scale > 0.0; +} + + +// Returns global position +BOOL LLViewerWindow::mousePointOnLandGlobal(const S32 x, const S32 y, LLVector3d *land_position_global) +{ + LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y); + F32 mouse_dir_scale; + BOOL hit_land = FALSE; + LLViewerRegion *regionp; + F32 land_z; + const F32 FIRST_PASS_STEP = 1.0f; // meters + const F32 SECOND_PASS_STEP = 0.1f; // meters + LLVector3d camera_pos_global; + + camera_pos_global = gAgentCamera.getCameraPositionGlobal(); + LLVector3d probe_point_global; + LLVector3 probe_point_region; + + // walk forwards to find the point + for (mouse_dir_scale = FIRST_PASS_STEP; mouse_dir_scale < gAgentCamera.mDrawDistance; mouse_dir_scale += FIRST_PASS_STEP) + { + LLVector3d mouse_direction_global_d; + mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale); + probe_point_global = camera_pos_global + mouse_direction_global_d; + + regionp = LLWorld::getInstance()->resolveRegionGlobal(probe_point_region, probe_point_global); + + if (!regionp) + { + // ...we're outside the world somehow + continue; + } + + S32 i = (S32) (probe_point_region.mV[VX]/regionp->getLand().getMetersPerGrid()); + S32 j = (S32) (probe_point_region.mV[VY]/regionp->getLand().getMetersPerGrid()); + S32 grids_per_edge = (S32) regionp->getLand().mGridsPerEdge; + if ((i >= grids_per_edge) || (j >= grids_per_edge)) + { + //llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl; + continue; + } + + land_z = regionp->getLand().resolveHeightRegion(probe_point_region); + + //llinfos << "mousePointOnLand initial z " << land_z << llendl; + + if (probe_point_region.mV[VZ] < land_z) + { + // ...just went under land + + // cout << "under land at " << probe_point << " scale " << mouse_vec_scale << endl; + + hit_land = TRUE; + break; + } + } + + + if (hit_land) + { + // Don't go more than one step beyond where we stopped above. + // This can't just be "mouse_vec_scale" because floating point error + // will stop the loop before the last increment.... X - 1.0 + 0.1 + 0.1 + ... + 0.1 != X + F32 stop_mouse_dir_scale = mouse_dir_scale + FIRST_PASS_STEP; + + // take a step backwards, then walk forwards again to refine position + for ( mouse_dir_scale -= FIRST_PASS_STEP; mouse_dir_scale <= stop_mouse_dir_scale; mouse_dir_scale += SECOND_PASS_STEP) + { + LLVector3d mouse_direction_global_d; + mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale); + probe_point_global = camera_pos_global + mouse_direction_global_d; + + regionp = LLWorld::getInstance()->resolveRegionGlobal(probe_point_region, probe_point_global); + + if (!regionp) + { + // ...we're outside the world somehow + continue; + } + + /* + i = (S32) (local_probe_point.mV[VX]/regionp->getLand().getMetersPerGrid()); + j = (S32) (local_probe_point.mV[VY]/regionp->getLand().getMetersPerGrid()); + if ((i >= regionp->getLand().mGridsPerEdge) || (j >= regionp->getLand().mGridsPerEdge)) + { + // llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl; + continue; + } + land_z = regionp->getLand().mSurfaceZ[ i + j * (regionp->getLand().mGridsPerEdge) ]; + */ + + land_z = regionp->getLand().resolveHeightRegion(probe_point_region); + + //llinfos << "mousePointOnLand refine z " << land_z << llendl; + + if (probe_point_region.mV[VZ] < land_z) + { + // ...just went under land again + + *land_position_global = probe_point_global; + return TRUE; + } + } + } + + return FALSE; +} + +// Saves an image to the harddrive as "SnapshotX" where X >= 1. +BOOL LLViewerWindow::saveImageNumbered(LLImageFormatted *image) +{ + if (!image) + { + return FALSE; + } + + LLFilePicker::ESaveFilter pick_type; + std::string extension("." + image->getExtension()); + if (extension == ".j2c") + pick_type = LLFilePicker::FFSAVE_J2C; + else if (extension == ".bmp") + pick_type = LLFilePicker::FFSAVE_BMP; + else if (extension == ".jpg") + pick_type = LLFilePicker::FFSAVE_JPEG; + else if (extension == ".png") + pick_type = LLFilePicker::FFSAVE_PNG; + else if (extension == ".tga") + pick_type = LLFilePicker::FFSAVE_TGA; + else + pick_type = LLFilePicker::FFSAVE_ALL; // ??? + + // Get a base file location if needed. + if ( ! isSnapshotLocSet()) + { + std::string proposed_name( sSnapshotBaseName ); + + // getSaveFile will append an appropriate extension to the proposed name, based on the ESaveFilter constant passed in. + + // pick a directory in which to save + LLFilePicker& picker = LLFilePicker::instance(); + if (!picker.getSaveFile(pick_type, proposed_name)) + { + // Clicked cancel + return FALSE; + } + + // Copy the directory + file name + std::string filepath = picker.getFirstFile(); + + LLViewerWindow::sSnapshotBaseName = gDirUtilp->getBaseFileName(filepath, true); + LLViewerWindow::sSnapshotDir = gDirUtilp->getDirName(filepath); + } + + // Look for an unused file name + std::string filepath; + S32 i = 1; + S32 err = 0; + + do + { + filepath = sSnapshotDir; + filepath += gDirUtilp->getDirDelimiter(); + filepath += sSnapshotBaseName; + filepath += llformat("_%.3d",i); + filepath += extension; + + llstat stat_info; + err = LLFile::stat( filepath, &stat_info ); + i++; + } + while( -1 != err ); // search until the file is not found (i.e., stat() gives an error). + + return image->save(filepath); +} + +void LLViewerWindow::resetSnapshotLoc() +{ + sSnapshotDir.clear(); +} + +static S32 BORDERHEIGHT = 0; +static S32 BORDERWIDTH = 0; + +// static +void LLViewerWindow::movieSize(S32 new_width, S32 new_height) +{ + LLCoordScreen size; + gViewerWindow->mWindow->getSize(&size); + if ( (size.mX != new_width + BORDERWIDTH) + ||(size.mY != new_height + BORDERHEIGHT)) + { + // use actual display dimensions, not virtual UI dimensions + S32 x = gViewerWindow->getWindowWidthRaw(); + S32 y = gViewerWindow->getWindowHeightRaw(); + BORDERWIDTH = size.mX - x; + BORDERHEIGHT = size.mY- y; + LLCoordScreen new_size(new_width + BORDERWIDTH, + new_height + BORDERHEIGHT); + gViewerWindow->mWindow->setSize(new_size); + } +} + +BOOL LLViewerWindow::saveSnapshot( const std::string& filepath, S32 image_width, S32 image_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type) +{ + llinfos << "Saving snapshot to: " << filepath << llendl; + + LLPointer raw = new LLImageRaw; + BOOL success = rawSnapshot(raw, image_width, image_height, TRUE, FALSE, show_ui, do_rebuild); + + if (success) + { + LLPointer bmp_image = new LLImageBMP; + success = bmp_image->encode(raw, 0.0f); + if( success ) + { + success = bmp_image->save(filepath); + } + else + { + llwarns << "Unable to encode bmp snapshot" << llendl; + } + } + else + { + llwarns << "Unable to capture raw snapshot" << llendl; + } + + return success; +} + + +void LLViewerWindow::playSnapshotAnimAndSound() +{ + if (gSavedSettings.getBOOL("QuietSnapshotsToDisk")) + { + return; + } + gAgent.sendAnimationRequest(ANIM_AGENT_SNAPSHOT, ANIM_REQUEST_START); + send_sound_trigger(LLUUID(gSavedSettings.getString("UISndSnapshot")), 1.0f); +} + +BOOL LLViewerWindow::thumbnailSnapshot(LLImageRaw *raw, S32 preview_width, S32 preview_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type) +{ + return rawSnapshot(raw, preview_width, preview_height, FALSE, FALSE, show_ui, do_rebuild, type); +} + +// Saves the image from the screen to the specified filename and path. +BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height, + BOOL keep_window_aspect, BOOL is_texture, BOOL show_ui, BOOL do_rebuild, ESnapshotType type, S32 max_size) +{ + if (!raw) + { + return FALSE; + } + + // PRE SNAPSHOT + gDisplaySwapBuffers = FALSE; + + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + setCursor(UI_CURSOR_WAIT); + + // Hide all the UI widgets first and draw a frame + BOOL prev_draw_ui = gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI) ? TRUE : FALSE; + + show_ui = show_ui ? TRUE : FALSE; + + if ( prev_draw_ui != show_ui) + { + LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI); + } + + BOOL hide_hud = !gSavedSettings.getBOOL("RenderHUDInSnapshot") && LLPipeline::sShowHUDAttachments; + if (hide_hud) + { + LLPipeline::sShowHUDAttachments = FALSE; + } + + // if not showing ui, use full window to render world view + updateWorldViewRect(!show_ui); + + // Copy screen to a buffer + // crop sides or top and bottom, if taking a snapshot of different aspect ratio + // from window + LLRect window_rect = show_ui ? getWindowRectRaw() : getWorldViewRectRaw(); + + S32 snapshot_width = window_rect.getWidth(); + S32 snapshot_height = window_rect.getHeight(); + // SNAPSHOT + S32 window_width = snapshot_width; + S32 window_height = snapshot_height; + + if (show_ui) + { + image_width = llmin(image_width, window_width); + image_height = llmin(image_height, window_height); + } + + F32 scale_factor = 1.0f ; + if(!keep_window_aspect) //image cropping + { + F32 ratio = llmin( (F32)window_width / image_width , (F32)window_height / image_height) ; + snapshot_width = (S32)(ratio * image_width) ; + snapshot_height = (S32)(ratio * image_height) ; + scale_factor = llmax(1.0f, 1.0f / ratio) ; + } + else //the scene(window) proportion needs to be maintained. + { + if(image_width > window_width || image_height > window_height) //need to enlarge the scene + { + F32 ratio = llmin( (F32)window_width / image_width , (F32)window_height / image_height) ; + snapshot_width = (S32)(ratio * image_width) ; + snapshot_height = (S32)(ratio * image_height) ; + scale_factor = llmax(1.0f, 1.0f / ratio) ; + } + } + + if (show_ui && scale_factor > 1.f) + { + llwarns << "over scaling UI not supported." << llendl; + } + + S32 buffer_x_offset = llfloor(((window_width - snapshot_width) * scale_factor) / 2.f); + S32 buffer_y_offset = llfloor(((window_height - snapshot_height) * scale_factor) / 2.f); + + S32 image_buffer_x = llfloor(snapshot_width*scale_factor) ; + S32 image_buffer_y = llfloor(snapshot_height *scale_factor) ; + + if(image_buffer_x > max_size || image_buffer_y > max_size) //boundary check to avoid memory overflow + { + scale_factor *= llmin((F32)max_size / image_buffer_x, (F32)max_size / image_buffer_y) ; + image_buffer_x = llfloor(snapshot_width*scale_factor) ; + image_buffer_y = llfloor(snapshot_height *scale_factor) ; + } + if(image_buffer_x > 0 && image_buffer_y > 0) + { + raw->resize(image_buffer_x, image_buffer_y, 3); + } + else + { + return FALSE ; + } + if(raw->isBufferInvalid()) + { + return FALSE ; + } + + BOOL high_res = scale_factor >= 2.f; // Font scaling is slow, only do so if rez is much higher + if (high_res && show_ui) + { + llwarns << "High res UI snapshot not supported. " << llendl; + /*send_agent_pause(); + //rescale fonts + initFonts(scale_factor); + LLHUDObject::reshapeAll();*/ + } + + S32 output_buffer_offset_y = 0; + + F32 depth_conversion_factor_1 = (LLViewerCamera::getInstance()->getFar() + LLViewerCamera::getInstance()->getNear()) / (2.f * LLViewerCamera::getInstance()->getFar() * LLViewerCamera::getInstance()->getNear()); + F32 depth_conversion_factor_2 = (LLViewerCamera::getInstance()->getFar() - LLViewerCamera::getInstance()->getNear()) / (2.f * LLViewerCamera::getInstance()->getFar() * LLViewerCamera::getInstance()->getNear()); + + gObjectList.generatePickList(*LLViewerCamera::getInstance()); + + for (int subimage_y = 0; subimage_y < scale_factor; ++subimage_y) + { + S32 subimage_y_offset = llclamp(buffer_y_offset - (subimage_y * window_height), 0, window_height);; + // handle fractional columns + U32 read_height = llmax(0, (window_height - subimage_y_offset) - + llmax(0, (window_height * (subimage_y + 1)) - (buffer_y_offset + raw->getHeight()))); + + S32 output_buffer_offset_x = 0; + for (int subimage_x = 0; subimage_x < scale_factor; ++subimage_x) + { + gDisplaySwapBuffers = FALSE; + gDepthDirty = TRUE; + + const U32 subfield = subimage_x+(subimage_y*llceil(scale_factor)); + + if (LLPipeline::sRenderDeferred) + { + display(do_rebuild, scale_factor, subfield, TRUE); + } + else + { + display(do_rebuild, scale_factor, subfield, TRUE); + // Required for showing the GUI in snapshots and performing bloom composite overlay + // Call even if show_ui is FALSE + render_ui(scale_factor, subfield); + } + + S32 subimage_x_offset = llclamp(buffer_x_offset - (subimage_x * window_width), 0, window_width); + // handle fractional rows + U32 read_width = llmax(0, (window_width - subimage_x_offset) - + llmax(0, (window_width * (subimage_x + 1)) - (buffer_x_offset + raw->getWidth()))); + for(U32 out_y = 0; out_y < read_height ; out_y++) + { + S32 output_buffer_offset = ( + (out_y * (raw->getWidth())) // ...plus iterated y... + + (window_width * subimage_x) // ...plus subimage start in x... + + (raw->getWidth() * window_height * subimage_y) // ...plus subimage start in y... + - output_buffer_offset_x // ...minus buffer padding x... + - (output_buffer_offset_y * (raw->getWidth())) // ...minus buffer padding y... + ) * raw->getComponents(); + + // Ping the wathdog thread every 100 lines to keep us alive (arbitrary number, feel free to change) + if (out_y % 100 == 0) + { + LLAppViewer::instance()->pingMainloopTimeout("LLViewerWindow::rawSnapshot"); + } + + if (type == SNAPSHOT_TYPE_COLOR) + { + glReadPixels( + subimage_x_offset, out_y + subimage_y_offset, + read_width, 1, + GL_RGB, GL_UNSIGNED_BYTE, + raw->getData() + output_buffer_offset + ); + } + else // SNAPSHOT_TYPE_DEPTH + { + LLPointer depth_line_buffer = new LLImageRaw(read_width, 1, sizeof(GL_FLOAT)); // need to store floating point values + glReadPixels( + subimage_x_offset, out_y + subimage_y_offset, + read_width, 1, + GL_DEPTH_COMPONENT, GL_FLOAT, + depth_line_buffer->getData()// current output pixel is beginning of buffer... + ); + + for (S32 i = 0; i < (S32)read_width; i++) + { + F32 depth_float = *(F32*)(depth_line_buffer->getData() + (i * sizeof(F32))); + + F32 linear_depth_float = 1.f / (depth_conversion_factor_1 - (depth_float * depth_conversion_factor_2)); + U8 depth_byte = F32_to_U8(linear_depth_float, LLViewerCamera::getInstance()->getNear(), LLViewerCamera::getInstance()->getFar()); + //write converted scanline out to result image + for(S32 j = 0; j < raw->getComponents(); j++) + { + *(raw->getData() + output_buffer_offset + (i * raw->getComponents()) + j) = depth_byte; + } + } + } + } + output_buffer_offset_x += subimage_x_offset; + stop_glerror(); + } + output_buffer_offset_y += subimage_y_offset; + } + + gDisplaySwapBuffers = FALSE; + gDepthDirty = TRUE; + + // POST SNAPSHOT + if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI); + } + + if (hide_hud) + { + LLPipeline::sShowHUDAttachments = TRUE; + } + + /*if (high_res) + { + initFonts(1.f); + LLHUDObject::reshapeAll(); + }*/ + + // Pre-pad image to number of pixels such that the line length is a multiple of 4 bytes (for BMP encoding) + // Note: this formula depends on the number of components being 3. Not obvious, but it's correct. + image_width += (image_width * 3) % 4; + + BOOL ret = TRUE ; + // Resize image + if(llabs(image_width - image_buffer_x) > 4 || llabs(image_height - image_buffer_y) > 4) + { + ret = raw->scale( image_width, image_height ); + } + else if(image_width != image_buffer_x || image_height != image_buffer_y) + { + ret = raw->scale( image_width, image_height, FALSE ); + } + + + setCursor(UI_CURSOR_ARROW); + + if (do_rebuild) + { + // If we had to do a rebuild, that means that the lists of drawables to be rendered + // was empty before we started. + // Need to reset these, otherwise we call state sort on it again when render gets called the next time + // and we stand a good chance of crashing on rebuild because the render drawable arrays have multiple copies of + // objects on them. + gPipeline.resetDrawOrders(); + } + + if (high_res) + { + send_agent_resume(); + } + + return ret; +} + +void LLViewerWindow::destroyWindow() +{ + if (mWindow) + { + LLWindowManager::destroyWindow(mWindow); + } + mWindow = NULL; +} + + +void LLViewerWindow::drawMouselookInstructions() +{ + // Draw instructions for mouselook ("Press ESC to return to World View" partially transparent at the bottom of the screen.) + const std::string instructions = LLTrans::getString("LeaveMouselook"); + const LLFontGL* font = LLFontGL::getFont(LLFontDescriptor("SansSerif", "Large", LLFontGL::BOLD)); + + //to be on top of Bottom bar when it is opened + const S32 INSTRUCTIONS_PAD = 50; + + font->renderUTF8( + instructions, 0, + getWorldViewRectScaled().getCenterX(), + getWorldViewRectScaled().mBottom + INSTRUCTIONS_PAD, + LLColor4( 1.0f, 1.0f, 1.0f, 0.5f ), + LLFontGL::HCENTER, LLFontGL::TOP, + LLFontGL::NORMAL,LLFontGL::DROP_SHADOW); +} + +void* LLViewerWindow::getPlatformWindow() const +{ + return mWindow->getPlatformWindow(); +} + +void* LLViewerWindow::getMediaWindow() const +{ + return mWindow->getMediaWindow(); +} + +void LLViewerWindow::focusClient() const +{ + return mWindow->focusClient(); +} + +LLRootView* LLViewerWindow::getRootView() const +{ + return mRootView; +} + +LLRect LLViewerWindow::getWorldViewRectScaled() const +{ + return mWorldViewRectScaled; +} + +S32 LLViewerWindow::getWorldViewHeightScaled() const +{ + return mWorldViewRectScaled.getHeight(); +} + +S32 LLViewerWindow::getWorldViewWidthScaled() const +{ + return mWorldViewRectScaled.getWidth(); +} + + +S32 LLViewerWindow::getWorldViewHeightRaw() const +{ + return mWorldViewRectRaw.getHeight(); +} + +S32 LLViewerWindow::getWorldViewWidthRaw() const +{ + return mWorldViewRectRaw.getWidth(); +} + +S32 LLViewerWindow::getWindowHeightScaled() const +{ + return mWindowRectScaled.getHeight(); +} + +S32 LLViewerWindow::getWindowWidthScaled() const +{ + return mWindowRectScaled.getWidth(); +} + +S32 LLViewerWindow::getWindowHeightRaw() const +{ + return mWindowRectRaw.getHeight(); +} + +S32 LLViewerWindow::getWindowWidthRaw() const +{ + return mWindowRectRaw.getWidth(); +} + +void LLViewerWindow::setup2DRender() +{ + // setup ortho camera + gl_state_for_2d(mWindowRectRaw.getWidth(), mWindowRectRaw.getHeight()); + setup2DViewport(); +} + +void LLViewerWindow::setup2DViewport(S32 x_offset, S32 y_offset) +{ + gGLViewport[0] = mWindowRectRaw.mLeft + x_offset; + gGLViewport[1] = mWindowRectRaw.mBottom + y_offset; + gGLViewport[2] = mWindowRectRaw.getWidth(); + gGLViewport[3] = mWindowRectRaw.getHeight(); + glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); +} + + +void LLViewerWindow::setup3DRender() +{ + // setup perspective camera + LLViewerCamera::getInstance()->setPerspective(NOT_FOR_SELECTION, mWorldViewRectRaw.mLeft, mWorldViewRectRaw.mBottom, mWorldViewRectRaw.getWidth(), mWorldViewRectRaw.getHeight(), FALSE, LLViewerCamera::getInstance()->getNear(), MAX_FAR_CLIP*2.f); + setup3DViewport(); +} + +void LLViewerWindow::setup3DViewport(S32 x_offset, S32 y_offset) +{ + gGLViewport[0] = mWorldViewRectRaw.mLeft + x_offset; + gGLViewport[1] = mWorldViewRectRaw.mBottom + y_offset; + gGLViewport[2] = mWorldViewRectRaw.getWidth(); + gGLViewport[3] = mWorldViewRectRaw.getHeight(); + glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); +} + +void LLViewerWindow::setShowProgress(const BOOL show) +{ + if (mProgressView) + { + mProgressView->setVisible(show); + } +} + +BOOL LLViewerWindow::getShowProgress() const +{ + return (mProgressView && mProgressView->getVisible()); +} + +void LLViewerWindow::setProgressString(const std::string& string) +{ + if (mProgressView) + { + mProgressView->setText(string); + } +} + +void LLViewerWindow::setProgressMessage(const std::string& msg) +{ + if(mProgressView) + { + mProgressView->setMessage(msg); + } +} + +void LLViewerWindow::setProgressPercent(const F32 percent) +{ + if (mProgressView) + { + mProgressView->setPercent(percent); + } +} + +void LLViewerWindow::setProgressCancelButtonVisible( BOOL b, const std::string& label ) +{ + if (mProgressView) + { + mProgressView->setCancelButtonVisible( b, label ); + } +} + + +LLProgressView *LLViewerWindow::getProgressView() const +{ + return mProgressView; +} + +void LLViewerWindow::dumpState() +{ + llinfos << "LLViewerWindow Active " << S32(mActive) << llendl; + llinfos << "mWindow visible " << S32(mWindow->getVisible()) + << " minimized " << S32(mWindow->getMinimized()) + << llendl; +} + +void LLViewerWindow::stopGL(BOOL save_state) +{ + //Note: --bao + //if not necessary, do not change the order of the function calls in this function. + //if change something, make sure it will not break anything. + //especially be careful to put anything behind gTextureList.destroyGL(save_state); + if (!gGLManager.mIsDisabled) + { + llinfos << "Shutting down GL..." << llendl; + + // Pause texture decode threads (will get unpaused during main loop) + LLAppViewer::getTextureCache()->pause(); + LLAppViewer::getImageDecodeThread()->pause(); + LLAppViewer::getTextureFetch()->pause(); + + gSky.destroyGL(); + stop_glerror(); + + LLManipTranslate::destroyGL() ; + stop_glerror(); + + gBumpImageList.destroyGL(); + stop_glerror(); + + LLFontGL::destroyAllGL(); + stop_glerror(); + + LLVOAvatar::destroyGL(); + stop_glerror(); + + LLViewerDynamicTexture::destroyGL(); + stop_glerror(); + + if (gPipeline.isInit()) + { + gPipeline.destroyGL(); + } + + gCone.cleanupGL(); + gBox.cleanupGL(); + gSphere.cleanupGL(); + gCylinder.cleanupGL(); + + if(gPostProcess) + { + gPostProcess->invalidate(); + } + + gTextureList.destroyGL(save_state); + stop_glerror(); + + gGLManager.mIsDisabled = TRUE; + stop_glerror(); + + llinfos << "Remaining allocated texture memory: " << LLImageGL::sGlobalTextureMemoryInBytes << " bytes" << llendl; + } +} + +void LLViewerWindow::restoreGL(const std::string& progress_message) +{ + //Note: --bao + //if not necessary, do not change the order of the function calls in this function. + //if change something, make sure it will not break anything. + //especially, be careful to put something before gTextureList.restoreGL(); + if (gGLManager.mIsDisabled) + { + llinfos << "Restoring GL..." << llendl; + gGLManager.mIsDisabled = FALSE; + + initGLDefaults(); + LLGLState::restoreGL(); + + gTextureList.restoreGL(); + + // for future support of non-square pixels, and fonts that are properly stretched + //LLFontGL::destroyDefaultFonts(); + initFonts(); + + gSky.restoreGL(); + gPipeline.restoreGL(); + LLDrawPoolWater::restoreGL(); + LLManipTranslate::restoreGL(); + + gBumpImageList.restoreGL(); + LLViewerDynamicTexture::restoreGL(); + LLVOAvatar::restoreGL(); + + gResizeScreenTexture = TRUE; + gWindowResized = TRUE; + + if (isAgentAvatarValid() && !gAgentAvatarp->isUsingBakedTextures()) + { + LLVisualParamHint::requestHintUpdates(); + } + + if (!progress_message.empty()) + { + gRestoreGLTimer.reset(); + gRestoreGL = TRUE; + setShowProgress(TRUE); + setProgressString(progress_message); + } + llinfos << "...Restoring GL done" << llendl; + if(!LLAppViewer::instance()->restoreErrorTrap()) + { + llwarns << " Someone took over my signal/exception handler (post restoreGL)!" << llendl; + } + + } +} + +void LLViewerWindow::initFonts(F32 zoom_factor) +{ + LLFontGL::destroyAllGL(); + // Initialize with possibly different zoom factor + LLFontGL::initClass( gSavedSettings.getF32("FontScreenDPI"), + mDisplayScale.mV[VX] * zoom_factor, + mDisplayScale.mV[VY] * zoom_factor, + gDirUtilp->getAppRODataDir(), + LLUI::getXUIPaths()); + // Force font reloads, which can be very slow + LLFontGL::loadDefaultFonts(); +} + +void LLViewerWindow::requestResolutionUpdate() +{ + mResDirty = true; +} + +void LLViewerWindow::checkSettings() +{ + if (mStatesDirty) + { + gGL.refreshState(); + LLViewerShaderMgr::instance()->setShaders(); + mStatesDirty = false; + } + + // We want to update the resolution AFTER the states getting refreshed not before. + if (mResDirty) + { + reshape(getWindowWidthRaw(), getWindowHeightRaw()); + mResDirty = false; + } +} + +void LLViewerWindow::restartDisplay(BOOL show_progress_bar) +{ + llinfos << "Restaring GL" << llendl; + stopGL(); + if (show_progress_bar) + { + restoreGL(LLTrans::getString("ProgressChangingResolution")); + } + else + { + restoreGL(); + } +} + +BOOL LLViewerWindow::changeDisplaySettings(LLCoordScreen size, BOOL disable_vsync, BOOL show_progress_bar) +{ + //BOOL was_maximized = gSavedSettings.getBOOL("WindowMaximized"); + + //gResizeScreenTexture = TRUE; + + //U32 fsaa = gSavedSettings.getU32("RenderFSAASamples"); + //U32 old_fsaa = mWindow->getFSAASamples(); + + // if not maximized, use the request size + if (!mWindow->getMaximized()) + { + mWindow->setSize(size); + } + + //if (fsaa == old_fsaa) + { + return TRUE; + } + +/* + + // Close floaters that don't handle settings change + LLFloaterReg::hideInstance("snapshot"); + + BOOL result_first_try = FALSE; + BOOL result_second_try = FALSE; + + LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); + send_agent_pause(); + llinfos << "Stopping GL during changeDisplaySettings" << llendl; + stopGL(); + mIgnoreActivate = TRUE; + LLCoordScreen old_size; + LLCoordScreen old_pos; + mWindow->getSize(&old_size); + + //mWindow->setFSAASamples(fsaa); + + result_first_try = mWindow->switchContext(false, size, disable_vsync); + if (!result_first_try) + { + // try to switch back + //mWindow->setFSAASamples(old_fsaa); + result_second_try = mWindow->switchContext(false, old_size, disable_vsync); + + if (!result_second_try) + { + // we are stuck...try once again with a minimal resolution? + send_agent_resume(); + mIgnoreActivate = FALSE; + return FALSE; + } + } + send_agent_resume(); + + llinfos << "Restoring GL during resolution change" << llendl; + if (show_progress_bar) + { + restoreGL(LLTrans::getString("ProgressChangingResolution")); + } + else + { + restoreGL(); + } + + if (!result_first_try) + { + LLSD args; + args["RESX"] = llformat("%d",size.mX); + args["RESY"] = llformat("%d",size.mY); + LLNotificationsUtil::add("ResolutionSwitchFail", args); + size = old_size; // for reshape below + } + + BOOL success = result_first_try || result_second_try; + + if (success) + { + // maximize window if was maximized, else reposition + if (was_maximized) + { + mWindow->maximize(); + } + else + { + S32 windowX = gSavedSettings.getS32("WindowX"); + S32 windowY = gSavedSettings.getS32("WindowY"); + + mWindow->setPosition(LLCoordScreen ( windowX, windowY ) ); + } + } + + mIgnoreActivate = FALSE; + gFocusMgr.setKeyboardFocus(keyboard_focus); + + return success; + + */ +} + +F32 LLViewerWindow::getWorldViewAspectRatio() const +{ + F32 world_aspect = (F32)mWorldViewRectRaw.getWidth() / (F32)mWorldViewRectRaw.getHeight(); + return world_aspect; +} + +void LLViewerWindow::calcDisplayScale() +{ + F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor"); + LLVector2 display_scale; + display_scale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f)); + display_scale *= ui_scale_factor; + + // limit minimum display scale + if (display_scale.mV[VX] < MIN_DISPLAY_SCALE || display_scale.mV[VY] < MIN_DISPLAY_SCALE) + { + display_scale *= MIN_DISPLAY_SCALE / llmin(display_scale.mV[VX], display_scale.mV[VY]); + } + + if (display_scale != mDisplayScale) + { + llinfos << "Setting display scale to " << display_scale << llendl; + + mDisplayScale = display_scale; + // Init default fonts + initFonts(); + } +} + +//static +LLRect LLViewerWindow::calcScaledRect(const LLRect & rect, const LLVector2& display_scale) +{ + LLRect res = rect; + res.mLeft = llround((F32)res.mLeft / display_scale.mV[VX]); + res.mRight = llround((F32)res.mRight / display_scale.mV[VX]); + res.mBottom = llround((F32)res.mBottom / display_scale.mV[VY]); + res.mTop = llround((F32)res.mTop / display_scale.mV[VY]); + + return res; +} + +S32 LLViewerWindow::getChatConsoleBottomPad() +{ + S32 offset = 0; + + if(LLBottomTray::instanceExists()) + offset += LLBottomTray::getInstance()->getRect().getHeight(); + + return offset; +} + +LLRect LLViewerWindow::getChatConsoleRect() +{ + LLRect full_window(0, getWindowHeightScaled(), getWindowWidthScaled(), 0); + LLRect console_rect = full_window; + + const S32 CONSOLE_PADDING_TOP = 24; + const S32 CONSOLE_PADDING_LEFT = 24; + const S32 CONSOLE_PADDING_RIGHT = 10; + + console_rect.mTop -= CONSOLE_PADDING_TOP; + console_rect.mBottom += getChatConsoleBottomPad(); + + console_rect.mLeft += CONSOLE_PADDING_LEFT; + + static const BOOL CHAT_FULL_WIDTH = gSavedSettings.getBOOL("ChatFullWidth"); + + if (CHAT_FULL_WIDTH) + { + console_rect.mRight -= CONSOLE_PADDING_RIGHT; + } + else + { + // Make console rect somewhat narrow so having inventory open is + // less of a problem. + console_rect.mRight = console_rect.mLeft + 2 * getWindowWidthScaled() / 3; + } + + return console_rect; +} +//---------------------------------------------------------------------------- + + +//static +bool LLViewerWindow::onAlert(const LLSD& notify) +{ + LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); + + if (gNoRender) + { + llinfos << "Alert: " << notification->getName() << llendl; + notification->respond(LLSD::emptyMap()); + LLNotifications::instance().cancel(notification); + return false; + } + + // If we're in mouselook, the mouse is hidden and so the user can't click + // the dialog buttons. In that case, change to First Person instead. + if( gAgentCamera.cameraMouselook() ) + { + gAgentCamera.changeCameraToDefault(); + } + return false; +} + +//////////////////////////////////////////////////////////////////////////// +// +// LLPickInfo +// +LLPickInfo::LLPickInfo() + : mKeyMask(MASK_NONE), + mPickCallback(NULL), + mPickType(PICK_INVALID), + mWantSurfaceInfo(FALSE), + mObjectFace(-1), + mUVCoords(-1.f, -1.f), + mSTCoords(-1.f, -1.f), + mXYCoords(-1, -1), + mIntersection(), + mNormal(), + mBinormal(), + mHUDIcon(NULL), + mPickTransparent(FALSE) +{ +} + +LLPickInfo::LLPickInfo(const LLCoordGL& mouse_pos, + MASK keyboard_mask, + BOOL pick_transparent, + BOOL pick_uv_coords, + void (*pick_callback)(const LLPickInfo& pick_info)) + : mMousePt(mouse_pos), + mKeyMask(keyboard_mask), + mPickCallback(pick_callback), + mPickType(PICK_INVALID), + mWantSurfaceInfo(pick_uv_coords), + mObjectFace(-1), + mUVCoords(-1.f, -1.f), + mSTCoords(-1.f, -1.f), + mXYCoords(-1, -1), + mNormal(), + mBinormal(), + mHUDIcon(NULL), + mPickTransparent(pick_transparent) +{ +} + +void LLPickInfo::fetchResults() +{ + + S32 face_hit = -1; + LLVector3 intersection, normal, binormal; + LLVector2 uv; + + LLHUDIcon* hit_icon = gViewerWindow->cursorIntersectIcon(mMousePt.mX, mMousePt.mY, 512.f, &intersection); + + F32 icon_dist = 0.f; + if (hit_icon) + { + icon_dist = (LLViewerCamera::getInstance()->getOrigin()-intersection).magVec(); + } + LLViewerObject* hit_object = gViewerWindow->cursorIntersect(mMousePt.mX, mMousePt.mY, 512.f, + NULL, -1, mPickTransparent, &face_hit, + &intersection, &uv, &normal, &binormal); + + mPickPt = mMousePt; + + U32 te_offset = face_hit > -1 ? face_hit : 0; + + //unproject relative clicked coordinate from window coordinate using GL + + LLViewerObject* objectp = hit_object; + + if (hit_icon && + (!objectp || + icon_dist < (LLViewerCamera::getInstance()->getOrigin()-intersection).magVec())) + { + // was this name referring to a hud icon? + mHUDIcon = hit_icon; + mPickType = PICK_ICON; + mPosGlobal = mHUDIcon->getPositionGlobal(); + } + else if (objectp) + { + if( objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH ) + { + // Hit land + mPickType = PICK_LAND; + mObjectID.setNull(); // land has no id + + // put global position into land_pos + LLVector3d land_pos; + if (!gViewerWindow->mousePointOnLandGlobal(mPickPt.mX, mPickPt.mY, &land_pos)) + { + // The selected point is beyond the draw distance or is otherwise + // not selectable. Return before calling mPickCallback(). + return; + } + + // Fudge the land focus a little bit above ground. + mPosGlobal = land_pos + LLVector3d::z_axis * 0.1f; + } + else + { + if(isFlora(objectp)) + { + mPickType = PICK_FLORA; + } + else + { + mPickType = PICK_OBJECT; + } + mObjectOffset = gAgentCamera.calcFocusOffset(objectp, intersection, mPickPt.mX, mPickPt.mY); + mObjectID = objectp->mID; + mObjectFace = (te_offset == NO_FACE) ? -1 : (S32)te_offset; + + mPosGlobal = gAgent.getPosGlobalFromAgent(intersection); + + if (mWantSurfaceInfo) + { + getSurfaceInfo(); + } + } + } + + if (mPickCallback) + { + mPickCallback(*this); + } +} + +LLPointer LLPickInfo::getObject() const +{ + return gObjectList.findObject( mObjectID ); +} + +void LLPickInfo::updateXYCoords() +{ + if (mObjectFace > -1) + { + const LLTextureEntry* tep = getObject()->getTE(mObjectFace); + LLPointer imagep = LLViewerTextureManager::getFetchedTexture(tep->getID()); + if(mUVCoords.mV[VX] >= 0.f && mUVCoords.mV[VY] >= 0.f && imagep.notNull()) + { + mXYCoords.mX = llround(mUVCoords.mV[VX] * (F32)imagep->getWidth()); + mXYCoords.mY = llround((1.f - mUVCoords.mV[VY]) * (F32)imagep->getHeight()); + } + } +} + +void LLPickInfo::getSurfaceInfo() +{ + // set values to uninitialized - this is what we return if no intersection is found + mObjectFace = -1; + mUVCoords = LLVector2(-1, -1); + mSTCoords = LLVector2(-1, -1); + mXYCoords = LLCoordScreen(-1, -1); + mIntersection = LLVector3(0,0,0); + mNormal = LLVector3(0,0,0); + mBinormal = LLVector3(0,0,0); + + LLViewerObject* objectp = getObject(); + + if (objectp) + { + if (gViewerWindow->cursorIntersect(llround((F32)mMousePt.mX), llround((F32)mMousePt.mY), 1024.f, + objectp, -1, mPickTransparent, + &mObjectFace, + &mIntersection, + &mSTCoords, + &mNormal, + &mBinormal)) + { + // if we succeeded with the intersect above, compute the texture coordinates: + + if (objectp->mDrawable.notNull() && mObjectFace > -1) + { + LLFace* facep = objectp->mDrawable->getFace(mObjectFace); + + mUVCoords = facep->surfaceToTexture(mSTCoords, mIntersection, mNormal); + } + + // and XY coords: + updateXYCoords(); + + } + } +} + + +/* code to get UV via a special UV render - removed in lieu of raycast method +LLVector2 LLPickInfo::pickUV() +{ + LLVector2 result(-1.f, -1.f); + + LLViewerObject* objectp = getObject(); + if (!objectp) + { + return result; + } + + if (mObjectFace > -1 && + objectp->mDrawable.notNull() && objectp->getPCode() == LL_PCODE_VOLUME && + mObjectFace < objectp->mDrawable->getNumFaces()) + { + S32 scaled_x = llround((F32)mPickPt.mX * gViewerWindow->getDisplayScale().mV[VX]); + S32 scaled_y = llround((F32)mPickPt.mY * gViewerWindow->getDisplayScale().mV[VY]); + const S32 UV_PICK_WIDTH = 5; + const S32 UV_PICK_HALF_WIDTH = (UV_PICK_WIDTH - 1) / 2; + U8 uv_pick_buffer[UV_PICK_WIDTH * UV_PICK_WIDTH * 4]; + LLFace* facep = objectp->mDrawable->getFace(mObjectFace); + if (facep) + { + LLGLState scissor_state(GL_SCISSOR_TEST); + scissor_state.enable(); + LLViewerCamera::getInstance()->setPerspective(FOR_SELECTION, scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, FALSE); + //glViewport(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH); + glScissor(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH); + + glClear(GL_DEPTH_BUFFER_BIT); + + facep->renderSelectedUV(); + + glReadPixels(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, GL_RGBA, GL_UNSIGNED_BYTE, uv_pick_buffer); + U8* center_pixel = &uv_pick_buffer[4 * ((UV_PICK_WIDTH * UV_PICK_HALF_WIDTH) + UV_PICK_HALF_WIDTH + 1)]; + + result.mV[VX] = (F32)((center_pixel[VGREEN] & 0xf) + (16.f * center_pixel[VRED])) / 4095.f; + result.mV[VY] = (F32)((center_pixel[VGREEN] >> 4) + (16.f * center_pixel[VBLUE])) / 4095.f; + } + } + + return result; +} */ + + +//static +bool LLPickInfo::isFlora(LLViewerObject* object) +{ + if (!object) return false; + + LLPCode pcode = object->getPCode(); + + if( (LL_PCODE_LEGACY_GRASS == pcode) + || (LL_PCODE_LEGACY_TREE == pcode) + || (LL_PCODE_TREE_NEW == pcode)) + { + return true; + } + return false; +} diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp index a933500706..b888a263d0 100644 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -71,6 +71,7 @@ LLVOCacheEntry::LLVOCacheEntry() } LLVOCacheEntry::LLVOCacheEntry(LLAPRFile* apr_file) + : mBuffer(NULL) { S32 size = -1; BOOL success; @@ -135,7 +136,10 @@ LLVOCacheEntry::LLVOCacheEntry(LLAPRFile* apr_file) LLVOCacheEntry::~LLVOCacheEntry() { - delete [] mBuffer; + if(mBuffer) + { + delete[] mBuffer; + } } diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index b692093fb9..a71539266d 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -853,7 +853,7 @@ void LLVoiceChannelP2P::activate() } // Add the party to the list of people with which we've recently interacted. - LLRecentPeople::instance().add(mOtherUserID); + addToTheRecentPeopleList(); //Default mic is ON on initiating/joining P2P calls if (!LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle()) @@ -938,3 +938,25 @@ void LLVoiceChannelP2P::setState(EState state) LLVoiceChannel::setState(state); } + +void LLVoiceChannelP2P::addToTheRecentPeopleList() +{ + bool avaline_call = LLIMModel::getInstance()->findIMSession(mSessionID)->isAvalineSessionType(); + + if (avaline_call) + { + LLSD call_data; + std::string call_number = LLVoiceChannel::getSessionName(); + + call_data["avaline_call"] = true; + call_data["session_id"] = mSessionID; + call_data["call_number"] = call_number; + call_data["date"] = LLDate::now(); + + LLRecentPeople::instance().add(mOtherUserID, call_data); + } + else + { + LLRecentPeople::instance().add(mOtherUserID); + } +} diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 7cef3c13d1..b8597ee5cb 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -191,6 +191,13 @@ protected: virtual void setState(EState state); private: + + /** + * Add the caller to the list of people with which we've recently interacted + * + **/ + void addToTheRecentPeopleList(); + std::string mSessionHandle; LLUUID mOtherUserID; BOOL mReceivedCall; diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 9db6d5e08c..8f7197c607 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -282,7 +282,9 @@ void LLWorld::removeRegion(const LLHost &host) updateWaterObjects(); - llassert_always(!gObjectList.hasMapObjectInRegion(regionp)) ; + //double check all objects of this region are removed. + gObjectList.clearAllMapObjectsInRegion(regionp) ; + //llassert_always(!gObjectList.hasMapObjectInRegion(regionp)) ; } @@ -1474,6 +1476,42 @@ void LLWorld::getAvatars(uuid_vec_t* avatar_ids, std::vector* positi } } } + // retrieve the list of close avatars from viewer objects as well + // for when we are above 1000m, only do this when we are retrieving + // uuid's too as there could be duplicates + if(avatar_ids != NULL) + { + for (std::vector::iterator iter = LLCharacter::sInstances.begin(); + iter != LLCharacter::sInstances.end(); ++iter) + { + LLVOAvatar* pVOAvatar = (LLVOAvatar*) *iter; + if(pVOAvatar->isDead() || pVOAvatar->isSelf()) + continue; + LLUUID uuid = pVOAvatar->getID(); + if(uuid.isNull()) + continue; + LLVector3d pos_global = pVOAvatar->getPositionGlobal(); + if(dist_vec(pos_global, relative_to) <= radius) + { + bool found = false; + uuid_vec_t::iterator sel_iter = avatar_ids->begin(); + for (; sel_iter != avatar_ids->end(); sel_iter++) + { + if(*sel_iter == uuid) + { + found = true; + break; + } + } + if(!found) + { + if(positions != NULL) + positions->push_back(pos_global); + avatar_ids->push_back(uuid); + } + } + } + } } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 7d9f63a408..99feb9a061 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -1,9006 +1,9024 @@ -/** - * @file pipeline.cpp - * @brief Rendering pipeline. - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "pipeline.h" - -// library includes -#include "llaudioengine.h" // For debugging. -#include "imageids.h" -#include "llerror.h" -#include "llviewercontrol.h" -#include "llfasttimer.h" -#include "llfontgl.h" -#include "llmemtype.h" -#include "llnamevalue.h" -#include "llpointer.h" -#include "llprimitive.h" -#include "llvolume.h" -#include "material_codes.h" -#include "timing.h" -#include "v3color.h" -#include "llui.h" -#include "llglheaders.h" -#include "llrender.h" -#include "llwindow.h" // swapBuffers() - -// newview includes -#include "llagent.h" -#include "llagentcamera.h" -#include "lldrawable.h" -#include "lldrawpoolalpha.h" -#include "lldrawpoolavatar.h" -#include "lldrawpoolground.h" -#include "lldrawpoolbump.h" -#include "lldrawpooltree.h" -#include "lldrawpoolwater.h" -#include "llface.h" -#include "llfeaturemanager.h" -#include "llfloatertelehub.h" -#include "llfloaterreg.h" -#include "llgldbg.h" -#include "llhudmanager.h" -#include "llhudnametag.h" -#include "llhudtext.h" -#include "lllightconstants.h" -#include "llresmgr.h" -#include "llselectmgr.h" -#include "llsky.h" -#include "lltracker.h" -#include "lltool.h" -#include "lltoolmgr.h" -#include "llviewercamera.h" -#include "llviewertexturelist.h" -#include "llviewerobject.h" -#include "llviewerobjectlist.h" -#include "llviewerparcelmgr.h" -#include "llviewerregion.h" // for audio debugging. -#include "llviewerwindow.h" // For getSpinAxis -#include "llvoavatarself.h" -#include "llvoground.h" -#include "llvosky.h" -#include "llvotree.h" -#include "llvovolume.h" -#include "llvosurfacepatch.h" -#include "llvowater.h" -#include "llvotree.h" -#include "llvopartgroup.h" -#include "llworld.h" -#include "llcubemap.h" -#include "llviewershadermgr.h" -#include "llviewerstats.h" -#include "llviewerjoystick.h" -#include "llviewerdisplay.h" -#include "llwlparammanager.h" -#include "llwaterparammanager.h" -#include "llspatialpartition.h" -#include "llmutelist.h" -#include "lltoolpie.h" - - -#ifdef _DEBUG -// Debug indices is disabled for now for debug performance - djs 4/24/02 -//#define DEBUG_INDICES -#else -//#define DEBUG_INDICES -#endif - -const F32 BACKLIGHT_DAY_MAGNITUDE_AVATAR = 0.2f; -const F32 BACKLIGHT_NIGHT_MAGNITUDE_AVATAR = 0.1f; -const F32 BACKLIGHT_DAY_MAGNITUDE_OBJECT = 0.1f; -const F32 BACKLIGHT_NIGHT_MAGNITUDE_OBJECT = 0.08f; -const S32 MAX_OFFSCREEN_GEOMETRY_CHANGES_PER_FRAME = 10; -const U32 REFLECTION_MAP_RES = 128; - -// Max number of occluders to search for. JC -const S32 MAX_OCCLUDER_COUNT = 2; - -extern S32 gBoxFrame; -//extern BOOL gHideSelectedObjects; -extern BOOL gDisplaySwapBuffers; -extern BOOL gDebugGL; - -// hack counter for rendering a fixed number of frames after toggling -// fullscreen to work around DEV-5361 -static S32 sDelayedVBOEnable = 0; - -BOOL gAvatarBacklight = FALSE; - -BOOL gDebugPipeline = FALSE; -LLPipeline gPipeline; -const LLMatrix4* gGLLastMatrix = NULL; - -LLFastTimer::DeclareTimer FTM_RENDER_GEOMETRY("Geometry"); -LLFastTimer::DeclareTimer FTM_RENDER_GRASS("Grass"); -LLFastTimer::DeclareTimer FTM_RENDER_INVISIBLE("Invisible"); -LLFastTimer::DeclareTimer FTM_RENDER_OCCLUSION("Occlusion"); -LLFastTimer::DeclareTimer FTM_RENDER_SHINY("Shiny"); -LLFastTimer::DeclareTimer FTM_RENDER_SIMPLE("Simple"); -LLFastTimer::DeclareTimer FTM_RENDER_TERRAIN("Terrain"); -LLFastTimer::DeclareTimer FTM_RENDER_TREES("Trees"); -LLFastTimer::DeclareTimer FTM_RENDER_UI("UI"); -LLFastTimer::DeclareTimer FTM_RENDER_WATER("Water"); -LLFastTimer::DeclareTimer FTM_RENDER_WL_SKY("Windlight Sky"); -LLFastTimer::DeclareTimer FTM_RENDER_ALPHA("Alpha Objects"); -LLFastTimer::DeclareTimer FTM_RENDER_CHARACTERS("Avatars"); -LLFastTimer::DeclareTimer FTM_RENDER_BUMP("Bump"); -LLFastTimer::DeclareTimer FTM_RENDER_FULLBRIGHT("Fullbright"); -LLFastTimer::DeclareTimer FTM_RENDER_GLOW("Glow"); -LLFastTimer::DeclareTimer FTM_GEO_UPDATE("Geo Update"); -LLFastTimer::DeclareTimer FTM_POOLRENDER("RenderPool"); -LLFastTimer::DeclareTimer FTM_POOLS("Pools"); -LLFastTimer::DeclareTimer FTM_RENDER_BLOOM_FBO("First FBO"); -LLFastTimer::DeclareTimer FTM_STATESORT("Sort Draw State"); -LLFastTimer::DeclareTimer FTM_PIPELINE("Pipeline"); -LLFastTimer::DeclareTimer FTM_CLIENT_COPY("Client Copy"); -LLFastTimer::DeclareTimer FTM_RENDER_DEFERRED("Deferred Shading"); - - -static LLFastTimer::DeclareTimer FTM_STATESORT_DRAWABLE("Sort Drawables"); -static LLFastTimer::DeclareTimer FTM_STATESORT_POSTSORT("Post Sort"); - -//---------------------------------------- -std::string gPoolNames[] = -{ - // Correspond to LLDrawpool enum render type - "NONE", - "POOL_SIMPLE", - "POOL_TERRAIN", - "POOL_BUMP", - "POOL_TREE", - "POOL_SKY", - "POOL_WL_SKY", - "POOL_GROUND", - "POOL_INVISIBLE", - "POOL_AVATAR", - "POOL_WATER", - "POOL_GRASS", - "POOL_FULLBRIGHT", - "POOL_GLOW", - "POOL_ALPHA", -}; - -void drawBox(const LLVector3& c, const LLVector3& r); -void drawBoxOutline(const LLVector3& pos, const LLVector3& size); - -U32 nhpo2(U32 v) -{ - U32 r = 1; - while (r < v) { - r *= 2; - } - return r; -} - -glh::matrix4f glh_copy_matrix(GLdouble* src) -{ - glh::matrix4f ret; - for (U32 i = 0; i < 16; i++) - { - ret.m[i] = (F32) src[i]; - } - return ret; -} - -glh::matrix4f glh_get_current_modelview() -{ - return glh_copy_matrix(gGLModelView); -} - -glh::matrix4f glh_get_current_projection() -{ - return glh_copy_matrix(gGLProjection); -} - -void glh_copy_matrix(const glh::matrix4f& src, GLdouble* dst) -{ - for (U32 i = 0; i < 16; i++) - { - dst[i] = src.m[i]; - } -} - -void glh_set_current_modelview(const glh::matrix4f& mat) -{ - glh_copy_matrix(mat, gGLModelView); -} - -void glh_set_current_projection(glh::matrix4f& mat) -{ - glh_copy_matrix(mat, gGLProjection); -} - -glh::matrix4f gl_ortho(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat znear, GLfloat zfar) -{ - glh::matrix4f ret( - 2.f/(right-left), 0.f, 0.f, -(right+left)/(right-left), - 0.f, 2.f/(top-bottom), 0.f, -(top+bottom)/(top-bottom), - 0.f, 0.f, -2.f/(zfar-znear), -(zfar+znear)/(zfar-znear), - 0.f, 0.f, 0.f, 1.f); - - return ret; -} - -void display_update_camera(); -//---------------------------------------- - -S32 LLPipeline::sCompiles = 0; - -BOOL LLPipeline::sPickAvatar = TRUE; -BOOL LLPipeline::sDynamicLOD = TRUE; -BOOL LLPipeline::sShowHUDAttachments = TRUE; -BOOL LLPipeline::sRenderPhysicalBeacons = TRUE; -BOOL LLPipeline::sRenderScriptedBeacons = FALSE; -BOOL LLPipeline::sRenderScriptedTouchBeacons = TRUE; -BOOL LLPipeline::sRenderParticleBeacons = FALSE; -BOOL LLPipeline::sRenderSoundBeacons = FALSE; -BOOL LLPipeline::sRenderBeacons = FALSE; -BOOL LLPipeline::sRenderHighlight = TRUE; -BOOL LLPipeline::sForceOldBakedUpload = FALSE; -S32 LLPipeline::sUseOcclusion = 0; -BOOL LLPipeline::sDelayVBUpdate = TRUE; -BOOL LLPipeline::sAutoMaskAlphaDeferred = TRUE; -BOOL LLPipeline::sAutoMaskAlphaNonDeferred = FALSE; -BOOL LLPipeline::sDisableShaders = FALSE; -BOOL LLPipeline::sRenderBump = TRUE; -BOOL LLPipeline::sUseTriStrips = TRUE; -BOOL LLPipeline::sUseFarClip = TRUE; -BOOL LLPipeline::sShadowRender = FALSE; -BOOL LLPipeline::sWaterReflections = FALSE; -BOOL LLPipeline::sRenderGlow = FALSE; -BOOL LLPipeline::sReflectionRender = FALSE; -BOOL LLPipeline::sImpostorRender = FALSE; -BOOL LLPipeline::sUnderWaterRender = FALSE; -BOOL LLPipeline::sTextureBindTest = FALSE; -BOOL LLPipeline::sRenderFrameTest = FALSE; -BOOL LLPipeline::sRenderAttachedLights = TRUE; -BOOL LLPipeline::sRenderAttachedParticles = TRUE; -BOOL LLPipeline::sRenderDeferred = FALSE; -BOOL LLPipeline::sAllowRebuildPriorityGroup = FALSE ; -S32 LLPipeline::sVisibleLightCount = 0; -F32 LLPipeline::sMinRenderSize = 0.f; - - -static LLCullResult* sCull = NULL; - -static const U32 gl_cube_face[] = -{ - GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, - GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, - GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, - GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, - GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, - GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, -}; - -void validate_framebuffer_object(); - - -void addDeferredAttachments(LLRenderTarget& target) -{ - target.addColorAttachment(GL_RGBA); //specular - target.addColorAttachment(GL_RGBA); //normal+z -} - -LLPipeline::LLPipeline() : - mBackfaceCull(FALSE), - mBatchCount(0), - mMatrixOpCount(0), - mTextureMatrixOps(0), - mMaxBatchSize(0), - mMinBatchSize(0), - mMeanBatchSize(0), - mTrianglesDrawn(0), - mNumVisibleNodes(0), - mVerticesRelit(0), - mLightingChanges(0), - mGeometryChanges(0), - mNumVisibleFaces(0), - - mInitialized(FALSE), - mVertexShadersEnabled(FALSE), - mVertexShadersLoaded(0), - mRenderDebugFeatureMask(0), - mRenderDebugMask(0), - mOldRenderDebugMask(0), - mLastRebuildPool(NULL), - mAlphaPool(NULL), - mSkyPool(NULL), - mTerrainPool(NULL), - mWaterPool(NULL), - mGroundPool(NULL), - mSimplePool(NULL), - mFullbrightPool(NULL), - mInvisiblePool(NULL), - mGlowPool(NULL), - mBumpPool(NULL), - mWLSkyPool(NULL), - mLightMask(0), - mLightMovingMask(0), - mLightingDetail(0), - mScreenWidth(0), - mScreenHeight(0) -{ - mNoiseMap = 0; - mTrueNoiseMap = 0; - mLightFunc = 0; -} - -void LLPipeline::init() -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_INIT); - - sDynamicLOD = gSavedSettings.getBOOL("RenderDynamicLOD"); - sRenderBump = gSavedSettings.getBOOL("RenderObjectBump"); - sUseTriStrips = gSavedSettings.getBOOL("RenderUseTriStrips"); - LLVertexBuffer::sUseStreamDraw = gSavedSettings.getBOOL("RenderUseStreamVBO"); - sRenderAttachedLights = gSavedSettings.getBOOL("RenderAttachedLights"); - sRenderAttachedParticles = gSavedSettings.getBOOL("RenderAttachedParticles"); - - mInitialized = TRUE; - - stop_glerror(); - - //create render pass pools - getPool(LLDrawPool::POOL_ALPHA); - getPool(LLDrawPool::POOL_SIMPLE); - getPool(LLDrawPool::POOL_GRASS); - getPool(LLDrawPool::POOL_FULLBRIGHT); - getPool(LLDrawPool::POOL_INVISIBLE); - getPool(LLDrawPool::POOL_BUMP); - getPool(LLDrawPool::POOL_GLOW); - - LLViewerStats::getInstance()->mTrianglesDrawnStat.reset(); - resetFrameStats(); - - for (U32 i = 0; i < NUM_RENDER_TYPES; ++i) - { - mRenderTypeEnabled[i] = TRUE; //all rendering types start enabled - } - - mRenderDebugFeatureMask = 0xffffffff; // All debugging features on - mRenderDebugMask = 0; // All debug starts off - - // Don't turn on ground when this is set - // Mac Books with intel 950s need this - if(!gSavedSettings.getBOOL("RenderGround")) - { - toggleRenderType(RENDER_TYPE_GROUND); - } - - mOldRenderDebugMask = mRenderDebugMask; - - mBackfaceCull = TRUE; - - stop_glerror(); - - // Enable features - - LLViewerShaderMgr::instance()->setShaders(); - - stop_glerror(); - - for (U32 i = 0; i < 2; ++i) - { - mSpotLightFade[i] = 1.f; - } - - setLightingDetail(-1); -} - -LLPipeline::~LLPipeline() -{ - -} - -void LLPipeline::cleanup() -{ - assertInitialized(); - - mGroupQ1.clear() ; - mGroupQ2.clear() ; - - for(pool_set_t::iterator iter = mPools.begin(); - iter != mPools.end(); ) - { - pool_set_t::iterator curiter = iter++; - LLDrawPool* poolp = *curiter; - if (poolp->isFacePool()) - { - LLFacePool* face_pool = (LLFacePool*) poolp; - if (face_pool->mReferences.empty()) - { - mPools.erase(curiter); - removeFromQuickLookup( poolp ); - delete poolp; - } - } - else - { - mPools.erase(curiter); - removeFromQuickLookup( poolp ); - delete poolp; - } - } - - if (!mTerrainPools.empty()) - { - llwarns << "Terrain Pools not cleaned up" << llendl; - } - if (!mTreePools.empty()) - { - llwarns << "Tree Pools not cleaned up" << llendl; - } - - delete mAlphaPool; - mAlphaPool = NULL; - delete mSkyPool; - mSkyPool = NULL; - delete mTerrainPool; - mTerrainPool = NULL; - delete mWaterPool; - mWaterPool = NULL; - delete mGroundPool; - mGroundPool = NULL; - delete mSimplePool; - mSimplePool = NULL; - delete mFullbrightPool; - mFullbrightPool = NULL; - delete mInvisiblePool; - mInvisiblePool = NULL; - delete mGlowPool; - mGlowPool = NULL; - delete mBumpPool; - mBumpPool = NULL; - // don't delete wl sky pool it was handled above in the for loop - //delete mWLSkyPool; - mWLSkyPool = NULL; - - releaseGLBuffers(); - - mFaceSelectImagep = NULL; - - mMovedBridge.clear(); - - mInitialized = FALSE; -} - -//============================================================================ - -void LLPipeline::destroyGL() -{ - stop_glerror(); - unloadShaders(); - mHighlightFaces.clear(); - - resetDrawOrders(); - - resetVertexBuffers(); - - releaseGLBuffers(); - - if (LLVertexBuffer::sEnableVBOs) - { - // render 30 frames after switching to work around DEV-5361 - sDelayedVBOEnable = 30; - LLVertexBuffer::sEnableVBOs = FALSE; - } -} - -static LLFastTimer::DeclareTimer FTM_RESIZE_SCREEN_TEXTURE("Resize Screen Texture"); - -void LLPipeline::resizeScreenTexture() -{ - LLFastTimer ft(FTM_RESIZE_SCREEN_TEXTURE); - if (gPipeline.canUseVertexShaders() && assertInitialized()) - { - GLuint resX = gViewerWindow->getWorldViewWidthRaw(); - GLuint resY = gViewerWindow->getWorldViewHeightRaw(); - - allocateScreenBuffer(resX,resY); - } -} - -void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY) -{ - // remember these dimensions - mScreenWidth = resX; - mScreenHeight = resY; - - //never use more than 4 samples for render targets - U32 samples = llmin(gSavedSettings.getU32("RenderFSAASamples"), (U32) 4); - U32 res_mod = gSavedSettings.getU32("RenderResolutionDivisor"); - - if (res_mod > 1 && res_mod < resX && res_mod < resY) - { - resX /= res_mod; - resY /= res_mod; - } - - if (gSavedSettings.getBOOL("RenderUIBuffer")) - { - mUIScreen.allocate(resX,resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); - } - - if (LLPipeline::sRenderDeferred) - { - //allocate deferred rendering color buffers - mDeferredScreen.allocate(resX, resY, GL_RGBA, TRUE, TRUE, LLTexUnit::TT_RECT_TEXTURE, FALSE); - mDeferredDepth.allocate(resX, resY, 0, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); - addDeferredAttachments(mDeferredScreen); - - mScreen.allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); - mEdgeMap.allocate(resX, resY, GL_ALPHA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); - - for (U32 i = 0; i < 3; i++) - { - mDeferredLight[i].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE); - } - - for (U32 i = 0; i < 2; i++) - { - mGIMapPost[i].allocate(resX,resY, GL_RGB, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE); - } - - F32 scale = gSavedSettings.getF32("RenderShadowResolutionScale"); - - //HACK: make alpha masking work on ATI depth shadows (work around for ATI driver bug) - U32 shadow_fmt = gGLManager.mIsATI ? GL_ALPHA : 0; - - for (U32 i = 0; i < 4; i++) - { - mShadow[i].allocate(U32(resX*scale),U32(resY*scale), shadow_fmt, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE); - } - - - U32 width = nhpo2(U32(resX*scale))/2; - U32 height = width; - - for (U32 i = 4; i < 6; i++) - { - mShadow[i].allocate(width, height, shadow_fmt, TRUE, FALSE); - } - - width = nhpo2(resX)/2; - height = nhpo2(resY)/2; - mLuminanceMap.allocate(width,height, GL_RGBA, FALSE, FALSE); - } - else - { - mScreen.allocate(resX, resY, GL_RGBA, TRUE, TRUE, LLTexUnit::TT_RECT_TEXTURE, FALSE); - } - - - if (LLRenderTarget::sUseFBO && gGLManager.mHasFramebufferMultisample && samples > 1) - { - mSampleBuffer.allocate(resX,resY,GL_RGBA,TRUE,TRUE,LLTexUnit::TT_RECT_TEXTURE,FALSE,samples); - if (LLPipeline::sRenderDeferred) - { - addDeferredAttachments(mSampleBuffer); - mDeferredScreen.setSampleBuffer(&mSampleBuffer); - } - - mScreen.setSampleBuffer(&mSampleBuffer); - - stop_glerror(); - } - - if (LLPipeline::sRenderDeferred) - { //share depth buffer between deferred targets - mDeferredScreen.shareDepthBuffer(mScreen); - for (U32 i = 0; i < 3; i++) - { //share stencil buffer with screen space lightmap to stencil out sky - mDeferredScreen.shareDepthBuffer(mDeferredLight[i]); - } - } - - gGL.getTexUnit(0)->disable(); - - stop_glerror(); - -} - -//static -void LLPipeline::updateRenderDeferred() -{ - BOOL deferred = ((gSavedSettings.getBOOL("RenderDeferred") && - LLRenderTarget::sUseFBO && - LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") && - gSavedSettings.getBOOL("VertexShaderEnable") && - gSavedSettings.getBOOL("RenderAvatarVP") && - gSavedSettings.getBOOL("WindLightUseAtmosShaders")) ? TRUE : FALSE) && - !gUseWireframe; - - sRenderDeferred = deferred; -} - -void LLPipeline::releaseGLBuffers() -{ - assertInitialized(); - - if (mNoiseMap) - { - LLImageGL::deleteTextures(1, &mNoiseMap); - mNoiseMap = 0; - } - - if (mTrueNoiseMap) - { - LLImageGL::deleteTextures(1, &mTrueNoiseMap); - mTrueNoiseMap = 0; - } - - if (mLightFunc) - { - LLImageGL::deleteTextures(1, &mLightFunc); - mLightFunc = 0; - } - - mWaterRef.release(); - mWaterDis.release(); - mScreen.release(); - mUIScreen.release(); - mSampleBuffer.releaseSampleBuffer(); - mDeferredScreen.release(); - mDeferredDepth.release(); - for (U32 i = 0; i < 3; i++) - { - mDeferredLight[i].release(); - } - - mEdgeMap.release(); - mGIMap.release(); - mGIMapPost[0].release(); - mGIMapPost[1].release(); - mHighlight.release(); - mLuminanceMap.release(); - - for (U32 i = 0; i < 6; i++) - { - mShadow[i].release(); - } - - for (U32 i = 0; i < 3; i++) - { - mGlow[i].release(); - } - - LLVOAvatar::resetImpostors(); -} - -void LLPipeline::createGLBuffers() -{ - LLMemType mt_cb(LLMemType::MTYPE_PIPELINE_CREATE_BUFFERS); - assertInitialized(); - - updateRenderDeferred(); - - if (LLPipeline::sWaterReflections) - { //water reflection texture - U32 res = (U32) gSavedSettings.getS32("RenderWaterRefResolution"); - - mWaterRef.allocate(res,res,GL_RGBA,TRUE,FALSE); - mWaterDis.allocate(res,res,GL_RGBA,TRUE,FALSE); - } - - mHighlight.allocate(256,256,GL_RGBA, FALSE, FALSE); - - stop_glerror(); - - GLuint resX = gViewerWindow->getWorldViewWidthRaw(); - GLuint resY = gViewerWindow->getWorldViewHeightRaw(); - - if (LLPipeline::sRenderGlow) - { //screen space glow buffers - const U32 glow_res = llmax(1, - llmin(512, 1 << gSavedSettings.getS32("RenderGlowResolutionPow"))); - - for (U32 i = 0; i < 3; i++) - { - mGlow[i].allocate(512,glow_res,GL_RGBA,FALSE,FALSE); - } - - allocateScreenBuffer(resX,resY); - mScreenWidth = 0; - mScreenHeight = 0; - - } - - if (sRenderDeferred) - { - if (!mNoiseMap) - { - const U32 noiseRes = 128; - LLVector3 noise[noiseRes*noiseRes]; - - F32 scaler = gSavedSettings.getF32("RenderDeferredNoise")/100.f; - for (U32 i = 0; i < noiseRes*noiseRes; ++i) - { - noise[i] = LLVector3(ll_frand()-0.5f, ll_frand()-0.5f, 0.f); - noise[i].normVec(); - noise[i].mV[2] = ll_frand()*scaler+1.f-scaler/2.f; - } - - LLImageGL::generateTextures(1, &mNoiseMap); - - gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mNoiseMap); - LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_RGB16F_ARB, noiseRes, noiseRes, GL_RGB, GL_FLOAT, noise); - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - } - - if (!mTrueNoiseMap) - { - const U32 noiseRes = 128; - F32 noise[noiseRes*noiseRes*3]; - for (U32 i = 0; i < noiseRes*noiseRes*3; i++) - { - noise[i] = ll_frand()*2.0-1.0; - } - - LLImageGL::generateTextures(1, &mTrueNoiseMap); - gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mTrueNoiseMap); - LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_RGB16F_ARB, noiseRes, noiseRes, GL_RGB,GL_FLOAT, noise); - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - } - - if (!mLightFunc) - { - U32 lightResX = gSavedSettings.getU32("RenderSpecularResX"); - U32 lightResY = gSavedSettings.getU32("RenderSpecularResY"); - U8* lg = new U8[lightResX*lightResY]; - - for (U32 y = 0; y < lightResY; ++y) - { - for (U32 x = 0; x < lightResX; ++x) - { - //spec func - F32 sa = (F32) x/(lightResX-1); - F32 spec = (F32) y/(lightResY-1); - //lg[y*lightResX+x] = (U8) (powf(sa, 128.f*spec*spec)*255); - - //F32 sp = acosf(sa)/(1.f-spec); - - sa = powf(sa, gSavedSettings.getF32("RenderSpecularExponent")); - F32 a = acosf(sa*0.25f+0.75f); - F32 m = llmax(0.5f-spec*0.5f, 0.001f); - F32 t2 = tanf(a)/m; - t2 *= t2; - - F32 c4a = (3.f+4.f*cosf(2.f*a)+cosf(4.f*a))/8.f; - F32 bd = 1.f/(4.f*m*m*c4a)*powf(F_E, -t2); - - lg[y*lightResX+x] = (U8) (llclamp(bd, 0.f, 1.f)*255); - } - } - - LLImageGL::generateTextures(1, &mLightFunc); - gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mLightFunc); - LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_ALPHA, lightResX, lightResY, GL_ALPHA, GL_UNSIGNED_BYTE, lg); - gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_TRILINEAR); - - delete [] lg; - } - - if (gSavedSettings.getBOOL("RenderDeferredGI")) - { - mGIMap.allocate(512,512,GL_RGBA, TRUE, FALSE); - addDeferredAttachments(mGIMap); - } - } -} - -void LLPipeline::restoreGL() -{ - LLMemType mt_cb(LLMemType::MTYPE_PIPELINE_RESTORE_GL); - assertInitialized(); - - if (mVertexShadersEnabled) - { - LLViewerShaderMgr::instance()->setShaders(); - } - - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* region = *iter; - for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) - { - LLSpatialPartition* part = region->getSpatialPartition(i); - if (part) - { - part->restoreGL(); - } - } - } -} - - -BOOL LLPipeline::canUseVertexShaders() -{ - if (sDisableShaders || - !gGLManager.mHasVertexShader || - !gGLManager.mHasFragmentShader || - !LLFeatureManager::getInstance()->isFeatureAvailable("VertexShaderEnable") || - (assertInitialized() && mVertexShadersLoaded != 1) ) - { - return FALSE; - } - else - { - return TRUE; - } -} - -BOOL LLPipeline::canUseWindLightShaders() const -{ - return (!LLPipeline::sDisableShaders && - gWLSkyProgram.mProgramObject != 0 && - LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_WINDLIGHT) > 1); -} - -BOOL LLPipeline::canUseWindLightShadersOnObjects() const -{ - return (canUseWindLightShaders() - && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_OBJECT) > 0); -} - -BOOL LLPipeline::canUseAntiAliasing() const -{ - return TRUE; //(gSavedSettings.getBOOL("RenderUseFBO")); -} - -void LLPipeline::unloadShaders() -{ - LLMemType mt_us(LLMemType::MTYPE_PIPELINE_UNLOAD_SHADERS); - LLViewerShaderMgr::instance()->unloadShaders(); - - mVertexShadersLoaded = 0; -} - -void LLPipeline::assertInitializedDoError() -{ - llerrs << "LLPipeline used when uninitialized." << llendl; -} - -//============================================================================ - -void LLPipeline::enableShadows(const BOOL enable_shadows) -{ - //should probably do something here to wrangle shadows.... -} - -S32 LLPipeline::getMaxLightingDetail() const -{ - /*if (mVertexShaderLevel[SHADER_OBJECT] >= LLDrawPoolSimple::SHADER_LEVEL_LOCAL_LIGHTS) - { - return 3; - } - else*/ - { - return 1; - } -} - -S32 LLPipeline::setLightingDetail(S32 level) -{ - LLMemType mt_ld(LLMemType::MTYPE_PIPELINE_LIGHTING_DETAIL); - assertInitialized(); - - if (level < 0) - { - if (gSavedSettings.getBOOL("VertexShaderEnable")) - { - level = 1; - } - else - { - level = 0; - } - } - level = llclamp(level, 0, getMaxLightingDetail()); - if (level != mLightingDetail) - { - mLightingDetail = level; - - if (mVertexShadersLoaded == 1) - { - LLViewerShaderMgr::instance()->setShaders(); - } - } - return mLightingDetail; -} - -class LLOctreeDirtyTexture : public LLOctreeTraveler -{ -public: - const std::set& mTextures; - - LLOctreeDirtyTexture(const std::set& textures) : mTextures(textures) { } - - virtual void visit(const LLOctreeNode* node) - { - LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0); - - if (!group->isState(LLSpatialGroup::GEOM_DIRTY) && !group->getData().empty()) - { - for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i) - { - for (LLSpatialGroup::drawmap_elem_t::iterator j = i->second.begin(); j != i->second.end(); ++j) - { - LLDrawInfo* params = *j; - LLViewerFetchedTexture* tex = LLViewerTextureManager::staticCastToFetchedTexture(params->mTexture); - if (tex && mTextures.find(tex) != mTextures.end()) - { - group->setState(LLSpatialGroup::GEOM_DIRTY); - } - } - } - } - - for (LLSpatialGroup::bridge_list_t::iterator i = group->mBridgeList.begin(); i != group->mBridgeList.end(); ++i) - { - LLSpatialBridge* bridge = *i; - traverse(bridge->mOctree); - } - } -}; - -// Called when a texture changes # of channels (causes faces to move to alpha pool) -void LLPipeline::dirtyPoolObjectTextures(const std::set& textures) -{ - assertInitialized(); - - // *TODO: This is inefficient and causes frame spikes; need a better way to do this - // Most of the time is spent in dirty.traverse. - - for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) - { - LLDrawPool *poolp = *iter; - if (poolp->isFacePool()) - { - ((LLFacePool*) poolp)->dirtyTextures(textures); - } - } - - LLOctreeDirtyTexture dirty(textures); - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* region = *iter; - for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) - { - LLSpatialPartition* part = region->getSpatialPartition(i); - if (part) - { - dirty.traverse(part->mOctree); - } - } - } -} - -LLDrawPool *LLPipeline::findPool(const U32 type, LLViewerTexture *tex0) -{ - assertInitialized(); - - LLDrawPool *poolp = NULL; - switch( type ) - { - case LLDrawPool::POOL_SIMPLE: - poolp = mSimplePool; - break; - - case LLDrawPool::POOL_GRASS: - poolp = mGrassPool; - break; - - case LLDrawPool::POOL_FULLBRIGHT: - poolp = mFullbrightPool; - break; - - case LLDrawPool::POOL_INVISIBLE: - poolp = mInvisiblePool; - break; - - case LLDrawPool::POOL_GLOW: - poolp = mGlowPool; - break; - - case LLDrawPool::POOL_TREE: - poolp = get_if_there(mTreePools, (uintptr_t)tex0, (LLDrawPool*)0 ); - break; - - case LLDrawPool::POOL_TERRAIN: - poolp = get_if_there(mTerrainPools, (uintptr_t)tex0, (LLDrawPool*)0 ); - break; - - case LLDrawPool::POOL_BUMP: - poolp = mBumpPool; - break; - - case LLDrawPool::POOL_ALPHA: - poolp = mAlphaPool; - break; - - case LLDrawPool::POOL_AVATAR: - break; // Do nothing - - case LLDrawPool::POOL_SKY: - poolp = mSkyPool; - break; - - case LLDrawPool::POOL_WATER: - poolp = mWaterPool; - break; - - case LLDrawPool::POOL_GROUND: - poolp = mGroundPool; - break; - - case LLDrawPool::POOL_WL_SKY: - poolp = mWLSkyPool; - break; - - default: - llassert(0); - llerrs << "Invalid Pool Type in LLPipeline::findPool() type=" << type << llendl; - break; - } - - return poolp; -} - - -LLDrawPool *LLPipeline::getPool(const U32 type, LLViewerTexture *tex0) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE); - LLDrawPool *poolp = findPool(type, tex0); - if (poolp) - { - return poolp; - } - - LLDrawPool *new_poolp = LLDrawPool::createPool(type, tex0); - addPool( new_poolp ); - - return new_poolp; -} - - -// static -LLDrawPool* LLPipeline::getPoolFromTE(const LLTextureEntry* te, LLViewerTexture* imagep) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE); - U32 type = getPoolTypeFromTE(te, imagep); - return gPipeline.getPool(type, imagep); -} - -//static -U32 LLPipeline::getPoolTypeFromTE(const LLTextureEntry* te, LLViewerTexture* imagep) -{ - LLMemType mt_gpt(LLMemType::MTYPE_PIPELINE_GET_POOL_TYPE); - - if (!te || !imagep) - { - return 0; - } - - bool alpha = te->getColor().mV[3] < 0.999f; - if (imagep) - { - alpha = alpha || (imagep->getComponents() == 4 && imagep->getType() != LLViewerTexture::MEDIA_TEXTURE) || (imagep->getComponents() == 2); - } - - if (alpha) - { - return LLDrawPool::POOL_ALPHA; - } - else if ((te->getBumpmap() || te->getShiny())) - { - return LLDrawPool::POOL_BUMP; - } - else - { - return LLDrawPool::POOL_SIMPLE; - } -} - - -void LLPipeline::addPool(LLDrawPool *new_poolp) -{ - LLMemType mt_a(LLMemType::MTYPE_PIPELINE_ADD_POOL); - assertInitialized(); - mPools.insert(new_poolp); - addToQuickLookup( new_poolp ); -} - -void LLPipeline::allocDrawable(LLViewerObject *vobj) -{ - LLMemType mt_ad(LLMemType::MTYPE_PIPELINE_ALLOCATE_DRAWABLE); - LLDrawable *drawable = new LLDrawable(); - vobj->mDrawable = drawable; - - drawable->mVObjp = vobj; - - //encompass completely sheared objects by taking - //the most extreme point possible (<1,1,0.5>) - drawable->setRadius(LLVector3(1,1,0.5f).scaleVec(vobj->getScale()).length()); - if (vobj->isOrphaned()) - { - drawable->setState(LLDrawable::FORCE_INVISIBLE); - } - drawable->updateXform(TRUE); -} - - -void LLPipeline::unlinkDrawable(LLDrawable *drawable) -{ - LLFastTimer t(FTM_PIPELINE); - - assertInitialized(); - - LLPointer drawablep = drawable; // make sure this doesn't get deleted before we are done - - // Based on flags, remove the drawable from the queues that it's on. - if (drawablep->isState(LLDrawable::ON_MOVE_LIST)) - { - LLDrawable::drawable_vector_t::iterator iter = std::find(mMovedList.begin(), mMovedList.end(), drawablep); - if (iter != mMovedList.end()) - { - mMovedList.erase(iter); - } - } - - if (drawablep->getSpatialGroup()) - { - if (!drawablep->getSpatialGroup()->mSpatialPartition->remove(drawablep, drawablep->getSpatialGroup())) - { -#ifdef LL_RELEASE_FOR_DOWNLOAD - llwarns << "Couldn't remove object from spatial group!" << llendl; -#else - llerrs << "Couldn't remove object from spatial group!" << llendl; -#endif - } - } - - mLights.erase(drawablep); - for (light_set_t::iterator iter = mNearbyLights.begin(); - iter != mNearbyLights.end(); iter++) - { - if (iter->drawable == drawablep) - { - mNearbyLights.erase(iter); - break; - } - } - - { - HighlightItem item(drawablep); - mHighlightSet.erase(item); - - if (mHighlightObject == drawablep) - { - mHighlightObject = NULL; - } - } - - for (U32 i = 0; i < 2; ++i) - { - if (mShadowSpotLight[i] == drawablep) - { - mShadowSpotLight[i] = NULL; - } - - if (mTargetShadowSpotLight[i] == drawablep) - { - mTargetShadowSpotLight[i] = NULL; - } - } - - -} - -U32 LLPipeline::addObject(LLViewerObject *vobj) -{ - LLMemType mt_ao(LLMemType::MTYPE_PIPELINE_ADD_OBJECT); - if (gNoRender) - { - return 0; - } - - if (gSavedSettings.getBOOL("RenderDelayCreation")) - { - mCreateQ.push_back(vobj); - } - else - { - createObject(vobj); - } - - return 1; -} - -void LLPipeline::createObjects(F32 max_dtime) -{ - LLFastTimer ftm(FTM_GEO_UPDATE); - LLMemType mt(LLMemType::MTYPE_PIPELINE_CREATE_OBJECTS); - - LLTimer update_timer; - - while (!mCreateQ.empty() && update_timer.getElapsedTimeF32() < max_dtime) - { - LLViewerObject* vobj = mCreateQ.front(); - if (!vobj->isDead()) - { - createObject(vobj); - } - mCreateQ.pop_front(); - } - - //for (LLViewerObject::vobj_list_t::iterator iter = mCreateQ.begin(); iter != mCreateQ.end(); ++iter) - //{ - // createObject(*iter); - //} - - //mCreateQ.clear(); -} - -void LLPipeline::createObject(LLViewerObject* vobj) -{ - LLDrawable* drawablep = vobj->mDrawable; - - if (!drawablep) - { - drawablep = vobj->createDrawable(this); - } - else - { - llerrs << "Redundant drawable creation!" << llendl; - } - - llassert(drawablep); - - if (vobj->getParent()) - { - vobj->setDrawableParent(((LLViewerObject*)vobj->getParent())->mDrawable); // LLPipeline::addObject 1 - } - else - { - vobj->setDrawableParent(NULL); // LLPipeline::addObject 2 - } - - markRebuild(drawablep, LLDrawable::REBUILD_ALL, TRUE); - - if (drawablep->getVOVolume() && gSavedSettings.getBOOL("RenderAnimateRes")) - { - // fun animated res - drawablep->updateXform(TRUE); - drawablep->clearState(LLDrawable::MOVE_UNDAMPED); - drawablep->setScale(LLVector3(0,0,0)); - drawablep->makeActive(); - } -} - - -void LLPipeline::resetFrameStats() -{ - assertInitialized(); - - LLViewerStats::getInstance()->mTrianglesDrawnStat.addValue(mTrianglesDrawn/1000.f); - - if (mBatchCount > 0) - { - mMeanBatchSize = gPipeline.mTrianglesDrawn/gPipeline.mBatchCount; - } - mTrianglesDrawn = 0; - sCompiles = 0; - mVerticesRelit = 0; - mLightingChanges = 0; - mGeometryChanges = 0; - mNumVisibleFaces = 0; - - if (mOldRenderDebugMask != mRenderDebugMask) - { - gObjectList.clearDebugText(); - mOldRenderDebugMask = mRenderDebugMask; - } - -} - -//external functions for asynchronous updating -void LLPipeline::updateMoveDampedAsync(LLDrawable* drawablep) -{ - if (gSavedSettings.getBOOL("FreezeTime")) - { - return; - } - if (!drawablep) - { - llerrs << "updateMove called with NULL drawablep" << llendl; - return; - } - if (drawablep->isState(LLDrawable::EARLY_MOVE)) - { - return; - } - - assertInitialized(); - - // update drawable now - drawablep->clearState(LLDrawable::MOVE_UNDAMPED); // force to DAMPED - drawablep->updateMove(); // returns done - drawablep->setState(LLDrawable::EARLY_MOVE); // flag says we already did an undamped move this frame - // Put on move list so that EARLY_MOVE gets cleared - if (!drawablep->isState(LLDrawable::ON_MOVE_LIST)) - { - mMovedList.push_back(drawablep); - drawablep->setState(LLDrawable::ON_MOVE_LIST); - } -} - -void LLPipeline::updateMoveNormalAsync(LLDrawable* drawablep) -{ - if (gSavedSettings.getBOOL("FreezeTime")) - { - return; - } - if (!drawablep) - { - llerrs << "updateMove called with NULL drawablep" << llendl; - return; - } - if (drawablep->isState(LLDrawable::EARLY_MOVE)) - { - return; - } - - assertInitialized(); - - // update drawable now - drawablep->setState(LLDrawable::MOVE_UNDAMPED); // force to UNDAMPED - drawablep->updateMove(); - drawablep->setState(LLDrawable::EARLY_MOVE); // flag says we already did an undamped move this frame - // Put on move list so that EARLY_MOVE gets cleared - if (!drawablep->isState(LLDrawable::ON_MOVE_LIST)) - { - mMovedList.push_back(drawablep); - drawablep->setState(LLDrawable::ON_MOVE_LIST); - } -} - -void LLPipeline::updateMovedList(LLDrawable::drawable_vector_t& moved_list) -{ - for (LLDrawable::drawable_vector_t::iterator iter = moved_list.begin(); - iter != moved_list.end(); ) - { - LLDrawable::drawable_vector_t::iterator curiter = iter++; - LLDrawable *drawablep = *curiter; - BOOL done = TRUE; - if (!drawablep->isDead() && (!drawablep->isState(LLDrawable::EARLY_MOVE))) - { - done = drawablep->updateMove(); - } - drawablep->clearState(LLDrawable::EARLY_MOVE | LLDrawable::MOVE_UNDAMPED); - if (done) - { - drawablep->clearState(LLDrawable::ON_MOVE_LIST); - iter = moved_list.erase(curiter); - } - } -} - -static LLFastTimer::DeclareTimer FTM_OCTREE_BALANCE("Balance Octree"); -static LLFastTimer::DeclareTimer FTM_UPDATE_MOVE("Update Move"); - -void LLPipeline::updateMove() -{ - LLFastTimer t(FTM_UPDATE_MOVE); - LLMemType mt_um(LLMemType::MTYPE_PIPELINE_UPDATE_MOVE); - - if (gSavedSettings.getBOOL("FreezeTime")) - { - return; - } - - assertInitialized(); - - { - static LLFastTimer::DeclareTimer ftm("Retexture"); - LLFastTimer t(ftm); - - for (LLDrawable::drawable_set_t::iterator iter = mRetexturedList.begin(); - iter != mRetexturedList.end(); ++iter) - { - LLDrawable* drawablep = *iter; - if (drawablep && !drawablep->isDead()) - { - drawablep->updateTexture(); - } - } - mRetexturedList.clear(); - } - - { - static LLFastTimer::DeclareTimer ftm("Moved List"); - LLFastTimer t(ftm); - updateMovedList(mMovedList); - } - - //balance octrees - { - LLFastTimer ot(FTM_OCTREE_BALANCE); - - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* region = *iter; - for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) - { - LLSpatialPartition* part = region->getSpatialPartition(i); - if (part) - { - part->mOctree->balance(); - } - } - } - } -} - -///////////////////////////////////////////////////////////////////////////// -// Culling and occlusion testing -///////////////////////////////////////////////////////////////////////////// - -//static -F32 LLPipeline::calcPixelArea(LLVector3 center, LLVector3 size, LLCamera &camera) -{ - LLVector3 lookAt = center - camera.getOrigin(); - F32 dist = lookAt.length(); - - //ramp down distance for nearby objects - //shrink dist by dist/16. - if (dist < 16.f) - { - dist /= 16.f; - dist *= dist; - dist *= 16.f; - } - - //get area of circle around node - F32 app_angle = atanf(size.length()/dist); - F32 radius = app_angle*LLDrawable::sCurPixelAngle; - return radius*radius * F_PI; -} - -void LLPipeline::grabReferences(LLCullResult& result) -{ - sCull = &result; -} - -BOOL LLPipeline::visibleObjectsInFrustum(LLCamera& camera) -{ - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* region = *iter; - - for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) - { - LLSpatialPartition* part = region->getSpatialPartition(i); - if (part) - { - if (hasRenderType(part->mDrawableType)) - { - if (part->visibleObjectsInFrustum(camera)) - { - return TRUE; - } - } - } - } - } - - return FALSE; -} - -BOOL LLPipeline::getVisibleExtents(LLCamera& camera, LLVector3& min, LLVector3& max) -{ - const F32 X = 65536.f; - - min = LLVector3(X,X,X); - max = LLVector3(-X,-X,-X); - - U32 saved_camera_id = LLViewerCamera::sCurCameraID; - LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD; - - BOOL res = TRUE; - - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* region = *iter; - - for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) - { - LLSpatialPartition* part = region->getSpatialPartition(i); - if (part) - { - if (hasRenderType(part->mDrawableType)) - { - if (!part->getVisibleExtents(camera, min, max)) - { - res = FALSE; - } - } - } - } - } - - LLViewerCamera::sCurCameraID = saved_camera_id; - - return res; -} - -static LLFastTimer::DeclareTimer FTM_CULL("Object Culling"); - -void LLPipeline::updateCull(LLCamera& camera, LLCullResult& result, S32 water_clip) -{ - LLFastTimer t(FTM_CULL); - LLMemType mt_uc(LLMemType::MTYPE_PIPELINE_UPDATE_CULL); - - grabReferences(result); - - sCull->clear(); - - BOOL to_texture = LLPipeline::sUseOcclusion > 1 && - !hasRenderType(LLPipeline::RENDER_TYPE_HUD) && - LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD && - gPipeline.canUseVertexShaders() && - sRenderGlow; - - if (to_texture) - { - mScreen.bindTarget(); - } - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadMatrixd(gGLLastProjection); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - gGLLastMatrix = NULL; - glLoadMatrixd(gGLLastModelView); - - - LLVertexBuffer::unbind(); - LLGLDisable blend(GL_BLEND); - LLGLDisable test(GL_ALPHA_TEST); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - if (sUseOcclusion > 1) - { - gGL.setColorMask(false, false); - } - - LLGLDepthTest depth(GL_TRUE, GL_FALSE); - - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* region = *iter; - if (water_clip != 0) - { - LLPlane plane(LLVector3(0,0, (F32) -water_clip), (F32) water_clip*region->getWaterHeight()); - camera.setUserClipPlane(plane); - } - else - { - camera.disableUserClipPlane(); - } - - for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) - { - LLSpatialPartition* part = region->getSpatialPartition(i); - if (part) - { - if (hasRenderType(part->mDrawableType)) - { - part->cull(camera); - } - } - } - } - - camera.disableUserClipPlane(); - - if (hasRenderType(LLPipeline::RENDER_TYPE_SKY) && - gSky.mVOSkyp.notNull() && - gSky.mVOSkyp->mDrawable.notNull()) - { - gSky.mVOSkyp->mDrawable->setVisible(camera); - sCull->pushDrawable(gSky.mVOSkyp->mDrawable); - gSky.updateCull(); - stop_glerror(); - } - - if (hasRenderType(LLPipeline::RENDER_TYPE_GROUND) && - !gPipeline.canUseWindLightShaders() && - gSky.mVOGroundp.notNull() && - gSky.mVOGroundp->mDrawable.notNull() && - !LLPipeline::sWaterReflections) - { - gSky.mVOGroundp->mDrawable->setVisible(camera); - sCull->pushDrawable(gSky.mVOGroundp->mDrawable); - } - - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - if (sUseOcclusion > 1) - { - gGL.setColorMask(true, false); - } - - if (to_texture) - { - mScreen.flush(); - } -} - -void LLPipeline::markNotCulled(LLSpatialGroup* group, LLCamera& camera) -{ - if (group->getData().empty()) - { - return; - } - - group->setVisible(); - - if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD) - { - group->updateDistance(camera); - } - - const F32 MINIMUM_PIXEL_AREA = 16.f; - - if (group->mPixelArea < MINIMUM_PIXEL_AREA) - { - return; - } - - if (sMinRenderSize > 0.f && - llmax(llmax(group->mBounds[1].mV[0], group->mBounds[1].mV[1]), group->mBounds[1].mV[2]) < sMinRenderSize) - { - return; - } - - assertInitialized(); - - if (!group->mSpatialPartition->mRenderByGroup) - { //render by drawable - sCull->pushDrawableGroup(group); - } - else - { //render by group - sCull->pushVisibleGroup(group); - } - - mNumVisibleNodes++; -} - -void LLPipeline::markOccluder(LLSpatialGroup* group) -{ - if (sUseOcclusion > 1 && group && !group->isOcclusionState(LLSpatialGroup::ACTIVE_OCCLUSION)) - { - LLSpatialGroup* parent = group->getParent(); - - if (!parent || !parent->isOcclusionState(LLSpatialGroup::OCCLUDED)) - { //only mark top most occluders as active occlusion - sCull->pushOcclusionGroup(group); - group->setOcclusionState(LLSpatialGroup::ACTIVE_OCCLUSION); - - if (parent && - !parent->isOcclusionState(LLSpatialGroup::ACTIVE_OCCLUSION) && - parent->getElementCount() == 0 && - parent->needsUpdate()) - { - sCull->pushOcclusionGroup(group); - parent->setOcclusionState(LLSpatialGroup::ACTIVE_OCCLUSION); - } - } - } -} - -void LLPipeline::doOcclusion(LLCamera& camera) -{ - LLVertexBuffer::unbind(); - - if (hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION)) - { - gGL.setColorMask(true, false, false, false); - } - else - { - gGL.setColorMask(false, false); - } - LLGLDisable blend(GL_BLEND); - LLGLDisable test(GL_ALPHA_TEST); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLGLDepthTest depth(GL_TRUE, GL_FALSE); - - LLGLDisable cull(GL_CULL_FACE); - if (LLPipeline::sUseOcclusion > 1) - { - for (LLCullResult::sg_list_t::iterator iter = sCull->beginOcclusionGroups(); iter != sCull->endOcclusionGroups(); ++iter) - { - LLSpatialGroup* group = *iter; - group->doOcclusion(&camera); - group->clearOcclusionState(LLSpatialGroup::ACTIVE_OCCLUSION); - } - } - - gGL.setColorMask(true, false); -} - -BOOL LLPipeline::updateDrawableGeom(LLDrawable* drawablep, BOOL priority) -{ - BOOL update_complete = drawablep->updateGeometry(priority); - if (update_complete && assertInitialized()) - { - drawablep->setState(LLDrawable::BUILT); - mGeometryChanges++; - } - return update_complete; -} - -void LLPipeline::updateGL() -{ - while (!LLGLUpdate::sGLQ.empty()) - { - LLGLUpdate* glu = LLGLUpdate::sGLQ.front(); - glu->updateGL(); - glu->mInQ = FALSE; - LLGLUpdate::sGLQ.pop_front(); - } -} - -void LLPipeline::rebuildPriorityGroups() -{ - if(!sAllowRebuildPriorityGroup) - { - return ; - } - sAllowRebuildPriorityGroup = FALSE ; - - LLTimer update_timer; - LLMemType mt(LLMemType::MTYPE_PIPELINE); - - assertInitialized(); - - // Iterate through all drawables on the priority build queue, - for (LLSpatialGroup::sg_vector_t::iterator iter = mGroupQ1.begin(); - iter != mGroupQ1.end(); ++iter) - { - LLSpatialGroup* group = *iter; - group->rebuildGeom(); - group->clearState(LLSpatialGroup::IN_BUILD_Q1); - } - - mGroupQ1.clear(); -} - -void LLPipeline::rebuildGroups() -{ - // Iterate through some drawables on the non-priority build queue - S32 size = (S32) mGroupQ2.size(); - S32 min_count = llclamp((S32) ((F32) (size * size)/4096*0.25f), 1, size); - - S32 count = 0; - - std::sort(mGroupQ2.begin(), mGroupQ2.end(), LLSpatialGroup::CompareUpdateUrgency()); - - LLSpatialGroup::sg_vector_t::iterator iter; - for (iter = mGroupQ2.begin(); - iter != mGroupQ2.end(); ++iter) - { - LLSpatialGroup* group = *iter; - - if (group->isDead()) - { - continue; - } - - group->rebuildGeom(); - - if (group->mSpatialPartition->mRenderByGroup) - { - count++; - } - - group->clearState(LLSpatialGroup::IN_BUILD_Q2); - - if (count > min_count) - { - ++iter; - break; - } - } - - mGroupQ2.erase(mGroupQ2.begin(), iter); - - updateMovedList(mMovedBridge); -} - -void LLPipeline::updateGeom(F32 max_dtime) -{ - LLTimer update_timer; - LLMemType mt(LLMemType::MTYPE_PIPELINE_UPDATE_GEOM); - LLPointer drawablep; - - LLFastTimer t(FTM_GEO_UPDATE); - - assertInitialized(); - - if (sDelayedVBOEnable > 0) - { - if (--sDelayedVBOEnable <= 0) - { - resetVertexBuffers(); - LLVertexBuffer::sEnableVBOs = TRUE; - } - } - - // notify various object types to reset internal cost metrics, etc. - // for now, only LLVOVolume does this to throttle LOD changes - LLVOVolume::preUpdateGeom(); - - // Iterate through all drawables on the priority build queue, - for (LLDrawable::drawable_list_t::iterator iter = mBuildQ1.begin(); - iter != mBuildQ1.end();) - { - LLDrawable::drawable_list_t::iterator curiter = iter++; - LLDrawable* drawablep = *curiter; - if (drawablep && !drawablep->isDead()) - { - if (drawablep->isState(LLDrawable::IN_REBUILD_Q2)) - { - drawablep->clearState(LLDrawable::IN_REBUILD_Q2); - LLDrawable::drawable_list_t::iterator find = std::find(mBuildQ2.begin(), mBuildQ2.end(), drawablep); - if (find != mBuildQ2.end()) - { - mBuildQ2.erase(find); - } - } - - if (updateDrawableGeom(drawablep, TRUE)) - { - drawablep->clearState(LLDrawable::IN_REBUILD_Q1); - mBuildQ1.erase(curiter); - } - } - else - { - mBuildQ1.erase(curiter); - } - } - - // Iterate through some drawables on the non-priority build queue - S32 min_count = 16; - S32 size = (S32) mBuildQ2.size(); - if (size > 1024) - { - min_count = llclamp((S32) (size * (F32) size/4096), 16, size); - } - - S32 count = 0; - - max_dtime = llmax(update_timer.getElapsedTimeF32()+0.001f, max_dtime); - LLSpatialGroup* last_group = NULL; - LLSpatialBridge* last_bridge = NULL; - - for (LLDrawable::drawable_list_t::iterator iter = mBuildQ2.begin(); - iter != mBuildQ2.end(); ) - { - LLDrawable::drawable_list_t::iterator curiter = iter++; - LLDrawable* drawablep = *curiter; - - LLSpatialBridge* bridge = drawablep->isRoot() ? drawablep->getSpatialBridge() : - drawablep->getParent()->getSpatialBridge(); - - if (drawablep->getSpatialGroup() != last_group && - (!last_bridge || bridge != last_bridge) && - (update_timer.getElapsedTimeF32() >= max_dtime) && count > min_count) - { - break; - } - - //make sure updates don't stop in the middle of a spatial group - //to avoid thrashing (objects are enqueued by group) - last_group = drawablep->getSpatialGroup(); - last_bridge = bridge; - - BOOL update_complete = TRUE; - if (!drawablep->isDead()) - { - update_complete = updateDrawableGeom(drawablep, FALSE); - count++; - } - if (update_complete) - { - drawablep->clearState(LLDrawable::IN_REBUILD_Q2); - mBuildQ2.erase(curiter); - } - } - - updateMovedList(mMovedBridge); -} - -void LLPipeline::markVisible(LLDrawable *drawablep, LLCamera& camera) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_MARK_VISIBLE); - - if(drawablep && !drawablep->isDead()) - { - if (drawablep->isSpatialBridge()) - { - const LLDrawable* root = ((LLSpatialBridge*) drawablep)->mDrawable; - llassert(root); // trying to catch a bad assumption - if (root && // // this test may not be needed, see above - root->getVObj()->isAttachment()) - { - LLDrawable* rootparent = root->getParent(); - if (rootparent) // this IS sometimes NULL - { - LLViewerObject *vobj = rootparent->getVObj(); - llassert(vobj); // trying to catch a bad assumption - if (vobj) // this test may not be needed, see above - { - const LLVOAvatar* av = vobj->asAvatar(); - if (av && av->isImpostor()) - { - return; - } - } - } - } - sCull->pushBridge((LLSpatialBridge*) drawablep); - } - else - { - sCull->pushDrawable(drawablep); - } - - drawablep->setVisible(camera); - } -} - -void LLPipeline::markMoved(LLDrawable *drawablep, BOOL damped_motion) -{ - LLMemType mt_mm(LLMemType::MTYPE_PIPELINE_MARK_MOVED); - - if (!drawablep) - { - //llerrs << "Sending null drawable to moved list!" << llendl; - return; - } - - if (drawablep->isDead()) - { - llwarns << "Marking NULL or dead drawable moved!" << llendl; - return; - } - - if (drawablep->getParent()) - { - //ensure that parent drawables are moved first - markMoved(drawablep->getParent(), damped_motion); - } - - assertInitialized(); - - if (!drawablep->isState(LLDrawable::ON_MOVE_LIST)) - { - if (drawablep->isSpatialBridge()) - { - mMovedBridge.push_back(drawablep); - } - else - { - mMovedList.push_back(drawablep); - } - drawablep->setState(LLDrawable::ON_MOVE_LIST); - } - if (damped_motion == FALSE) - { - drawablep->setState(LLDrawable::MOVE_UNDAMPED); // UNDAMPED trumps DAMPED - } - else if (drawablep->isState(LLDrawable::MOVE_UNDAMPED)) - { - drawablep->clearState(LLDrawable::MOVE_UNDAMPED); - } -} - -void LLPipeline::markShift(LLDrawable *drawablep) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_MARK_SHIFT); - - if (!drawablep || drawablep->isDead()) - { - return; - } - - assertInitialized(); - - if (!drawablep->isState(LLDrawable::ON_SHIFT_LIST)) - { - drawablep->getVObj()->setChanged(LLXform::SHIFTED | LLXform::SILHOUETTE); - if (drawablep->getParent()) - { - markShift(drawablep->getParent()); - } - mShiftList.push_back(drawablep); - drawablep->setState(LLDrawable::ON_SHIFT_LIST); - } -} - -void LLPipeline::shiftObjects(const LLVector3 &offset) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_SHIFT_OBJECTS); - - assertInitialized(); - - glClear(GL_DEPTH_BUFFER_BIT); - gDepthDirty = TRUE; - - for (LLDrawable::drawable_vector_t::iterator iter = mShiftList.begin(); - iter != mShiftList.end(); iter++) - { - LLDrawable *drawablep = *iter; - if (drawablep->isDead()) - { - continue; - } - drawablep->shiftPos(offset); - drawablep->clearState(LLDrawable::ON_SHIFT_LIST); - } - mShiftList.resize(0); - - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* region = *iter; - for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) - { - LLSpatialPartition* part = region->getSpatialPartition(i); - if (part) - { - part->shift(offset); - } - } - } - - LLHUDText::shiftAll(offset); - LLHUDNameTag::shiftAll(offset); - display_update_camera(); -} - -void LLPipeline::markTextured(LLDrawable *drawablep) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_MARK_TEXTURED); - - if (drawablep && !drawablep->isDead() && assertInitialized()) - { - mRetexturedList.insert(drawablep); - } -} - -void LLPipeline::markGLRebuild(LLGLUpdate* glu) -{ - if (glu && !glu->mInQ) - { - LLGLUpdate::sGLQ.push_back(glu); - glu->mInQ = TRUE; - } -} - -void LLPipeline::markRebuild(LLSpatialGroup* group, BOOL priority) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE); - //assert_main_thread(); - - if (group && !group->isDead() && group->mSpatialPartition) - { - if (group->mSpatialPartition->mPartitionType == LLViewerRegion::PARTITION_HUD) - { - priority = TRUE; - } - - if (priority) - { - if (!group->isState(LLSpatialGroup::IN_BUILD_Q1)) - { - mGroupQ1.push_back(group); - group->setState(LLSpatialGroup::IN_BUILD_Q1); - - if (group->isState(LLSpatialGroup::IN_BUILD_Q2)) - { - LLSpatialGroup::sg_vector_t::iterator iter = std::find(mGroupQ2.begin(), mGroupQ2.end(), group); - if (iter != mGroupQ2.end()) - { - mGroupQ2.erase(iter); - } - group->clearState(LLSpatialGroup::IN_BUILD_Q2); - } - } - } - else if (!group->isState(LLSpatialGroup::IN_BUILD_Q2 | LLSpatialGroup::IN_BUILD_Q1)) - { - //llerrs << "Non-priority updates not yet supported!" << llendl; - if (std::find(mGroupQ2.begin(), mGroupQ2.end(), group) != mGroupQ2.end()) - { - llerrs << "WTF?" << llendl; - } - mGroupQ2.push_back(group); - group->setState(LLSpatialGroup::IN_BUILD_Q2); - - } - } -} - -void LLPipeline::markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags flag, BOOL priority) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_MARK_REBUILD); - - if (drawablep && !drawablep->isDead() && assertInitialized()) - { - if (!drawablep->isState(LLDrawable::BUILT)) - { - priority = TRUE; - } - if (priority) - { - if (!drawablep->isState(LLDrawable::IN_REBUILD_Q1)) - { - mBuildQ1.push_back(drawablep); - drawablep->setState(LLDrawable::IN_REBUILD_Q1); // mark drawable as being in priority queue - } - } - else if (!drawablep->isState(LLDrawable::IN_REBUILD_Q2)) - { - mBuildQ2.push_back(drawablep); - drawablep->setState(LLDrawable::IN_REBUILD_Q2); // need flag here because it is just a list - } - if (flag & (LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION)) - { - drawablep->getVObj()->setChanged(LLXform::SILHOUETTE); - } - drawablep->setState(flag); - } -} - -static LLFastTimer::DeclareTimer FTM_RESET_DRAWORDER("Reset Draw Order"); - -void LLPipeline::stateSort(LLCamera& camera, LLCullResult &result) -{ - if (hasAnyRenderType(LLPipeline::RENDER_TYPE_AVATAR, - LLPipeline::RENDER_TYPE_GROUND, - LLPipeline::RENDER_TYPE_TERRAIN, - LLPipeline::RENDER_TYPE_TREE, - LLPipeline::RENDER_TYPE_SKY, - LLPipeline::RENDER_TYPE_VOIDWATER, - LLPipeline::RENDER_TYPE_WATER, - LLPipeline::END_RENDER_TYPES)) - { - //clear faces from face pools - LLFastTimer t(FTM_RESET_DRAWORDER); - gPipeline.resetDrawOrders(); - } - - LLFastTimer ftm(FTM_STATESORT); - LLMemType mt(LLMemType::MTYPE_PIPELINE_STATE_SORT); - - //LLVertexBuffer::unbind(); - - grabReferences(result); - for (LLCullResult::sg_list_t::iterator iter = sCull->beginDrawableGroups(); iter != sCull->endDrawableGroups(); ++iter) - { - LLSpatialGroup* group = *iter; - group->checkOcclusion(); - if (sUseOcclusion > 1 && group->isOcclusionState(LLSpatialGroup::OCCLUDED)) - { - markOccluder(group); - } - else - { - group->setVisible(); - for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i) - { - markVisible(*i, camera); - } - } - } - for (LLCullResult::sg_list_t::iterator iter = sCull->beginVisibleGroups(); iter != sCull->endVisibleGroups(); ++iter) - { - LLSpatialGroup* group = *iter; - group->checkOcclusion(); - if (sUseOcclusion > 1 && group->isOcclusionState(LLSpatialGroup::OCCLUDED)) - { - markOccluder(group); - } - else - { - group->setVisible(); - stateSort(group, camera); - } - } - - if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD) - { - for (LLCullResult::bridge_list_t::iterator i = sCull->beginVisibleBridge(); i != sCull->endVisibleBridge(); ++i) - { - LLCullResult::bridge_list_t::iterator cur_iter = i; - LLSpatialBridge* bridge = *cur_iter; - LLSpatialGroup* group = bridge->getSpatialGroup(); - if (!bridge->isDead() && group && !group->isOcclusionState(LLSpatialGroup::OCCLUDED)) - { - stateSort(bridge, camera); - } - } - } - { - LLFastTimer ftm(FTM_STATESORT_DRAWABLE); - for (LLCullResult::drawable_list_t::iterator iter = sCull->beginVisibleList(); - iter != sCull->endVisibleList(); ++iter) - { - LLDrawable *drawablep = *iter; - if (!drawablep->isDead()) - { - stateSort(drawablep, camera); - } - } - } - { - LLFastTimer ftm(FTM_CLIENT_COPY); - LLVertexBuffer::clientCopy(); - } - - postSort(camera); -} - -void LLPipeline::stateSort(LLSpatialGroup* group, LLCamera& camera) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_STATE_SORT); - if (group->changeLOD()) - { - for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i) - { - LLDrawable* drawablep = *i; - stateSort(drawablep, camera); - } - } - -} - -void LLPipeline::stateSort(LLSpatialBridge* bridge, LLCamera& camera) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_STATE_SORT); - if (!sShadowRender && bridge->getSpatialGroup()->changeLOD()) - { - bool force_update = false; - bridge->updateDistance(camera, force_update); - } -} - -void LLPipeline::stateSort(LLDrawable* drawablep, LLCamera& camera) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_STATE_SORT); - - if (!drawablep - || drawablep->isDead() - || !hasRenderType(drawablep->getRenderType())) - { - return; - } - - if (LLSelectMgr::getInstance()->mHideSelectedObjects) - { - if (drawablep->getVObj().notNull() && - drawablep->getVObj()->isSelected()) - { - return; - } - } - - if (drawablep->isAvatar()) - { //don't draw avatars beyond render distance or if we don't have a spatial group. - if ((drawablep->getSpatialGroup() == NULL) || - (drawablep->getSpatialGroup()->mDistance > LLVOAvatar::sRenderDistance)) - { - return; - } - - LLVOAvatar* avatarp = (LLVOAvatar*) drawablep->getVObj().get(); - if (!avatarp->isVisible()) - { - return; - } - } - - assertInitialized(); - - if (hasRenderType(drawablep->mRenderType)) - { - if (!drawablep->isState(LLDrawable::INVISIBLE|LLDrawable::FORCE_INVISIBLE)) - { - drawablep->setVisible(camera, NULL, FALSE); - } - else if (drawablep->isState(LLDrawable::CLEAR_INVISIBLE)) - { - // clear invisible flag here to avoid single frame glitch - drawablep->clearState(LLDrawable::FORCE_INVISIBLE|LLDrawable::CLEAR_INVISIBLE); - } - } - - if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD) - { - LLSpatialGroup* group = drawablep->getSpatialGroup(); - if (!group || group->changeLOD()) - { - if (drawablep->isVisible()) - { - if (!drawablep->isActive()) - { - bool force_update = false; - drawablep->updateDistance(camera, force_update); - } - else if (drawablep->isAvatar()) - { - bool force_update = false; - drawablep->updateDistance(camera, force_update); // calls vobj->updateLOD() which calls LLVOAvatar::updateVisibility() - } - } - } - } - - if (!drawablep->getVOVolume()) - { - for (LLDrawable::face_list_t::iterator iter = drawablep->mFaces.begin(); - iter != drawablep->mFaces.end(); iter++) - { - LLFace* facep = *iter; - - if (facep->hasGeometry()) - { - if (facep->getPool()) - { - facep->getPool()->enqueue(facep); - } - else - { - break; - } - } - } - } - - - mNumVisibleFaces += drawablep->getNumFaces(); -} - - -void forAllDrawables(LLCullResult::sg_list_t::iterator begin, - LLCullResult::sg_list_t::iterator end, - void (*func)(LLDrawable*)) -{ - for (LLCullResult::sg_list_t::iterator i = begin; i != end; ++i) - { - for (LLSpatialGroup::element_iter j = (*i)->getData().begin(); j != (*i)->getData().end(); ++j) - { - func(*j); - } - } -} - -void LLPipeline::forAllVisibleDrawables(void (*func)(LLDrawable*)) -{ - forAllDrawables(sCull->beginDrawableGroups(), sCull->endDrawableGroups(), func); - forAllDrawables(sCull->beginVisibleGroups(), sCull->endVisibleGroups(), func); -} - -//function for creating scripted beacons -void renderScriptedBeacons(LLDrawable* drawablep) -{ - LLViewerObject *vobj = drawablep->getVObj(); - if (vobj - && !vobj->isAvatar() - && !vobj->getParent() - && vobj->flagScripted()) - { - if (gPipeline.sRenderBeacons) - { - gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(1.f, 0.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f), gSavedSettings.getS32("DebugBeaconLineWidth")); - } - - if (gPipeline.sRenderHighlight) - { - S32 face_id; - S32 count = drawablep->getNumFaces(); - for (face_id = 0; face_id < count; face_id++) - { - gPipeline.mHighlightFaces.push_back(drawablep->getFace(face_id) ); - } - } - } -} - -void renderScriptedTouchBeacons(LLDrawable* drawablep) -{ - LLViewerObject *vobj = drawablep->getVObj(); - if (vobj - && !vobj->isAvatar() - && !vobj->getParent() - && vobj->flagScripted() - && vobj->flagHandleTouch()) - { - if (gPipeline.sRenderBeacons) - { - gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(1.f, 0.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f), gSavedSettings.getS32("DebugBeaconLineWidth")); - } - - if (gPipeline.sRenderHighlight) - { - S32 face_id; - S32 count = drawablep->getNumFaces(); - for (face_id = 0; face_id < count; face_id++) - { - gPipeline.mHighlightFaces.push_back(drawablep->getFace(face_id) ); - } - } - } -} - -void renderPhysicalBeacons(LLDrawable* drawablep) -{ - LLViewerObject *vobj = drawablep->getVObj(); - if (vobj - && !vobj->isAvatar() - //&& !vobj->getParent() - && vobj->usePhysics()) - { - if (gPipeline.sRenderBeacons) - { - gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(0.f, 1.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f), gSavedSettings.getS32("DebugBeaconLineWidth")); - } - - if (gPipeline.sRenderHighlight) - { - S32 face_id; - S32 count = drawablep->getNumFaces(); - for (face_id = 0; face_id < count; face_id++) - { - gPipeline.mHighlightFaces.push_back(drawablep->getFace(face_id) ); - } - } - } -} - -void renderParticleBeacons(LLDrawable* drawablep) -{ - // Look for attachments, objects, etc. - LLViewerObject *vobj = drawablep->getVObj(); - if (vobj - && vobj->isParticleSource()) - { - if (gPipeline.sRenderBeacons) - { - LLColor4 light_blue(0.5f, 0.5f, 1.f, 0.5f); - gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", light_blue, LLColor4(1.f, 1.f, 1.f, 0.5f), gSavedSettings.getS32("DebugBeaconLineWidth")); - } - - if (gPipeline.sRenderHighlight) - { - S32 face_id; - S32 count = drawablep->getNumFaces(); - for (face_id = 0; face_id < count; face_id++) - { - gPipeline.mHighlightFaces.push_back(drawablep->getFace(face_id) ); - } - } - } -} - -void renderSoundHighlights(LLDrawable* drawablep) -{ - // Look for attachments, objects, etc. - LLViewerObject *vobj = drawablep->getVObj(); - if (vobj && vobj->isAudioSource()) - { - if (gPipeline.sRenderHighlight) - { - S32 face_id; - S32 count = drawablep->getNumFaces(); - for (face_id = 0; face_id < count; face_id++) - { - gPipeline.mHighlightFaces.push_back(drawablep->getFace(face_id) ); - } - } - } -} - -void LLPipeline::postSort(LLCamera& camera) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_POST_SORT); - LLFastTimer ftm(FTM_STATESORT_POSTSORT); - - assertInitialized(); - - llpushcallstacks ; - //rebuild drawable geometry - for (LLCullResult::sg_list_t::iterator i = sCull->beginDrawableGroups(); i != sCull->endDrawableGroups(); ++i) - { - LLSpatialGroup* group = *i; - if (!sUseOcclusion || - !group->isOcclusionState(LLSpatialGroup::OCCLUDED)) - { - group->rebuildGeom(); - } - } - llpushcallstacks ; - //rebuild groups - sCull->assertDrawMapsEmpty(); - - /*LLSpatialGroup::sNoDelete = FALSE; - for (LLCullResult::sg_list_t::iterator i = sCull->beginVisibleGroups(); i != sCull->endVisibleGroups(); ++i) - { - LLSpatialGroup* group = *i; - if (sUseOcclusion && - group->isState(LLSpatialGroup::OCCLUDED)) - { - continue; - } - - group->rebuildGeom(); - } - LLSpatialGroup::sNoDelete = TRUE;*/ - - - rebuildPriorityGroups(); - llpushcallstacks ; - - const S32 bin_count = 1024*8; - - static LLCullResult::drawinfo_list_t alpha_bins[bin_count]; - static U32 bin_size[bin_count]; - - //clear one bin per frame to avoid memory bloat - static S32 clear_idx = 0; - clear_idx = (1+clear_idx)%bin_count; - alpha_bins[clear_idx].clear(); - - for (U32 j = 0; j < bin_count; j++) - { - bin_size[j] = 0; - } - - //build render map - for (LLCullResult::sg_list_t::iterator i = sCull->beginVisibleGroups(); i != sCull->endVisibleGroups(); ++i) - { - LLSpatialGroup* group = *i; - if (sUseOcclusion && - group->isOcclusionState(LLSpatialGroup::OCCLUDED)) - { - continue; - } - - if (group->isState(LLSpatialGroup::NEW_DRAWINFO) && group->isState(LLSpatialGroup::GEOM_DIRTY)) - { //no way this group is going to be drawable without a rebuild - group->rebuildGeom(); - } - - for (LLSpatialGroup::draw_map_t::iterator j = group->mDrawMap.begin(); j != group->mDrawMap.end(); ++j) - { - LLSpatialGroup::drawmap_elem_t& src_vec = j->second; - if (!hasRenderType(j->first)) - { - continue; - } - - for (LLSpatialGroup::drawmap_elem_t::iterator k = src_vec.begin(); k != src_vec.end(); ++k) - { - if (sMinRenderSize > 0.f) - { - LLVector3 bounds = (*k)->mExtents[1]-(*k)->mExtents[0]; - if (llmax(llmax(bounds.mV[0], bounds.mV[1]), bounds.mV[2]) > sMinRenderSize) - { - sCull->pushDrawInfo(j->first, *k); - } - } - else - { - sCull->pushDrawInfo(j->first, *k); - } - } - } - - if (hasRenderType(LLPipeline::RENDER_TYPE_PASS_ALPHA)) - { - LLSpatialGroup::draw_map_t::iterator alpha = group->mDrawMap.find(LLRenderPass::PASS_ALPHA); - - if (alpha != group->mDrawMap.end()) - { //store alpha groups for sorting - LLSpatialBridge* bridge = group->mSpatialPartition->asBridge(); - if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD) - { - if (bridge) - { - LLCamera trans_camera = bridge->transformCamera(camera); - group->updateDistance(trans_camera); - } - else - { - group->updateDistance(camera); - } - } - - if (hasRenderType(LLDrawPool::POOL_ALPHA)) - { - sCull->pushAlphaGroup(group); - } - } - } - } - - if (!sShadowRender) - { - //sort by texture or bump map - for (U32 i = 0; i < LLRenderPass::NUM_RENDER_TYPES; ++i) - { - if (i == LLRenderPass::PASS_BUMP) - { - std::sort(sCull->beginRenderMap(i), sCull->endRenderMap(i), LLDrawInfo::CompareBump()); - } - else - { - std::sort(sCull->beginRenderMap(i), sCull->endRenderMap(i), LLDrawInfo::CompareTexturePtrMatrix()); - } - } - - std::sort(sCull->beginAlphaGroups(), sCull->endAlphaGroups(), LLSpatialGroup::CompareDepthGreater()); - } - llpushcallstacks ; - // only render if the flag is set. The flag is only set if we are in edit mode or the toggle is set in the menus - if (LLFloaterReg::instanceVisible("beacons") && !sShadowRender) - { - if (sRenderScriptedTouchBeacons) - { - // Only show the beacon on the root object. - forAllVisibleDrawables(renderScriptedTouchBeacons); - } - else - if (sRenderScriptedBeacons) - { - // Only show the beacon on the root object. - forAllVisibleDrawables(renderScriptedBeacons); - } - - if (sRenderPhysicalBeacons) - { - // Only show the beacon on the root object. - forAllVisibleDrawables(renderPhysicalBeacons); - } - - if (sRenderParticleBeacons) - { - forAllVisibleDrawables(renderParticleBeacons); - } - - // If god mode, also show audio cues - if (sRenderSoundBeacons && gAudiop) - { - // Walk all sound sources and render out beacons for them. Note, this isn't done in the ForAllVisibleDrawables function, because some are not visible. - LLAudioEngine::source_map::iterator iter; - for (iter = gAudiop->mAllSources.begin(); iter != gAudiop->mAllSources.end(); ++iter) - { - LLAudioSource *sourcep = iter->second; - - LLVector3d pos_global = sourcep->getPositionGlobal(); - LLVector3 pos = gAgent.getPosAgentFromGlobal(pos_global); - if (gPipeline.sRenderBeacons) - { - //pos += LLVector3(0.f, 0.f, 0.2f); - gObjectList.addDebugBeacon(pos, "", LLColor4(1.f, 1.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f), gSavedSettings.getS32("DebugBeaconLineWidth")); - } - } - // now deal with highlights for all those seeable sound sources - forAllVisibleDrawables(renderSoundHighlights); - } - } - llpushcallstacks ; - // If managing your telehub, draw beacons at telehub and currently selected spawnpoint. - if (LLFloaterTelehub::renderBeacons()) - { - LLFloaterTelehub::addBeacons(); - } - - if (!sShadowRender) - { - mSelectedFaces.clear(); - - // Draw face highlights for selected faces. - if (LLSelectMgr::getInstance()->getTEMode()) - { - struct f : public LLSelectedTEFunctor - { - virtual bool apply(LLViewerObject* object, S32 te) - { - if (object->mDrawable) - { - gPipeline.mSelectedFaces.push_back(object->mDrawable->getFace(te)); - } - return true; - } - } func; - LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func); - } - } - - //LLSpatialGroup::sNoDelete = FALSE; - llpushcallstacks ; -} - - -void render_hud_elements() -{ - LLMemType mt_rhe(LLMemType::MTYPE_PIPELINE_RENDER_HUD_ELS); - LLFastTimer t(FTM_RENDER_UI); - gPipeline.disableLights(); - - LLGLDisable fog(GL_FOG); - LLGLSUIDefault gls_ui; - - LLGLEnable stencil(GL_STENCIL_TEST); - glStencilFunc(GL_ALWAYS, 255, 0xFFFFFFFF); - glStencilMask(0xFFFFFFFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - - gGL.color4f(1,1,1,1); - if (!LLPipeline::sReflectionRender && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) - { - LLGLEnable multisample(GL_MULTISAMPLE_ARB); - gViewerWindow->renderSelections(FALSE, FALSE, FALSE); // For HUD version in render_ui_3d() - - // Draw the tracking overlays - LLTracker::render3D(); - - // Show the property lines - LLWorld::getInstance()->renderPropertyLines(); - LLViewerParcelMgr::getInstance()->render(); - LLViewerParcelMgr::getInstance()->renderParcelCollision(); - - // Render name tags. - LLHUDObject::renderAll(); - } - else if (gForceRenderLandFence) - { - // This is only set when not rendering the UI, for parcel snapshots - LLViewerParcelMgr::getInstance()->render(); - } - else if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD)) - { - LLHUDText::renderAllHUD(); - } - gGL.flush(); -} - -void LLPipeline::renderHighlights() -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_RENDER_HL); - - assertInitialized(); - - // Draw 3D UI elements here (before we clear the Z buffer in POOL_HUD) - // Render highlighted faces. - LLGLSPipelineAlpha gls_pipeline_alpha; - LLColor4 color(1.f, 1.f, 1.f, 0.5f); - LLGLEnable color_mat(GL_COLOR_MATERIAL); - disableLights(); - - if (!hasRenderType(LLPipeline::RENDER_TYPE_HUD) && !mHighlightSet.empty()) - { //draw blurry highlight image over screen - LLGLEnable blend(GL_BLEND); - LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); - LLGLDisable test(GL_ALPHA_TEST); - - LLGLEnable stencil(GL_STENCIL_TEST); - gGL.flush(); - glStencilMask(0xFFFFFFFF); - glClearStencil(1); - glClear(GL_STENCIL_BUFFER_BIT); - - glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFFF); - glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); - - gGL.setColorMask(false, false); - for (std::set::iterator iter = mHighlightSet.begin(); iter != mHighlightSet.end(); ++iter) - { - renderHighlight(iter->mItem->getVObj(), 1.f); - } - gGL.setColorMask(true, false); - - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilFunc(GL_NOTEQUAL, 0, 0xFFFFFFFF); - - //gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); - - gGL.pushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - gGL.pushMatrix(); - glLoadIdentity(); - - gGL.getTexUnit(0)->bind(&mHighlight); - - LLVector2 tc1; - LLVector2 tc2; - - tc1.setVec(0,0); - tc2.setVec(2,2); - - gGL.begin(LLRender::TRIANGLES); - - F32 scale = gSavedSettings.getF32("RenderHighlightBrightness"); - LLColor4 color = gSavedSettings.getColor4("RenderHighlightColor"); - F32 thickness = gSavedSettings.getF32("RenderHighlightThickness"); - - for (S32 pass = 0; pass < 2; ++pass) - { - if (pass == 0) - { - gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); - } - else - { - gGL.setSceneBlendType(LLRender::BT_ALPHA); - } - - for (S32 i = 0; i < 8; ++i) - { - for (S32 j = 0; j < 8; ++j) - { - LLVector2 tc(i-4+0.5f, j-4+0.5f); - - F32 dist = 1.f-(tc.length()/sqrtf(32.f)); - dist *= scale/64.f; - - tc *= thickness; - tc.mV[0] = (tc.mV[0])/mHighlight.getWidth(); - tc.mV[1] = (tc.mV[1])/mHighlight.getHeight(); - - gGL.color4f(color.mV[0], - color.mV[1], - color.mV[2], - color.mV[3]*dist); - - gGL.texCoord2f(tc.mV[0]+tc1.mV[0], tc.mV[1]+tc2.mV[1]); - gGL.vertex2f(-1,3); - - gGL.texCoord2f(tc.mV[0]+tc1.mV[0], tc.mV[1]+tc1.mV[1]); - gGL.vertex2f(-1,-1); - - gGL.texCoord2f(tc.mV[0]+tc2.mV[0], tc.mV[1]+tc1.mV[1]); - gGL.vertex2f(3,-1); - } - } - } - - gGL.end(); - - gGL.popMatrix(); - glMatrixMode(GL_MODELVIEW); - gGL.popMatrix(); - - //gGL.setSceneBlendType(LLRender::BT_ALPHA); - } - - if ((LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_INTERFACE) > 0)) - { - gHighlightProgram.bind(); - gHighlightProgram.vertexAttrib4f(LLViewerShaderMgr::MATERIAL_COLOR,1,1,1,0.5f); - } - - if (hasRenderDebugFeatureMask(RENDER_DEBUG_FEATURE_SELECTED)) - { - // Make sure the selection image gets downloaded and decoded - if (!mFaceSelectImagep) - { - mFaceSelectImagep = LLViewerTextureManager::getFetchedTexture(IMG_FACE_SELECT); - } - mFaceSelectImagep->addTextureStats((F32)MAX_IMAGE_AREA); - - U32 count = mSelectedFaces.size(); - for (U32 i = 0; i < count; i++) - { - LLFace *facep = mSelectedFaces[i]; - if (!facep || facep->getDrawable()->isDead()) - { - llerrs << "Bad face on selection" << llendl; - return; - } - - facep->renderSelected(mFaceSelectImagep, color); - } - } - - if (hasRenderDebugFeatureMask(RENDER_DEBUG_FEATURE_SELECTED)) - { - // Paint 'em red! - color.setVec(1.f, 0.f, 0.f, 0.5f); - if ((LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_INTERFACE) > 0)) - { - gHighlightProgram.vertexAttrib4f(LLViewerShaderMgr::MATERIAL_COLOR,1,0,0,0.5f); - } - int count = mHighlightFaces.size(); - for (S32 i = 0; i < count; i++) - { - LLFace* facep = mHighlightFaces[i]; - facep->renderSelected(LLViewerTexture::sNullImagep, color); - } - } - - // Contains a list of the faces of objects that are physical or - // have touch-handlers. - mHighlightFaces.clear(); - - if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_INTERFACE) > 0) - { - gHighlightProgram.unbind(); - } -} - -//debug use -U32 LLPipeline::sCurRenderPoolType = 0 ; - -void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_RENDER_GEOM); - LLFastTimer t(FTM_RENDER_GEOMETRY); - - assertInitialized(); - - F64 saved_modelview[16]; - F64 saved_projection[16]; - - //HACK: preserve/restore matrices around HUD render - if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD)) - { - for (U32 i = 0; i < 16; i++) - { - saved_modelview[i] = gGLModelView[i]; - saved_projection[i] = gGLProjection[i]; - } - } - - /////////////////////////////////////////// - // - // Sync and verify GL state - // - // - - stop_glerror(); - - LLVertexBuffer::unbind(); - - // Do verification of GL state - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); - if (mRenderDebugMask & RENDER_DEBUG_VERIFY) - { - if (!verify()) - { - llerrs << "Pipeline verification failed!" << llendl; - } - } - - LLAppViewer::instance()->pingMainloopTimeout("Pipeline:ForceVBO"); - - // Initialize lots of GL state to "safe" values - glMatrixMode(GL_TEXTURE); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - - LLGLSPipeline gls_pipeline; - LLGLEnable multisample(GL_MULTISAMPLE_ARB); - - LLGLState gls_color_material(GL_COLOR_MATERIAL, mLightingDetail < 2); - - // Toggle backface culling for debugging - LLGLEnable cull_face(mBackfaceCull ? GL_CULL_FACE : 0); - // Set fog - BOOL use_fog = hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FOG); - LLGLEnable fog_enable(use_fog && - !gPipeline.canUseWindLightShadersOnObjects() ? GL_FOG : 0); - gSky.updateFog(camera.getFar()); - if (!use_fog) - { - sUnderWaterRender = FALSE; - } - - gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sDefaultImagep); - LLViewerFetchedTexture::sDefaultImagep->setAddressMode(LLTexUnit::TAM_WRAP); - - ////////////////////////////////////////////// - // - // Actually render all of the geometry - // - // - stop_glerror(); - - LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderDrawPools"); - - for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) - { - LLDrawPool *poolp = *iter; - if (hasRenderType(poolp->getType())) - { - poolp->prerender(); - } - } - - { - LLFastTimer t(FTM_POOLS); - - // HACK: don't calculate local lights if we're rendering the HUD! - // Removing this check will cause bad flickering when there are - // HUD elements being rendered AND the user is in flycam mode -nyx - if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD)) - { - calcNearbyLights(camera); - setupHWLights(NULL); - } - - BOOL occlude = sUseOcclusion > 1; - U32 cur_type = 0; - - pool_set_t::iterator iter1 = mPools.begin(); - while ( iter1 != mPools.end() ) - { - LLDrawPool *poolp = *iter1; - - cur_type = poolp->getType(); - - //debug use - sCurRenderPoolType = cur_type ; - - if (occlude && cur_type >= LLDrawPool::POOL_GRASS) - { - occlude = FALSE; - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - doOcclusion(camera); - } - - pool_set_t::iterator iter2 = iter1; - if (hasRenderType(poolp->getType()) && poolp->getNumPasses() > 0) - { - LLFastTimer t(FTM_POOLRENDER); - - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - - for( S32 i = 0; i < poolp->getNumPasses(); i++ ) - { - LLVertexBuffer::unbind(); - poolp->beginRenderPass(i); - for (iter2 = iter1; iter2 != mPools.end(); iter2++) - { - LLDrawPool *p = *iter2; - if (p->getType() != cur_type) - { - break; - } - - p->render(i); - } - poolp->endRenderPass(i); - LLVertexBuffer::unbind(); - if (gDebugGL || gDebugPipeline) - { - GLint depth; - glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &depth); - if (depth > 3) - { - if (gDebugSession) - { - ll_fail("GL matrix stack corrupted."); - } - llerrs << "GL matrix stack corrupted!" << llendl; - } - std::string msg = llformat("%s pass %d", gPoolNames[cur_type].c_str(), i); - LLGLState::checkStates(msg); - LLGLState::checkTextureChannels(msg); - LLGLState::checkClientArrays(msg); - } - } - } - else - { - // Skip all pools of this type - for (iter2 = iter1; iter2 != mPools.end(); iter2++) - { - LLDrawPool *p = *iter2; - if (p->getType() != cur_type) - { - break; - } - } - } - iter1 = iter2; - stop_glerror(); - } - - LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderDrawPoolsEnd"); - - LLVertexBuffer::unbind(); - - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - - if (occlude) - { - occlude = FALSE; - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - doOcclusion(camera); - } - } - - LLVertexBuffer::unbind(); - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); - - - - stop_glerror(); - - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); - - LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderHighlights"); - - if (!sReflectionRender) - { - renderHighlights(); - } - - // Contains a list of the faces of objects that are physical or - // have touch-handlers. - mHighlightFaces.clear(); - - LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderDebug"); - - renderDebug(); - - LLVertexBuffer::unbind(); - - if (!LLPipeline::sReflectionRender && !LLPipeline::sRenderDeferred) - { - if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) - { - // Render debugging beacons. - gObjectList.renderObjectBeacons(); - gObjectList.resetObjectBeacons(); - } - else - { - // Make sure particle effects disappear - LLHUDObject::renderAllForTimer(); - } - } - else - { - // Make sure particle effects disappear - LLHUDObject::renderAllForTimer(); - } - - LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderGeomEnd"); - - //HACK: preserve/restore matrices around HUD render - if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD)) - { - for (U32 i = 0; i < 16; i++) - { - gGLModelView[i] = saved_modelview[i]; - gGLProjection[i] = saved_projection[i]; - } - } - - LLVertexBuffer::unbind(); - - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); -} - -void LLPipeline::renderGeomDeferred(LLCamera& camera) -{ - LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderGeomDeferred"); - - LLMemType mt_rgd(LLMemType::MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED); - LLFastTimer t(FTM_RENDER_GEOMETRY); - - LLFastTimer t2(FTM_POOLS); - - LLGLEnable cull(GL_CULL_FACE); - - LLGLEnable stencil(GL_STENCIL_TEST); - glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF); - stop_glerror(); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - stop_glerror(); - - for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) - { - LLDrawPool *poolp = *iter; - if (hasRenderType(poolp->getType())) - { - poolp->prerender(); - } - } - - LLGLEnable multisample(GL_MULTISAMPLE_ARB); - - LLVertexBuffer::unbind(); - - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); - - U32 cur_type = 0; - - gGL.setColorMask(true, true); - - pool_set_t::iterator iter1 = mPools.begin(); - - while ( iter1 != mPools.end() ) - { - LLDrawPool *poolp = *iter1; - - cur_type = poolp->getType(); - - pool_set_t::iterator iter2 = iter1; - if (hasRenderType(poolp->getType()) && poolp->getNumDeferredPasses() > 0) - { - LLFastTimer t(FTM_POOLRENDER); - - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - - for( S32 i = 0; i < poolp->getNumDeferredPasses(); i++ ) - { - LLVertexBuffer::unbind(); - poolp->beginDeferredPass(i); - for (iter2 = iter1; iter2 != mPools.end(); iter2++) - { - LLDrawPool *p = *iter2; - if (p->getType() != cur_type) - { - break; - } - - p->renderDeferred(i); - } - poolp->endDeferredPass(i); - LLVertexBuffer::unbind(); - - if (gDebugGL || gDebugPipeline) - { - GLint depth; - glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &depth); - if (depth > 3) - { - llerrs << "GL matrix stack corrupted!" << llendl; - } - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); - } - } - } - else - { - // Skip all pools of this type - for (iter2 = iter1; iter2 != mPools.end(); iter2++) - { - LLDrawPool *p = *iter2; - if (p->getType() != cur_type) - { - break; - } - } - } - iter1 = iter2; - stop_glerror(); - } - - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - - gGL.setColorMask(true, false); -} - -void LLPipeline::renderGeomPostDeferred(LLCamera& camera) -{ - LLMemType mt_rgpd(LLMemType::MTYPE_PIPELINE_RENDER_GEOM_POST_DEF); - LLFastTimer t(FTM_POOLS); - U32 cur_type = 0; - - LLGLEnable cull(GL_CULL_FACE); - - LLGLEnable multisample(GL_MULTISAMPLE_ARB); - - calcNearbyLights(camera); - setupHWLights(NULL); - - gGL.setColorMask(true, false); - - pool_set_t::iterator iter1 = mPools.begin(); - BOOL occlude = LLPipeline::sUseOcclusion > 1; - - while ( iter1 != mPools.end() ) - { - LLDrawPool *poolp = *iter1; - - cur_type = poolp->getType(); - - if (occlude && cur_type >= LLDrawPool::POOL_GRASS) - { - occlude = FALSE; - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - doOcclusion(camera); - gGL.setColorMask(true, false); - } - - pool_set_t::iterator iter2 = iter1; - if (hasRenderType(poolp->getType()) && poolp->getNumPostDeferredPasses() > 0) - { - LLFastTimer t(FTM_POOLRENDER); - - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - - for( S32 i = 0; i < poolp->getNumPostDeferredPasses(); i++ ) - { - LLVertexBuffer::unbind(); - poolp->beginPostDeferredPass(i); - for (iter2 = iter1; iter2 != mPools.end(); iter2++) - { - LLDrawPool *p = *iter2; - if (p->getType() != cur_type) - { - break; - } - - p->renderPostDeferred(i); - } - poolp->endPostDeferredPass(i); - LLVertexBuffer::unbind(); - - if (gDebugGL || gDebugPipeline) - { - GLint depth; - glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &depth); - if (depth > 3) - { - llerrs << "GL matrix stack corrupted!" << llendl; - } - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); - } - } - } - else - { - // Skip all pools of this type - for (iter2 = iter1; iter2 != mPools.end(); iter2++) - { - LLDrawPool *p = *iter2; - if (p->getType() != cur_type) - { - break; - } - } - } - iter1 = iter2; - stop_glerror(); - } - - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - - if (occlude) - { - occlude = FALSE; - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - doOcclusion(camera); - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - } -} - -void LLPipeline::renderGeomShadow(LLCamera& camera) -{ - LLMemType mt_rgs(LLMemType::MTYPE_PIPELINE_RENDER_GEOM_SHADOW); - U32 cur_type = 0; - - LLGLEnable cull(GL_CULL_FACE); - - LLVertexBuffer::unbind(); - - pool_set_t::iterator iter1 = mPools.begin(); - - while ( iter1 != mPools.end() ) - { - LLDrawPool *poolp = *iter1; - - cur_type = poolp->getType(); - - pool_set_t::iterator iter2 = iter1; - if (hasRenderType(poolp->getType()) && poolp->getNumShadowPasses() > 0) - { - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - - for( S32 i = 0; i < poolp->getNumShadowPasses(); i++ ) - { - LLVertexBuffer::unbind(); - poolp->beginShadowPass(i); - for (iter2 = iter1; iter2 != mPools.end(); iter2++) - { - LLDrawPool *p = *iter2; - if (p->getType() != cur_type) - { - break; - } - - p->renderShadow(i); - } - poolp->endShadowPass(i); - LLVertexBuffer::unbind(); - - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); - } - } - else - { - // Skip all pools of this type - for (iter2 = iter1; iter2 != mPools.end(); iter2++) - { - LLDrawPool *p = *iter2; - if (p->getType() != cur_type) - { - break; - } - } - } - iter1 = iter2; - stop_glerror(); - } - - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); -} - - -void LLPipeline::addTrianglesDrawn(S32 index_count, U32 render_type) -{ - assertInitialized(); - S32 count = 0; - if (render_type == LLRender::TRIANGLE_STRIP) - { - count = index_count-2; - } - else - { - count = index_count/3; - } - - mTrianglesDrawn += count; - mBatchCount++; - mMaxBatchSize = llmax(mMaxBatchSize, count); - mMinBatchSize = llmin(mMinBatchSize, count); - - if (LLPipeline::sRenderFrameTest) - { - gViewerWindow->getWindow()->swapBuffers(); - ms_sleep(16); - } -} - -void LLPipeline::renderDebug() -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE); - - assertInitialized(); - - gGL.color4f(1,1,1,1); - - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - gGL.setColorMask(true, false); - - // Debug stuff. - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* region = *iter; - for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) - { - LLSpatialPartition* part = region->getSpatialPartition(i); - if (part) - { - if (hasRenderType(part->mDrawableType)) - { - part->renderDebug(); - } - } - } - } - - for (LLCullResult::bridge_list_t::const_iterator i = sCull->beginVisibleBridge(); i != sCull->endVisibleBridge(); ++i) - { - LLSpatialBridge* bridge = *i; - if (!bridge->isDead() && hasRenderType(bridge->mDrawableType)) - { - glPushMatrix(); - glMultMatrixf((F32*)bridge->mDrawable->getRenderMatrix().mMatrix); - bridge->renderDebug(); - glPopMatrix(); - } - } - - if (hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA)) - { - LLGLEnable blend(GL_BLEND); - LLGLDepthTest depth(TRUE, FALSE); - LLGLDisable cull(GL_CULL_FACE); - - gGL.color4f(1,1,1,1); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - F32 a = 0.1f; - - F32 col[] = - { - 1,0,0,a, - 0,1,0,a, - 0,0,1,a, - 1,0,1,a, - - 1,1,0,a, - 0,1,1,a, - 1,1,1,a, - 1,0,1,a, - }; - - for (U32 i = 0; i < 8; i++) - { - LLVector3* frust = mShadowCamera[i].mAgentFrustum; - - if (i > 3) - { //render shadow frusta as volumes - if (mShadowFrustPoints[i-4].empty()) - { - continue; - } - - gGL.color4fv(col+(i-4)*4); - - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.vertex3fv(frust[0].mV); gGL.vertex3fv(frust[4].mV); - gGL.vertex3fv(frust[1].mV); gGL.vertex3fv(frust[5].mV); - gGL.vertex3fv(frust[2].mV); gGL.vertex3fv(frust[6].mV); - gGL.vertex3fv(frust[3].mV); gGL.vertex3fv(frust[7].mV); - gGL.vertex3fv(frust[0].mV); gGL.vertex3fv(frust[4].mV); - gGL.end(); - - - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.vertex3fv(frust[0].mV); - gGL.vertex3fv(frust[1].mV); - gGL.vertex3fv(frust[3].mV); - gGL.vertex3fv(frust[2].mV); - gGL.end(); - - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.vertex3fv(frust[4].mV); - gGL.vertex3fv(frust[5].mV); - gGL.vertex3fv(frust[7].mV); - gGL.vertex3fv(frust[6].mV); - gGL.end(); - } - - - if (i < 4) - { - - if (i == 0 || !mShadowFrustPoints[i].empty()) - { - //render visible point cloud - gGL.flush(); - glPointSize(8.f); - gGL.begin(LLRender::POINTS); - - F32* c = col+i*4; - gGL.color3fv(c); - - for (U32 j = 0; j < mShadowFrustPoints[i].size(); ++j) - { - gGL.vertex3fv(mShadowFrustPoints[i][j].mV); - - } - gGL.end(); - - gGL.flush(); - glPointSize(1.f); - - LLVector3* ext = mShadowExtents[i]; - LLVector3 pos = (ext[0]+ext[1])*0.5f; - LLVector3 size = (ext[1]-ext[0])*0.5f; - drawBoxOutline(pos, size); - - //render camera frustum splits as outlines - gGL.begin(LLRender::LINES); - gGL.vertex3fv(frust[0].mV); gGL.vertex3fv(frust[1].mV); - gGL.vertex3fv(frust[1].mV); gGL.vertex3fv(frust[2].mV); - gGL.vertex3fv(frust[2].mV); gGL.vertex3fv(frust[3].mV); - gGL.vertex3fv(frust[3].mV); gGL.vertex3fv(frust[0].mV); - gGL.vertex3fv(frust[4].mV); gGL.vertex3fv(frust[5].mV); - gGL.vertex3fv(frust[5].mV); gGL.vertex3fv(frust[6].mV); - gGL.vertex3fv(frust[6].mV); gGL.vertex3fv(frust[7].mV); - gGL.vertex3fv(frust[7].mV); gGL.vertex3fv(frust[4].mV); - gGL.vertex3fv(frust[0].mV); gGL.vertex3fv(frust[4].mV); - gGL.vertex3fv(frust[1].mV); gGL.vertex3fv(frust[5].mV); - gGL.vertex3fv(frust[2].mV); gGL.vertex3fv(frust[6].mV); - gGL.vertex3fv(frust[3].mV); gGL.vertex3fv(frust[7].mV); - gGL.end(); - } - - } - - /*for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* region = *iter; - for (U32 j = 0; j < LLViewerRegion::NUM_PARTITIONS; j++) - { - LLSpatialPartition* part = region->getSpatialPartition(j); - if (part) - { - if (hasRenderType(part->mDrawableType)) - { - part->renderIntersectingBBoxes(&mShadowCamera[i]); - } - } - } - }*/ - } - } - - if (mRenderDebugMask & RENDER_DEBUG_COMPOSITION) - { - // Debug composition layers - F32 x, y; - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - if (gAgent.getRegion()) - { - gGL.begin(LLRender::POINTS); - // Draw the composition layer for the region that I'm in. - for (x = 0; x <= 260; x++) - { - for (y = 0; y <= 260; y++) - { - if ((x > 255) || (y > 255)) - { - gGL.color4f(1.f, 0.f, 0.f, 1.f); - } - else - { - gGL.color4f(0.f, 0.f, 1.f, 1.f); - } - F32 z = gAgent.getRegion()->getCompositionXY((S32)x, (S32)y); - z *= 5.f; - z += 50.f; - gGL.vertex3f(x, y, z); - } - } - gGL.end(); - } - } - - if (mRenderDebugMask & LLPipeline::RENDER_DEBUG_BUILD_QUEUE) - { - U32 count = 0; - U32 size = mBuildQ2.size(); - LLColor4 col; - - LLGLEnable blend(GL_BLEND); - LLGLDepthTest depth(GL_TRUE, GL_FALSE); - gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep); - - for (LLSpatialGroup::sg_vector_t::iterator iter = mGroupQ2.begin(); iter != mGroupQ2.end(); ++iter) - { - LLSpatialGroup* group = *iter; - if (group->isDead()) - { - continue; - } - - LLSpatialBridge* bridge = group->mSpatialPartition->asBridge(); - - if (bridge && (!bridge->mDrawable || bridge->mDrawable->isDead())) - { - continue; - } - - if (bridge) - { - gGL.pushMatrix(); - glMultMatrixf((F32*)bridge->mDrawable->getRenderMatrix().mMatrix); - } - - F32 alpha = (F32) (size-count)/size; - - - LLVector2 c(1.f-alpha, alpha); - c.normVec(); - - - ++count; - col.set(c.mV[0], c.mV[1], 0, alpha*0.5f+0.1f); - group->drawObjectBox(col); - - if (bridge) - { - gGL.popMatrix(); - } - } - } - - gGL.flush(); -} - -void LLPipeline::rebuildPools() -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_REBUILD_POOLS); - - assertInitialized(); - - S32 max_count = mPools.size(); - pool_set_t::iterator iter1 = mPools.upper_bound(mLastRebuildPool); - while(max_count > 0 && mPools.size() > 0) // && num_rebuilds < MAX_REBUILDS) - { - if (iter1 == mPools.end()) - { - iter1 = mPools.begin(); - } - LLDrawPool* poolp = *iter1; - - if (poolp->isDead()) - { - mPools.erase(iter1++); - removeFromQuickLookup( poolp ); - if (poolp == mLastRebuildPool) - { - mLastRebuildPool = NULL; - } - delete poolp; - } - else - { - mLastRebuildPool = poolp; - iter1++; - } - max_count--; - } - - if (isAgentAvatarValid()) - { - gAgentAvatarp->rebuildHUD(); - } -} - -void LLPipeline::addToQuickLookup( LLDrawPool* new_poolp ) -{ - LLMemType mt(LLMemType::MTYPE_PIPELINE_QUICK_LOOKUP); - - assertInitialized(); - - switch( new_poolp->getType() ) - { - case LLDrawPool::POOL_SIMPLE: - if (mSimplePool) - { - llassert(0); - llwarns << "Ignoring duplicate simple pool." << llendl; - } - else - { - mSimplePool = (LLRenderPass*) new_poolp; - } - break; - - case LLDrawPool::POOL_GRASS: - if (mGrassPool) - { - llassert(0); - llwarns << "Ignoring duplicate grass pool." << llendl; - } - else - { - mGrassPool = (LLRenderPass*) new_poolp; - } - break; - - case LLDrawPool::POOL_FULLBRIGHT: - if (mFullbrightPool) - { - llassert(0); - llwarns << "Ignoring duplicate simple pool." << llendl; - } - else - { - mFullbrightPool = (LLRenderPass*) new_poolp; - } - break; - - case LLDrawPool::POOL_INVISIBLE: - if (mInvisiblePool) - { - llassert(0); - llwarns << "Ignoring duplicate simple pool." << llendl; - } - else - { - mInvisiblePool = (LLRenderPass*) new_poolp; - } - break; - - case LLDrawPool::POOL_GLOW: - if (mGlowPool) - { - llassert(0); - llwarns << "Ignoring duplicate glow pool." << llendl; - } - else - { - mGlowPool = (LLRenderPass*) new_poolp; - } - break; - - case LLDrawPool::POOL_TREE: - mTreePools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ; - break; - - case LLDrawPool::POOL_TERRAIN: - mTerrainPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ; - break; - - case LLDrawPool::POOL_BUMP: - if (mBumpPool) - { - llassert(0); - llwarns << "Ignoring duplicate bump pool." << llendl; - } - else - { - mBumpPool = new_poolp; - } - break; - - case LLDrawPool::POOL_ALPHA: - if( mAlphaPool ) - { - llassert(0); - llwarns << "LLPipeline::addPool(): Ignoring duplicate Alpha pool" << llendl; - } - else - { - mAlphaPool = new_poolp; - } - break; - - case LLDrawPool::POOL_AVATAR: - break; // Do nothing - - case LLDrawPool::POOL_SKY: - if( mSkyPool ) - { - llassert(0); - llwarns << "LLPipeline::addPool(): Ignoring duplicate Sky pool" << llendl; - } - else - { - mSkyPool = new_poolp; - } - break; - - case LLDrawPool::POOL_WATER: - if( mWaterPool ) - { - llassert(0); - llwarns << "LLPipeline::addPool(): Ignoring duplicate Water pool" << llendl; - } - else - { - mWaterPool = new_poolp; - } - break; - - case LLDrawPool::POOL_GROUND: - if( mGroundPool ) - { - llassert(0); - llwarns << "LLPipeline::addPool(): Ignoring duplicate Ground Pool" << llendl; - } - else - { - mGroundPool = new_poolp; - } - break; - - case LLDrawPool::POOL_WL_SKY: - if( mWLSkyPool ) - { - llassert(0); - llwarns << "LLPipeline::addPool(): Ignoring duplicate WLSky Pool" << llendl; - } - else - { - mWLSkyPool = new_poolp; - } - break; - - default: - llassert(0); - llwarns << "Invalid Pool Type in LLPipeline::addPool()" << llendl; - break; - } -} - -void LLPipeline::removePool( LLDrawPool* poolp ) -{ - assertInitialized(); - removeFromQuickLookup(poolp); - mPools.erase(poolp); - delete poolp; -} - -void LLPipeline::removeFromQuickLookup( LLDrawPool* poolp ) -{ - assertInitialized(); - LLMemType mt(LLMemType::MTYPE_PIPELINE); - switch( poolp->getType() ) - { - case LLDrawPool::POOL_SIMPLE: - llassert(mSimplePool == poolp); - mSimplePool = NULL; - break; - - case LLDrawPool::POOL_GRASS: - llassert(mGrassPool == poolp); - mGrassPool = NULL; - break; - - case LLDrawPool::POOL_FULLBRIGHT: - llassert(mFullbrightPool == poolp); - mFullbrightPool = NULL; - break; - - case LLDrawPool::POOL_INVISIBLE: - llassert(mInvisiblePool == poolp); - mInvisiblePool = NULL; - break; - - case LLDrawPool::POOL_WL_SKY: - llassert(mWLSkyPool == poolp); - mWLSkyPool = NULL; - break; - - case LLDrawPool::POOL_GLOW: - llassert(mGlowPool == poolp); - mGlowPool = NULL; - break; - - case LLDrawPool::POOL_TREE: - #ifdef _DEBUG - { - BOOL found = mTreePools.erase( (uintptr_t)poolp->getTexture() ); - llassert( found ); - } - #else - mTreePools.erase( (uintptr_t)poolp->getTexture() ); - #endif - break; - - case LLDrawPool::POOL_TERRAIN: - #ifdef _DEBUG - { - BOOL found = mTerrainPools.erase( (uintptr_t)poolp->getTexture() ); - llassert( found ); - } - #else - mTerrainPools.erase( (uintptr_t)poolp->getTexture() ); - #endif - break; - - case LLDrawPool::POOL_BUMP: - llassert( poolp == mBumpPool ); - mBumpPool = NULL; - break; - - case LLDrawPool::POOL_ALPHA: - llassert( poolp == mAlphaPool ); - mAlphaPool = NULL; - break; - - case LLDrawPool::POOL_AVATAR: - break; // Do nothing - - case LLDrawPool::POOL_SKY: - llassert( poolp == mSkyPool ); - mSkyPool = NULL; - break; - - case LLDrawPool::POOL_WATER: - llassert( poolp == mWaterPool ); - mWaterPool = NULL; - break; - - case LLDrawPool::POOL_GROUND: - llassert( poolp == mGroundPool ); - mGroundPool = NULL; - break; - - default: - llassert(0); - llwarns << "Invalid Pool Type in LLPipeline::removeFromQuickLookup() type=" << poolp->getType() << llendl; - break; - } -} - -void LLPipeline::resetDrawOrders() -{ - assertInitialized(); - // Iterate through all of the draw pools and rebuild them. - for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) - { - LLDrawPool *poolp = *iter; - poolp->resetDrawOrders(); - } -} - -//============================================================================ -// Once-per-frame setup of hardware lights, -// including sun/moon, avatar backlight, and up to 6 local lights - -void LLPipeline::setupAvatarLights(BOOL for_edit) -{ - assertInitialized(); - - if (for_edit) - { - LLColor4 diffuse(1.f, 1.f, 1.f, 0.f); - LLVector4 light_pos_cam(-8.f, 0.25f, 10.f, 0.f); // w==0 => directional light - LLMatrix4 camera_mat = LLViewerCamera::getInstance()->getModelview(); - LLMatrix4 camera_rot(camera_mat.getMat3()); - camera_rot.invert(); - LLVector4 light_pos = light_pos_cam * camera_rot; - - light_pos.normalize(); - - mHWLightColors[1] = diffuse; - glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse.mV); - glLightfv(GL_LIGHT1, GL_AMBIENT, LLColor4::black.mV); - glLightfv(GL_LIGHT1, GL_SPECULAR, LLColor4::black.mV); - glLightfv(GL_LIGHT1, GL_POSITION, light_pos.mV); - glLightf (GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.0f); - glLightf (GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.0f); - glLightf (GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0f); - glLightf (GL_LIGHT1, GL_SPOT_EXPONENT, 0.0f); - glLightf (GL_LIGHT1, GL_SPOT_CUTOFF, 180.0f); - } - else if (gAvatarBacklight) // Always true (unless overridden in a devs .ini) - { - LLVector3 opposite_pos = -1.f * mSunDir; - LLVector3 orthog_light_pos = mSunDir % LLVector3::z_axis; - LLVector4 backlight_pos = LLVector4(lerp(opposite_pos, orthog_light_pos, 0.3f), 0.0f); - backlight_pos.normalize(); - - LLColor4 light_diffuse = mSunDiffuse; - LLColor4 backlight_diffuse(1.f - light_diffuse.mV[VRED], 1.f - light_diffuse.mV[VGREEN], 1.f - light_diffuse.mV[VBLUE], 1.f); - F32 max_component = 0.001f; - for (S32 i = 0; i < 3; i++) - { - if (backlight_diffuse.mV[i] > max_component) - { - max_component = backlight_diffuse.mV[i]; - } - } - F32 backlight_mag; - if (gSky.getSunDirection().mV[2] >= LLSky::NIGHTTIME_ELEVATION_COS) - { - backlight_mag = BACKLIGHT_DAY_MAGNITUDE_OBJECT; - } - else - { - backlight_mag = BACKLIGHT_NIGHT_MAGNITUDE_OBJECT; - } - backlight_diffuse *= backlight_mag / max_component; - - mHWLightColors[1] = backlight_diffuse; - glLightfv(GL_LIGHT1, GL_POSITION, backlight_pos.mV); // this is just sun/moon direction - glLightfv(GL_LIGHT1, GL_DIFFUSE, backlight_diffuse.mV); - glLightfv(GL_LIGHT1, GL_AMBIENT, LLColor4::black.mV); - glLightfv(GL_LIGHT1, GL_SPECULAR, LLColor4::black.mV); - glLightf (GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.0f); - glLightf (GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.0f); - glLightf (GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0f); - glLightf (GL_LIGHT1, GL_SPOT_EXPONENT, 0.0f); - glLightf (GL_LIGHT1, GL_SPOT_CUTOFF, 180.0f); - } - else - { - mHWLightColors[1] = LLColor4::black; - glLightfv(GL_LIGHT1, GL_DIFFUSE, LLColor4::black.mV); - glLightfv(GL_LIGHT1, GL_AMBIENT, LLColor4::black.mV); - glLightfv(GL_LIGHT1, GL_SPECULAR, LLColor4::black.mV); - } -} - -static F32 calc_light_dist(LLVOVolume* light, const LLVector3& cam_pos, F32 max_dist) -{ - F32 inten = light->getLightIntensity(); - if (inten < .001f) - { - return max_dist; - } - F32 radius = light->getLightRadius(); - BOOL selected = light->isSelected(); - LLVector3 dpos = light->getRenderPosition() - cam_pos; - F32 dist2 = dpos.lengthSquared(); - if (!selected && dist2 > (max_dist + radius)*(max_dist + radius)) - { - return max_dist; - } - F32 dist = fsqrtf(dist2); - dist *= 1.f / inten; - dist -= radius; - if (selected) - { - dist -= 10000.f; // selected lights get highest priority - } - if (light->mDrawable.notNull() && light->mDrawable->isState(LLDrawable::ACTIVE)) - { - // moving lights get a little higher priority (too much causes artifacts) - dist -= light->getLightRadius()*0.25f; - } - return dist; -} - -void LLPipeline::calcNearbyLights(LLCamera& camera) -{ - assertInitialized(); - - if (LLPipeline::sReflectionRender) - { - return; - } - - if (mLightingDetail >= 1) - { - // mNearbyLight (and all light_set_t's) are sorted such that - // begin() == the closest light and rbegin() == the farthest light - const S32 MAX_LOCAL_LIGHTS = 6; -// LLVector3 cam_pos = gAgentCamera.getCameraPositionAgent(); - LLVector3 cam_pos = LLViewerJoystick::getInstance()->getOverrideCamera() ? - camera.getOrigin() : - gAgent.getPositionAgent(); - - F32 max_dist = LIGHT_MAX_RADIUS * 4.f; // ignore enitrely lights > 4 * max light rad - - // UPDATE THE EXISTING NEARBY LIGHTS - light_set_t cur_nearby_lights; - for (light_set_t::iterator iter = mNearbyLights.begin(); - iter != mNearbyLights.end(); iter++) - { - const Light* light = &(*iter); - LLDrawable* drawable = light->drawable; - LLVOVolume* volight = drawable->getVOVolume(); - if (!volight || !drawable->isState(LLDrawable::LIGHT)) - { - drawable->clearState(LLDrawable::NEARBY_LIGHT); - continue; - } - if (light->fade <= -LIGHT_FADE_TIME) - { - drawable->clearState(LLDrawable::NEARBY_LIGHT); - continue; - } - if (!sRenderAttachedLights && volight && volight->isAttachment()) - { - drawable->clearState(LLDrawable::NEARBY_LIGHT); - continue; - } - - F32 dist = calc_light_dist(volight, cam_pos, max_dist); - cur_nearby_lights.insert(Light(drawable, dist, light->fade)); - } - mNearbyLights = cur_nearby_lights; - - // FIND NEW LIGHTS THAT ARE IN RANGE - light_set_t new_nearby_lights; - for (LLDrawable::drawable_set_t::iterator iter = mLights.begin(); - iter != mLights.end(); ++iter) - { - LLDrawable* drawable = *iter; - LLVOVolume* light = drawable->getVOVolume(); - if (!light || drawable->isState(LLDrawable::NEARBY_LIGHT)) - { - continue; - } - if (light->isHUDAttachment()) - { - continue; // no lighting from HUD objects - } - F32 dist = calc_light_dist(light, cam_pos, max_dist); - if (dist >= max_dist) - { - continue; - } - if (!sRenderAttachedLights && light && light->isAttachment()) - { - continue; - } - new_nearby_lights.insert(Light(drawable, dist, 0.f)); - if (new_nearby_lights.size() > (U32)MAX_LOCAL_LIGHTS) - { - new_nearby_lights.erase(--new_nearby_lights.end()); - const Light& last = *new_nearby_lights.rbegin(); - max_dist = last.dist; - } - } - - // INSERT ANY NEW LIGHTS - for (light_set_t::iterator iter = new_nearby_lights.begin(); - iter != new_nearby_lights.end(); iter++) - { - const Light* light = &(*iter); - if (mNearbyLights.size() < (U32)MAX_LOCAL_LIGHTS) - { - mNearbyLights.insert(*light); - ((LLDrawable*) light->drawable)->setState(LLDrawable::NEARBY_LIGHT); - } - else - { - // crazy cast so that we can overwrite the fade value - // even though gcc enforces sets as const - // (fade value doesn't affect sort so this is safe) - Light* farthest_light = ((Light*) (&(*(mNearbyLights.rbegin())))); - if (light->dist < farthest_light->dist) - { - if (farthest_light->fade >= 0.f) - { - farthest_light->fade = -gFrameIntervalSeconds; - } - } - else - { - break; // none of the other lights are closer - } - } - } - - } -} - -void LLPipeline::setupHWLights(LLDrawPool* pool) -{ - assertInitialized(); - - // Ambient - LLColor4 ambient = gSky.getTotalAmbientColor(); - glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambient.mV); - - // Light 0 = Sun or Moon (All objects) - { - if (gSky.getSunDirection().mV[2] >= LLSky::NIGHTTIME_ELEVATION_COS) - { - mSunDir.setVec(gSky.getSunDirection()); - mSunDiffuse.setVec(gSky.getSunDiffuseColor()); - } - else - { - mSunDir.setVec(gSky.getMoonDirection()); - mSunDiffuse.setVec(gSky.getMoonDiffuseColor()); - } - - F32 max_color = llmax(mSunDiffuse.mV[0], mSunDiffuse.mV[1], mSunDiffuse.mV[2]); - if (max_color > 1.f) - { - mSunDiffuse *= 1.f/max_color; - } - mSunDiffuse.clamp(); - - LLVector4 light_pos(mSunDir, 0.0f); - LLColor4 light_diffuse = mSunDiffuse; - mHWLightColors[0] = light_diffuse; - glLightfv(GL_LIGHT0, GL_POSITION, light_pos.mV); // this is just sun/moon direction - glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse.mV); - glLightfv(GL_LIGHT0, GL_AMBIENT, LLColor4::black.mV); - glLightfv(GL_LIGHT0, GL_SPECULAR, LLColor4::black.mV); - glLightf (GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0f); - glLightf (GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0f); - glLightf (GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.0f); - glLightf (GL_LIGHT0, GL_SPOT_EXPONENT, 0.0f); - glLightf (GL_LIGHT0, GL_SPOT_CUTOFF, 180.0f); - } - - // Light 1 = Backlight (for avatars) - // (set by enableLightsAvatar) - - S32 cur_light = 2; - - // Nearby lights = LIGHT 2-7 - - mLightMovingMask = 0; - - if (mLightingDetail >= 1) - { - for (light_set_t::iterator iter = mNearbyLights.begin(); - iter != mNearbyLights.end(); ++iter) - { - LLDrawable* drawable = iter->drawable; - LLVOVolume* light = drawable->getVOVolume(); - if (!light) - { - continue; - } - if (drawable->isState(LLDrawable::ACTIVE)) - { - mLightMovingMask |= (1<getLightColor(); - light_color.mV[3] = 0.0f; - - F32 fade = iter->fade; - if (fade < LIGHT_FADE_TIME) - { - // fade in/out light - if (fade >= 0.f) - { - fade = fade / LIGHT_FADE_TIME; - ((Light*) (&(*iter)))->fade += gFrameIntervalSeconds; - } - else - { - fade = 1.f + fade / LIGHT_FADE_TIME; - ((Light*) (&(*iter)))->fade -= gFrameIntervalSeconds; - } - fade = llclamp(fade,0.f,1.f); - light_color *= fade; - } - - LLVector3 light_pos(light->getRenderPosition()); - LLVector4 light_pos_gl(light_pos, 1.0f); - - F32 light_radius = llmax(light->getLightRadius(), 0.001f); - - F32 x = (3.f * (1.f + light->getLightFalloff())); // why this magic? probably trying to match a historic behavior. - float linatten = x / (light_radius); // % of brightness at radius - - mHWLightColors[cur_light] = light_color; - S32 gllight = GL_LIGHT0+cur_light; - glLightfv(gllight, GL_POSITION, light_pos_gl.mV); - glLightfv(gllight, GL_DIFFUSE, light_color.mV); - glLightfv(gllight, GL_AMBIENT, LLColor4::black.mV); - glLightf (gllight, GL_CONSTANT_ATTENUATION, 0.0f); - glLightf (gllight, GL_LINEAR_ATTENUATION, linatten); - glLightf (gllight, GL_QUADRATIC_ATTENUATION, 0.0f); - if (light->isLightSpotlight() // directional (spot-)light - && (LLPipeline::sRenderDeferred || gSavedSettings.getBOOL("RenderSpotLightsInNondeferred"))) // these are only rendered as GL spotlights if we're in deferred rendering mode *or* the setting forces them on - { - LLVector3 spotparams = light->getSpotLightParams(); - LLQuaternion quat = light->getRenderRotation(); - LLVector3 at_axis(0,0,-1); // this matches deferred rendering's object light direction - at_axis *= quat; - //llinfos << "SPOT!!!!!!! fov: " << spotparams.mV[0] << " focus: " << spotparams.mV[1] << " dir: " << at_axis << llendl; - glLightfv(gllight, GL_SPOT_DIRECTION, at_axis.mV); - glLightf (gllight, GL_SPOT_EXPONENT, 2.0f); // 2.0 = good old dot product ^ 2 - glLightf (gllight, GL_SPOT_CUTOFF, 90.0f); // hemisphere - const float specular[] = {0.f, 0.f, 0.f, 0.f}; - glLightfv(gllight, GL_SPECULAR, specular); - } - else // omnidirectional (point) light - { - glLightf (gllight, GL_SPOT_EXPONENT, 0.0f); - glLightf (gllight, GL_SPOT_CUTOFF, 180.0f); - - // we use specular.w = 1.0 as a cheap hack for the shaders to know that this is omnidirectional rather than a spotlight - const float specular[] = {0.f, 0.f, 0.f, 1.f}; - glLightfv(gllight, GL_SPECULAR, specular); - //llinfos << "boring light" << llendl; - } - cur_light++; - if (cur_light >= 8) - { - break; // safety - } - } - } - for ( ; cur_light < 8 ; cur_light++) - { - mHWLightColors[cur_light] = LLColor4::black; - S32 gllight = GL_LIGHT0+cur_light; - glLightfv(gllight, GL_DIFFUSE, LLColor4::black.mV); - glLightfv(gllight, GL_AMBIENT, LLColor4::black.mV); - glLightfv(gllight, GL_SPECULAR, LLColor4::black.mV); - } - - if (isAgentAvatarValid() && - gAgentAvatarp->mSpecialRenderMode == 3) - { - LLColor4 light_color = LLColor4::white; - light_color.mV[3] = 0.0f; - - LLVector3 light_pos(LLViewerCamera::getInstance()->getOrigin()); - LLVector4 light_pos_gl(light_pos, 1.0f); - - F32 light_radius = 16.f; - - F32 x = 3.f; - float linatten = x / (light_radius); // % of brightness at radius - - mHWLightColors[2] = light_color; - S32 gllight = GL_LIGHT2; - glLightfv(gllight, GL_POSITION, light_pos_gl.mV); - glLightfv(gllight, GL_DIFFUSE, light_color.mV); - glLightfv(gllight, GL_AMBIENT, LLColor4::black.mV); - glLightfv(gllight, GL_SPECULAR, LLColor4::black.mV); - glLightf (gllight, GL_CONSTANT_ATTENUATION, 0.0f); - glLightf (gllight, GL_LINEAR_ATTENUATION, linatten); - glLightf (gllight, GL_QUADRATIC_ATTENUATION, 0.0f); - glLightf (gllight, GL_SPOT_EXPONENT, 0.0f); - glLightf (gllight, GL_SPOT_CUTOFF, 180.0f); - } - - // Init GL state - glDisable(GL_LIGHTING); - for (S32 gllight=GL_LIGHT0; gllight<=GL_LIGHT7; gllight++) - { - glDisable(gllight); - } - mLightMask = 0; -} - -void LLPipeline::enableLights(U32 mask) -{ - assertInitialized(); - - if (mLightingDetail == 0) - { - mask &= 0xf003; // sun and backlight only (and fullbright bit) - } - if (mLightMask != mask) - { - stop_glerror(); - if (!mLightMask) - { - glEnable(GL_LIGHTING); - } - if (mask) - { - stop_glerror(); - for (S32 i=0; i<8; i++) - { - if (mask & (1<= 2) - { - mask |= mLightMovingMask; // Hardware moving lights - glColor4f(0.f, 0.f, 0.f, 1.0f); // no local lighting by default - } - else - { - mask |= 0xff & (~2); // Hardware local lights - } - enableLights(mask); -} - -void LLPipeline::enableLightsDynamic() -{ - assertInitialized(); - U32 mask = 0xff & (~2); // Local lights - enableLights(mask); - if (mLightingDetail >= 2) - { - glColor4f(0.f, 0.f, 0.f, 1.f); // no local lighting by default - } - - if (isAgentAvatarValid() && getLightingDetail() <= 0) - { - if (gAgentAvatarp->mSpecialRenderMode == 0) // normal - { - gPipeline.enableLightsAvatar(); - } - else if (gAgentAvatarp->mSpecialRenderMode >= 1) // anim preview - { - gPipeline.enableLightsAvatarEdit(LLColor4(0.7f, 0.6f, 0.3f, 1.f)); - } - } -} - -void LLPipeline::enableLightsAvatar() -{ - U32 mask = 0xff; // All lights - setupAvatarLights(FALSE); - enableLights(mask); -} - -void LLPipeline::enableLightsAvatarEdit(const LLColor4& color) -{ - U32 mask = 0x2002; // Avatar backlight only, set ambient - setupAvatarLights(TRUE); - enableLights(mask); - - glLightModelfv(GL_LIGHT_MODEL_AMBIENT,color.mV); -} - -void LLPipeline::enableLightsFullbright(const LLColor4& color) -{ - assertInitialized(); - U32 mask = 0x1000; // Non-0 mask, set ambient - enableLights(mask); - - glLightModelfv(GL_LIGHT_MODEL_AMBIENT,color.mV); - /*if (mLightingDetail >= 2) - { - glColor4f(0.f, 0.f, 0.f, 1.f); // no local lighting by default - }*/ -} - -void LLPipeline::disableLights() -{ - enableLights(0); // no lighting (full bright) - glColor4f(1.f, 1.f, 1.f, 1.f); // lighting color = white by default -} - -//============================================================================ - -class LLMenuItemGL; -class LLInvFVBridge; -struct cat_folder_pair; -class LLVOBranch; -class LLVOLeaf; - -void LLPipeline::findReferences(LLDrawable *drawablep) -{ - assertInitialized(); - if (mLights.find(drawablep) != mLights.end()) - { - llinfos << "In mLights" << llendl; - } - if (std::find(mMovedList.begin(), mMovedList.end(), drawablep) != mMovedList.end()) - { - llinfos << "In mMovedList" << llendl; - } - if (std::find(mShiftList.begin(), mShiftList.end(), drawablep) != mShiftList.end()) - { - llinfos << "In mShiftList" << llendl; - } - if (mRetexturedList.find(drawablep) != mRetexturedList.end()) - { - llinfos << "In mRetexturedList" << llendl; - } - - if (std::find(mBuildQ1.begin(), mBuildQ1.end(), drawablep) != mBuildQ1.end()) - { - llinfos << "In mBuildQ1" << llendl; - } - if (std::find(mBuildQ2.begin(), mBuildQ2.end(), drawablep) != mBuildQ2.end()) - { - llinfos << "In mBuildQ2" << llendl; - } - - S32 count; - - count = gObjectList.findReferences(drawablep); - if (count) - { - llinfos << "In other drawables: " << count << " references" << llendl; - } -} - -BOOL LLPipeline::verify() -{ - BOOL ok = assertInitialized(); - if (ok) - { - for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) - { - LLDrawPool *poolp = *iter; - if (!poolp->verify()) - { - ok = FALSE; - } - } - } - - if (!ok) - { - llwarns << "Pipeline verify failed!" << llendl; - } - return ok; -} - -////////////////////////////// -// -// Collision detection -// -// - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/** - * A method to compute a ray-AABB intersection. - * Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 - * Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) - * Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) - * - * Hence this version is faster as well as more robust than the original one. - * - * Should work provided: - * 1) the integer representation of 0.0f is 0x00000000 - * 2) the sign bit of the float is the most significant one - * - * Report bugs: p.terdiman@codercorner.com - * - * \param aabb [in] the axis-aligned bounding box - * \param origin [in] ray origin - * \param dir [in] ray direction - * \param coord [out] impact coordinates - * \return true if ray intersects AABB - */ -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -//#define RAYAABB_EPSILON 0.00001f -#define IR(x) ((U32&)x) - -bool LLRayAABB(const LLVector3 ¢er, const LLVector3 &size, const LLVector3& origin, const LLVector3& dir, LLVector3 &coord, F32 epsilon) -{ - BOOL Inside = TRUE; - LLVector3 MinB = center - size; - LLVector3 MaxB = center + size; - LLVector3 MaxT; - MaxT.mV[VX]=MaxT.mV[VY]=MaxT.mV[VZ]=-1.0f; - - // Find candidate planes. - for(U32 i=0;i<3;i++) - { - if(origin.mV[i] < MinB.mV[i]) - { - coord.mV[i] = MinB.mV[i]; - Inside = FALSE; - - // Calculate T distances to candidate planes - if(IR(dir.mV[i])) MaxT.mV[i] = (MinB.mV[i] - origin.mV[i]) / dir.mV[i]; - } - else if(origin.mV[i] > MaxB.mV[i]) - { - coord.mV[i] = MaxB.mV[i]; - Inside = FALSE; - - // Calculate T distances to candidate planes - if(IR(dir.mV[i])) MaxT.mV[i] = (MaxB.mV[i] - origin.mV[i]) / dir.mV[i]; - } - } - - // Ray origin inside bounding box - if(Inside) - { - coord = origin; - return true; - } - - // Get largest of the maxT's for final choice of intersection - U32 WhichPlane = 0; - if(MaxT.mV[1] > MaxT.mV[WhichPlane]) WhichPlane = 1; - if(MaxT.mV[2] > MaxT.mV[WhichPlane]) WhichPlane = 2; - - // Check final candidate actually inside box - if(IR(MaxT.mV[WhichPlane])&0x80000000) return false; - - for(U32 i=0;i<3;i++) - { - if(i!=WhichPlane) - { - coord.mV[i] = origin.mV[i] + MaxT.mV[WhichPlane] * dir.mV[i]; - if (epsilon > 0) - { - if(coord.mV[i] < MinB.mV[i] - epsilon || coord.mV[i] > MaxB.mV[i] + epsilon) return false; - } - else - { - if(coord.mV[i] < MinB.mV[i] || coord.mV[i] > MaxB.mV[i]) return false; - } - } - } - return true; // ray hits box -} - -////////////////////////////// -// -// Macros, functions, and inline methods from other classes -// -// - -void LLPipeline::setLight(LLDrawable *drawablep, BOOL is_light) -{ - if (drawablep && assertInitialized()) - { - if (is_light) - { - mLights.insert(drawablep); - drawablep->setState(LLDrawable::LIGHT); - } - else - { - drawablep->clearState(LLDrawable::LIGHT); - mLights.erase(drawablep); - } - } -} - -//static -void LLPipeline::toggleRenderType(U32 type) -{ - gPipeline.mRenderTypeEnabled[type] = !gPipeline.mRenderTypeEnabled[type]; - if (type == LLPipeline::RENDER_TYPE_WATER) - { - gPipeline.mRenderTypeEnabled[LLPipeline::RENDER_TYPE_VOIDWATER] = !gPipeline.mRenderTypeEnabled[LLPipeline::RENDER_TYPE_VOIDWATER]; - } -} - -//static -void LLPipeline::toggleRenderTypeControl(void* data) -{ - U32 type = (U32)(intptr_t)data; - U32 bit = (1<inBuildMode() ? FALSE : TRUE; - - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* region = *iter; - - for (U32 j = 0; j < LLViewerRegion::NUM_PARTITIONS; j++) - { - if ((j == LLViewerRegion::PARTITION_VOLUME) || - (j == LLViewerRegion::PARTITION_BRIDGE) || - (j == LLViewerRegion::PARTITION_TERRAIN) || - (j == LLViewerRegion::PARTITION_TREE) || - (j == LLViewerRegion::PARTITION_GRASS)) // only check these partitions for now - { - LLSpatialPartition* part = region->getSpatialPartition(j); - if (part && hasRenderType(part->mDrawableType)) - { - LLDrawable* hit = part->lineSegmentIntersect(start, local_end, pick_transparent, face_hit, &position, tex_coord, normal, bi_normal); - if (hit) - { - drawable = hit; - local_end = position; - } - } - } - } - } - - if (!sPickAvatar) - { - //save hit info in case we need to restore - //due to attachment override - LLVector3 local_normal; - LLVector3 local_binormal; - LLVector2 local_texcoord; - S32 local_face_hit = -1; - - if (face_hit) - { - local_face_hit = *face_hit; - } - if (tex_coord) - { - local_texcoord = *tex_coord; - } - if (bi_normal) - { - local_binormal = *bi_normal; - } - if (normal) - { - local_normal = *normal; - } - - const F32 ATTACHMENT_OVERRIDE_DIST = 0.1f; - - //check against avatars - sPickAvatar = TRUE; - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* region = *iter; - - LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_BRIDGE); - if (part && hasRenderType(part->mDrawableType)) - { - LLDrawable* hit = part->lineSegmentIntersect(start, local_end, pick_transparent, face_hit, &position, tex_coord, normal, bi_normal); - if (hit) - { - if (!drawable || - !drawable->getVObj()->isAttachment() || - (position-local_end).magVec() > ATTACHMENT_OVERRIDE_DIST) - { //avatar overrides if previously hit drawable is not an attachment or - //attachment is far enough away from detected intersection - drawable = hit; - local_end = position; - } - else - { //prioritize attachments over avatars - position = local_end; - - if (face_hit) - { - *face_hit = local_face_hit; - } - if (tex_coord) - { - *tex_coord = local_texcoord; - } - if (bi_normal) - { - *bi_normal = local_binormal; - } - if (normal) - { - *normal = local_normal; - } - } - } - } - } - } - - //check all avatar nametags (silly, isn't it?) - for (std::vector< LLCharacter* >::iterator iter = LLCharacter::sInstances.begin(); - iter != LLCharacter::sInstances.end(); - ++iter) - { - LLVOAvatar* av = (LLVOAvatar*) *iter; - if (av->mNameText.notNull() - && av->mNameText->lineSegmentIntersect(start, local_end, position)) - { - drawable = av->mDrawable; - local_end = position; - } - } - - if (intersection) - { - *intersection = position; - } - - return drawable ? drawable->getVObj().get() : NULL; -} - -LLViewerObject* LLPipeline::lineSegmentIntersectInHUD(const LLVector3& start, const LLVector3& end, - BOOL pick_transparent, - S32* face_hit, - LLVector3* intersection, // return the intersection point - LLVector2* tex_coord, // return the texture coordinates of the intersection point - LLVector3* normal, // return the surface normal at the intersection point - LLVector3* bi_normal // return the surface bi-normal at the intersection point - ) -{ - LLDrawable* drawable = NULL; - - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* region = *iter; - - BOOL toggle = FALSE; - if (!hasRenderType(LLPipeline::RENDER_TYPE_HUD)) - { - toggleRenderType(LLPipeline::RENDER_TYPE_HUD); - toggle = TRUE; - } - - LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_HUD); - if (part) - { - LLDrawable* hit = part->lineSegmentIntersect(start, end, pick_transparent, face_hit, intersection, tex_coord, normal, bi_normal); - if (hit) - { - drawable = hit; - } - } - - if (toggle) - { - toggleRenderType(LLPipeline::RENDER_TYPE_HUD); - } - } - return drawable ? drawable->getVObj().get() : NULL; -} - -LLSpatialPartition* LLPipeline::getSpatialPartition(LLViewerObject* vobj) -{ - if (vobj) - { - LLViewerRegion* region = vobj->getRegion(); - if (region) - { - return region->getSpatialPartition(vobj->getPartitionType()); - } - } - return NULL; -} - - -void LLPipeline::resetVertexBuffers(LLDrawable* drawable) -{ - if (!drawable || drawable->isDead()) - { - return; - } - - for (S32 i = 0; i < drawable->getNumFaces(); i++) - { - LLFace* facep = drawable->getFace(i); - facep->mVertexBuffer = NULL; - facep->mLastVertexBuffer = NULL; - } -} - -void LLPipeline::resetVertexBuffers() -{ - sRenderBump = gSavedSettings.getBOOL("RenderObjectBump"); - sUseTriStrips = gSavedSettings.getBOOL("RenderUseTriStrips"); - LLVertexBuffer::sUseStreamDraw = gSavedSettings.getBOOL("RenderUseStreamVBO"); - - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* region = *iter; - for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) - { - LLSpatialPartition* part = region->getSpatialPartition(i); - if (part) - { - part->resetVertexBuffers(); - } - } - } - - resetDrawOrders(); - - gSky.resetVertexBuffers(); - - if (LLVertexBuffer::sGLCount > 0) - { - LLVertexBuffer::cleanupClass(); - } - - //delete all name pool caches - LLGLNamePool::cleanupPools(); - - if (LLVertexBuffer::sGLCount > 0) - { - llwarns << "VBO wipe failed." << llendl; - } - - if (!LLVertexBuffer::sStreamIBOPool.mNameList.empty() || - !LLVertexBuffer::sStreamVBOPool.mNameList.empty() || - !LLVertexBuffer::sDynamicIBOPool.mNameList.empty() || - !LLVertexBuffer::sDynamicVBOPool.mNameList.empty()) - { - llwarns << "VBO name pool cleanup failed." << llendl; - } - - LLVertexBuffer::unbind(); - - LLPipeline::sTextureBindTest = gSavedSettings.getBOOL("RenderDebugTextureBind"); -} - -void LLPipeline::renderObjects(U32 type, U32 mask, BOOL texture) -{ - LLMemType mt_ro(LLMemType::MTYPE_PIPELINE_RENDER_OBJECTS); - assertInitialized(); - glLoadMatrixd(gGLModelView); - gGLLastMatrix = NULL; - mSimplePool->pushBatches(type, mask); - glLoadMatrixd(gGLModelView); - gGLLastMatrix = NULL; -} - -void LLPipeline::setUseVBO(BOOL use_vbo) -{ - if (use_vbo != LLVertexBuffer::sEnableVBOs) - { - if (use_vbo) - { - llinfos << "Enabling VBO." << llendl; - } - else - { - llinfos << "Disabling VBO." << llendl; - } - - resetVertexBuffers(); - LLVertexBuffer::initClass(use_vbo); - } -} - -void apply_cube_face_rotation(U32 face) -{ - switch (face) - { - case 0: - glRotatef(90.f, 0, 1, 0); - glRotatef(180.f, 1, 0, 0); - break; - case 2: - glRotatef(-90.f, 1, 0, 0); - break; - case 4: - glRotatef(180.f, 0, 1, 0); - glRotatef(180.f, 0, 0, 1); - break; - case 1: - glRotatef(-90.f, 0, 1, 0); - glRotatef(180.f, 1, 0, 0); - break; - case 3: - glRotatef(90, 1, 0, 0); - break; - case 5: - glRotatef(180, 0, 0, 1); - break; - } -} - -void validate_framebuffer_object() -{ - GLenum status; - status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - switch(status) - { - case GL_FRAMEBUFFER_COMPLETE_EXT: - //framebuffer OK, no error. - break; - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: - // frame buffer not OK: probably means unsupported depth buffer format - llerrs << "Framebuffer Incomplete Dimensions." << llendl; - break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: - // frame buffer not OK: probably means unsupported depth buffer format - llerrs << "Framebuffer Incomplete Attachment." << llendl; - break; - case GL_FRAMEBUFFER_UNSUPPORTED_EXT: - /* choose different formats */ - llerrs << "Framebuffer unsupported." << llendl; - break; - default: - llerrs << "Unknown framebuffer status." << llendl; - break; - } -} - -void LLPipeline::bindScreenToTexture() -{ - -} - -static LLFastTimer::DeclareTimer FTM_RENDER_BLOOM("Bloom"); - -void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) -{ - LLMemType mt_ru(LLMemType::MTYPE_PIPELINE_RENDER_BLOOM); - if (!(gPipeline.canUseVertexShaders() && - sRenderGlow)) - { - return; - } - - LLVertexBuffer::unbind(); - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - - assertInitialized(); - - if (gUseWireframe) - { - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - } - - U32 res_mod = gSavedSettings.getU32("RenderResolutionDivisor"); - - LLVector2 tc1(0,0); - LLVector2 tc2((F32) gViewerWindow->getWorldViewWidthRaw()*2, - (F32) gViewerWindow->getWorldViewHeightRaw()*2); - - if (res_mod > 1) - { - tc2 /= (F32) res_mod; - } - - gGL.setColorMask(true, true); - - LLFastTimer ftm(FTM_RENDER_BLOOM); - gGL.color4f(1,1,1,1); - LLGLDepthTest depth(GL_FALSE); - LLGLDisable blend(GL_BLEND); - LLGLDisable cull(GL_CULL_FACE); - - enableLightsFullbright(LLColor4(1,1,1,1)); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - LLGLDisable test(GL_ALPHA_TEST); - - gGL.setColorMask(true, true); - glClearColor(0,0,0,0); - - /*if (for_snapshot) - { - gGL.getTexUnit(0)->bind(&mGlow[1]); - { - //LLGLEnable stencil(GL_STENCIL_TEST); - //glStencilFunc(GL_NOTEQUAL, 255, 0xFFFFFFFF); - //glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - //LLGLDisable blend(GL_BLEND); - - // If the snapshot is constructed from tiles, calculate which - // tile we're in. - - //from LLViewerCamera::setPerpsective - if (zoom_factor > 1.f) - { - int pos_y = subfield / llceil(zoom_factor); - int pos_x = subfield - (pos_y*llceil(zoom_factor)); - F32 size = 1.f/zoom_factor; - - tc1.set(pos_x*size, pos_y*size); - tc2 = tc1 + LLVector2(size,size); - } - else - { - tc2.set(1,1); - } - - LLGLEnable blend(GL_BLEND); - gGL.setSceneBlendType(LLRender::BT_ADD); - - - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.color4f(1,1,1,1); - gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); - gGL.vertex2f(-1,-1); - - gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); - gGL.vertex2f(-1,1); - - gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); - gGL.vertex2f(1,-1); - - gGL.texCoord2f(tc2.mV[0], tc2.mV[1]); - gGL.vertex2f(1,1); - - gGL.end(); - - gGL.flush(); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - } - - gGL.flush(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - return; - }*/ - - { - { - LLFastTimer ftm(FTM_RENDER_BLOOM_FBO); - mGlow[2].bindTarget(); - mGlow[2].clear(); - } - - gGlowExtractProgram.bind(); - F32 minLum = llmax(gSavedSettings.getF32("RenderGlowMinLuminance"), 0.0f); - F32 maxAlpha = gSavedSettings.getF32("RenderGlowMaxExtractAlpha"); - F32 warmthAmount = gSavedSettings.getF32("RenderGlowWarmthAmount"); - LLVector3 lumWeights = gSavedSettings.getVector3("RenderGlowLumWeights"); - LLVector3 warmthWeights = gSavedSettings.getVector3("RenderGlowWarmthWeights"); - gGlowExtractProgram.uniform1f("minLuminance", minLum); - gGlowExtractProgram.uniform1f("maxExtractAlpha", maxAlpha); - gGlowExtractProgram.uniform3f("lumWeights", lumWeights.mV[0], lumWeights.mV[1], lumWeights.mV[2]); - gGlowExtractProgram.uniform3f("warmthWeights", warmthWeights.mV[0], warmthWeights.mV[1], warmthWeights.mV[2]); - gGlowExtractProgram.uniform1f("warmthAmount", warmthAmount); - LLGLEnable blend_on(GL_BLEND); - LLGLEnable test(GL_ALPHA_TEST); - gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); - gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.getTexUnit(0)->disable(); - gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE); - gGL.getTexUnit(0)->bind(&mScreen); - - gGL.color4f(1,1,1,1); - gPipeline.enableLightsFullbright(LLColor4(1,1,1,1)); - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); - gGL.vertex2f(-1,-1); - - gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); - gGL.vertex2f(-1,3); - - gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); - gGL.vertex2f(3,-1); - - gGL.end(); - - gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); - - mGlow[2].flush(); - } - - tc1.setVec(0,0); - tc2.setVec(2,2); - - // power of two between 1 and 1024 - U32 glowResPow = gSavedSettings.getS32("RenderGlowResolutionPow"); - const U32 glow_res = llmax(1, - llmin(1024, 1 << glowResPow)); - - S32 kernel = gSavedSettings.getS32("RenderGlowIterations")*2; - F32 delta = gSavedSettings.getF32("RenderGlowWidth") / glow_res; - // Use half the glow width if we have the res set to less than 9 so that it looks - // almost the same in either case. - if (glowResPow < 9) - { - delta *= 0.5f; - } - F32 strength = gSavedSettings.getF32("RenderGlowStrength"); - - gGlowProgram.bind(); - gGlowProgram.uniform1f("glowStrength", strength); - - for (S32 i = 0; i < kernel; i++) - { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - { - LLFastTimer ftm(FTM_RENDER_BLOOM_FBO); - mGlow[i%2].bindTarget(); - mGlow[i%2].clear(); - } - - if (i == 0) - { - gGL.getTexUnit(0)->bind(&mGlow[2]); - } - else - { - gGL.getTexUnit(0)->bind(&mGlow[(i-1)%2]); - } - - if (i%2 == 0) - { - gGlowProgram.uniform2f("glowDelta", delta, 0); - } - else - { - gGlowProgram.uniform2f("glowDelta", 0, delta); - } - - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); - gGL.vertex2f(-1,-1); - - gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); - gGL.vertex2f(-1,3); - - gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); - gGL.vertex2f(3,-1); - - gGL.end(); - - mGlow[i%2].flush(); - } - - gGlowProgram.unbind(); - - if (LLRenderTarget::sUseFBO) - { - LLFastTimer ftm(FTM_RENDER_BLOOM_FBO); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - } - - gGLViewport[0] = gViewerWindow->getWorldViewRectRaw().mLeft; - gGLViewport[1] = gViewerWindow->getWorldViewRectRaw().mBottom; - gGLViewport[2] = gViewerWindow->getWorldViewRectRaw().getWidth(); - gGLViewport[3] = gViewerWindow->getWorldViewRectRaw().getHeight(); - glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); - - tc2.setVec((F32) gViewerWindow->getWorldViewWidthRaw(), - (F32) gViewerWindow->getWorldViewHeightRaw()); - - gGL.flush(); - - LLVertexBuffer::unbind(); - - if (LLPipeline::sRenderDeferred && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 2) - { - LLGLDisable blend(GL_BLEND); - bindDeferredShader(gDeferredGIFinalProgram); - - S32 channel = gDeferredGIFinalProgram.enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE); - if (channel > -1) - { - mScreen.bindTexture(0, channel); - } - - gGL.begin(LLRender::TRIANGLE_STRIP); - gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); - gGL.vertex2f(-1,-1); - - gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); - gGL.vertex2f(-1,3); - - gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); - gGL.vertex2f(3,-1); - - gGL.end(); - - unbindDeferredShader(gDeferredGIFinalProgram); - } - else - { - - if (res_mod > 1) - { - tc2 /= (F32) res_mod; - } - - U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1; - LLPointer buff = new LLVertexBuffer(mask, 0); - buff->allocateBuffer(3,0,TRUE); - - LLStrider v; - LLStrider uv1; - LLStrider uv2; - - buff->getVertexStrider(v); - buff->getTexCoord0Strider(uv1); - buff->getTexCoord1Strider(uv2); - - uv1[0] = LLVector2(0, 0); - uv1[1] = LLVector2(0, 2); - uv1[2] = LLVector2(2, 0); - - uv2[0] = LLVector2(0, 0); - uv2[1] = LLVector2(0, tc2.mV[1]*2.f); - uv2[2] = LLVector2(tc2.mV[0]*2.f, 0); - - v[0] = LLVector3(-1,-1,0); - v[1] = LLVector3(-1,3,0); - v[2] = LLVector3(3,-1,0); - - buff->setBuffer(0); - - LLGLDisable blend(GL_BLEND); - - //tex unit 0 - gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_COLOR); - - gGL.getTexUnit(0)->bind(&mGlow[1]); - gGL.getTexUnit(1)->activate(); - gGL.getTexUnit(1)->enable(LLTexUnit::TT_RECT_TEXTURE); - - - //tex unit 1 - gGL.getTexUnit(1)->setTextureColorBlend(LLTexUnit::TBO_ADD, LLTexUnit::TBS_TEX_COLOR, LLTexUnit::TBS_PREV_COLOR); - - gGL.getTexUnit(1)->bind(&mScreen); - gGL.getTexUnit(1)->activate(); - - LLGLEnable multisample(GL_MULTISAMPLE_ARB); - - buff->setBuffer(mask); - buff->drawArrays(LLRender::TRIANGLE_STRIP, 0, 3); - - gGL.getTexUnit(1)->disable(); - gGL.getTexUnit(1)->setTextureBlendType(LLTexUnit::TB_MULT); - - gGL.getTexUnit(0)->activate(); - gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); - - if (LLRenderTarget::sUseFBO) - { //copy depth buffer from mScreen to framebuffer - LLRenderTarget::copyContentsToFramebuffer(mScreen, 0, 0, mScreen.getWidth(), mScreen.getHeight(), - 0, 0, mScreen.getWidth(), mScreen.getHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST); - } - } - - - gGL.setSceneBlendType(LLRender::BT_ALPHA); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - LLVertexBuffer::unbind(); - - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - -} - -static LLFastTimer::DeclareTimer FTM_BIND_DEFERRED("Bind Deferred"); - -void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, LLRenderTarget* gi_source, LLRenderTarget* last_gi_post, U32 noise_map) -{ - LLFastTimer t(FTM_BIND_DEFERRED); - - if (noise_map == 0xFFFFFFFF) - { - noise_map = mNoiseMap; - } - - LLGLState::checkTextureChannels(); - - shader.bind(); - S32 channel = 0; - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE); - if (channel > -1) - { - mDeferredScreen.bindTexture(0,channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_SPECULAR, LLTexUnit::TT_RECT_TEXTURE); - if (channel > -1) - { - mDeferredScreen.bindTexture(1, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL, LLTexUnit::TT_RECT_TEXTURE); - if (channel > -1) - { - mDeferredScreen.bindTexture(2, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - } - - if (gi_source) - { - BOOL has_gi = FALSE; - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_DIFFUSE); - if (channel > -1) - { - has_gi = TRUE; - gi_source->bindTexture(0, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_SPECULAR); - if (channel > -1) - { - has_gi = TRUE; - gi_source->bindTexture(1, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_NORMAL); - if (channel > -1) - { - has_gi = TRUE; - gi_source->bindTexture(2, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_MIN_POS); - if (channel > -1) - { - has_gi = TRUE; - gi_source->bindTexture(1, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_MAX_POS); - if (channel > -1) - { - has_gi = TRUE; - gi_source->bindTexture(3, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_DIFFUSE); - if (channel > -1) - { - has_gi = TRUE; - last_gi_post->bindTexture(0, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_NORMAL); - if (channel > -1) - { - has_gi = TRUE; - last_gi_post->bindTexture(2, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_MAX_POS); - if (channel > -1) - { - has_gi = TRUE; - last_gi_post->bindTexture(1, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_MIN_POS); - if (channel > -1) - { - has_gi = TRUE; - last_gi_post->bindTexture(3, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_DEPTH); - if (channel > -1) - { - has_gi = TRUE; - gGL.getTexUnit(channel)->bind(gi_source, TRUE); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - stop_glerror(); - - glTexParameteri(LLTexUnit::getInternalType(mGIMap.getUsage()), GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); - glTexParameteri(LLTexUnit::getInternalType(mGIMap.getUsage()), GL_DEPTH_TEXTURE_MODE_ARB, GL_ALPHA); - - stop_glerror(); - } - - if (has_gi) - { - F32 range_x = llmin(mGIRange.mV[0], 1.f); - F32 range_y = llmin(mGIRange.mV[1], 1.f); - - LLVector2 scale(range_x,range_y); - - LLVector2 kern[25]; - - for (S32 i = 0; i < 5; ++i) - { - for (S32 j = 0; j < 5; ++j) - { - S32 idx = i*5+j; - kern[idx].mV[0] = (i-2)*0.5f; - kern[idx].mV[1] = (j-2)*0.5f; - kern[idx].scaleVec(scale); - } - } - - shader.uniform2fv("gi_kern", 25, (F32*) kern); - shader.uniformMatrix4fv("gi_mat", 1, FALSE, mGIMatrix.m); - shader.uniformMatrix4fv("gi_mat_proj", 1, FALSE, mGIMatrixProj.m); - shader.uniformMatrix4fv("gi_inv_proj", 1, FALSE, mGIInvProj.m); - shader.uniformMatrix4fv("gi_norm_mat", 1, FALSE, mGINormalMatrix.m); - } - } - - /*channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_POSITION, LLTexUnit::TT_RECT_TEXTURE); - if (channel > -1) - { - mDeferredScreen.bindTexture(3, channel); - }*/ - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, LLTexUnit::TT_RECT_TEXTURE); - if (channel > -1) - { - gGL.getTexUnit(channel)->bind(&mDeferredDepth, TRUE); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - stop_glerror(); - - glTexParameteri(LLTexUnit::getInternalType(mDeferredDepth.getUsage()), GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); - glTexParameteri(LLTexUnit::getInternalType(mDeferredDepth.getUsage()), GL_DEPTH_TEXTURE_MODE_ARB, GL_ALPHA); - - stop_glerror(); - - glh::matrix4f projection = glh_get_current_projection(); - glh::matrix4f inv_proj = projection.inverse(); - - shader.uniformMatrix4fv("inv_proj", 1, FALSE, inv_proj.m); - shader.uniform4f("viewport", (F32) gGLViewport[0], - (F32) gGLViewport[1], - (F32) gGLViewport[2], - (F32) gGLViewport[3]); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_NOISE); - if (channel > -1) - { - gGL.getTexUnit(channel)->bindManual(LLTexUnit::TT_TEXTURE, noise_map); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_LIGHTFUNC); - if (channel > -1) - { - gGL.getTexUnit(channel)->bindManual(LLTexUnit::TT_TEXTURE, mLightFunc); - } - - stop_glerror(); - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_LIGHT, LLTexUnit::TT_RECT_TEXTURE); - if (channel > -1) - { - mDeferredLight[light_index].bindTexture(0, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_LUMINANCE); - if (channel > -1) - { - gGL.getTexUnit(channel)->bindManual(LLTexUnit::TT_TEXTURE, mLuminanceMap.getTexture(), true); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_TRILINEAR); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_BLOOM); - if (channel > -1) - { - mGlow[1].bindTexture(0, channel); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_LIGHT, LLTexUnit::TT_RECT_TEXTURE); - if (channel > -1) - { - gi_source->bindTexture(0, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_EDGE, LLTexUnit::TT_RECT_TEXTURE); - if (channel > -1) - { - mEdgeMap.bindTexture(0, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_SUN_LIGHT, LLTexUnit::TT_RECT_TEXTURE); - if (channel > -1) - { - mDeferredLight[1].bindTexture(0, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - } - - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_LOCAL_LIGHT, LLTexUnit::TT_RECT_TEXTURE); - if (channel > -1) - { - mDeferredLight[2].bindTexture(0, channel); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - } - - - stop_glerror(); - - for (U32 i = 0; i < 4; i++) - { - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_RECT_TEXTURE); - stop_glerror(); - if (channel > -1) - { - stop_glerror(); - gGL.getTexUnit(channel)->bind(&mShadow[i], TRUE); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); - stop_glerror(); - - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); - stop_glerror(); - } - } - - for (U32 i = 4; i < 6; i++) - { - channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_SHADOW0+i); - stop_glerror(); - if (channel > -1) - { - stop_glerror(); - gGL.getTexUnit(channel)->bind(&mShadow[i], TRUE); - gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); - gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); - stop_glerror(); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); - stop_glerror(); - } - } - - stop_glerror(); - - F32 mat[16*6]; - for (U32 i = 0; i < 16; i++) - { - mat[i] = mSunShadowMatrix[0].m[i]; - mat[i+16] = mSunShadowMatrix[1].m[i]; - mat[i+32] = mSunShadowMatrix[2].m[i]; - mat[i+48] = mSunShadowMatrix[3].m[i]; - mat[i+64] = mSunShadowMatrix[4].m[i]; - mat[i+80] = mSunShadowMatrix[5].m[i]; - } - - shader.uniformMatrix4fv("shadow_matrix[0]", 6, FALSE, mat); - shader.uniformMatrix4fv("shadow_matrix", 6, FALSE, mat); - - stop_glerror(); - - channel = shader.enableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP); - if (channel > -1) - { - LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL; - if (cube_map) - { - cube_map->enable(channel); - cube_map->bind(); - F64* m = gGLModelView; - - - F32 mat[] = { m[0], m[1], m[2], - m[4], m[5], m[6], - m[8], m[9], m[10] }; - - shader.uniform3fv("env_mat[0]", 3, mat); - shader.uniform3fv("env_mat", 3, mat); - } - } - - shader.uniform4fv("shadow_clip", 1, mSunClipPlanes.mV); - shader.uniform1f("sun_wash", gSavedSettings.getF32("RenderDeferredSunWash")); - shader.uniform1f("shadow_noise", gSavedSettings.getF32("RenderShadowNoise")); - shader.uniform1f("blur_size", gSavedSettings.getF32("RenderShadowBlurSize")); - - shader.uniform1f("ssao_radius", gSavedSettings.getF32("RenderSSAOScale")); - shader.uniform1f("ssao_max_radius", gSavedSettings.getU32("RenderSSAOMaxScale")); - - F32 ssao_factor = gSavedSettings.getF32("RenderSSAOFactor"); - shader.uniform1f("ssao_factor", ssao_factor); - shader.uniform1f("ssao_factor_inv", 1.0/ssao_factor); - - LLVector3 ssao_effect = gSavedSettings.getVector3("RenderSSAOEffect"); - F32 matrix_diag = (ssao_effect[0] + 2.0*ssao_effect[1])/3.0; - F32 matrix_nondiag = (ssao_effect[0] - ssao_effect[1])/3.0; - // This matrix scales (proj of color onto <1/rt(3),1/rt(3),1/rt(3)>) by - // value factor, and scales remainder by saturation factor - F32 ssao_effect_mat[] = { matrix_diag, matrix_nondiag, matrix_nondiag, - matrix_nondiag, matrix_diag, matrix_nondiag, - matrix_nondiag, matrix_nondiag, matrix_diag}; - shader.uniformMatrix3fv("ssao_effect_mat", 1, GL_FALSE, ssao_effect_mat); - - F32 shadow_offset_error = 1.f + gSavedSettings.getF32("RenderShadowOffsetError") * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2]); - F32 shadow_bias_error = 1.f + gSavedSettings.getF32("RenderShadowBiasError") * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2]); - - shader.uniform2f("screen_res", mDeferredScreen.getWidth(), mDeferredScreen.getHeight()); - shader.uniform1f("near_clip", LLViewerCamera::getInstance()->getNear()*2.f); - shader.uniform1f ("shadow_offset", gSavedSettings.getF32("RenderShadowOffset")*shadow_offset_error); - shader.uniform1f("shadow_bias", gSavedSettings.getF32("RenderShadowBias")*shadow_bias_error); - shader.uniform1f ("spot_shadow_offset", gSavedSettings.getF32("RenderSpotShadowOffset")); - shader.uniform1f("spot_shadow_bias", gSavedSettings.getF32("RenderSpotShadowBias")); - - shader.uniform1f("lum_scale", gSavedSettings.getF32("RenderLuminanceScale")); - shader.uniform1f("sun_lum_scale", gSavedSettings.getF32("RenderSunLuminanceScale")); - shader.uniform1f("sun_lum_offset", gSavedSettings.getF32("RenderSunLuminanceOffset")); - shader.uniform1f("lum_lod", gSavedSettings.getF32("RenderLuminanceDetail")); - shader.uniform1f("gi_range", gSavedSettings.getF32("RenderGIRange")); - shader.uniform1f("gi_brightness", gSavedSettings.getF32("RenderGIBrightness")); - shader.uniform1f("gi_luminance", gSavedSettings.getF32("RenderGILuminance")); - shader.uniform1f("gi_edge_weight", gSavedSettings.getF32("RenderGIBlurEdgeWeight")); - shader.uniform1f("gi_blur_brightness", gSavedSettings.getF32("RenderGIBlurBrightness")); - shader.uniform1f("gi_sample_width", mGILightRadius); - shader.uniform1f("gi_noise", gSavedSettings.getF32("RenderGINoise")); - shader.uniform1f("gi_attenuation", gSavedSettings.getF32("RenderGIAttenuation")); - shader.uniform1f("gi_ambiance", gSavedSettings.getF32("RenderGIAmbiance")); - shader.uniform2f("shadow_res", mShadow[0].getWidth(), mShadow[0].getHeight()); - shader.uniform2f("proj_shadow_res", mShadow[4].getWidth(), mShadow[4].getHeight()); - shader.uniform1f("depth_cutoff", gSavedSettings.getF32("RenderEdgeDepthCutoff")); - shader.uniform1f("norm_cutoff", gSavedSettings.getF32("RenderEdgeNormCutoff")); - - if (shader.getUniformLocation("norm_mat") >= 0) - { - glh::matrix4f norm_mat = glh_get_current_modelview().inverse().transpose(); - shader.uniformMatrix4fv("norm_mat", 1, FALSE, norm_mat.m); - } -} - -static LLFastTimer::DeclareTimer FTM_GI_TRACE("Trace"); -static LLFastTimer::DeclareTimer FTM_GI_GATHER("Gather"); -static LLFastTimer::DeclareTimer FTM_SUN_SHADOW("Shadow Map"); -static LLFastTimer::DeclareTimer FTM_SOFTEN_SHADOW("Shadow Soften"); -static LLFastTimer::DeclareTimer FTM_EDGE_DETECTION("Find Edges"); -static LLFastTimer::DeclareTimer FTM_LOCAL_LIGHTS("Local Lights"); -static LLFastTimer::DeclareTimer FTM_ATMOSPHERICS("Atmospherics"); -static LLFastTimer::DeclareTimer FTM_FULLSCREEN_LIGHTS("Fullscreen Lights"); -static LLFastTimer::DeclareTimer FTM_PROJECTORS("Projectors"); -static LLFastTimer::DeclareTimer FTM_POST("Post"); - - -void LLPipeline::renderDeferredLighting() -{ - if (!sCull) - { - return; - } - - { - LLFastTimer ftm(FTM_RENDER_DEFERRED); - - LLViewerCamera* camera = LLViewerCamera::getInstance(); - { - LLGLDepthTest depth(GL_TRUE); - mDeferredDepth.copyContents(mDeferredScreen, 0, 0, mDeferredScreen.getWidth(), mDeferredScreen.getHeight(), - 0, 0, mDeferredDepth.getWidth(), mDeferredDepth.getHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST); - } - - LLGLEnable multisample(GL_MULTISAMPLE_ARB); - - if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD)) - { - gPipeline.toggleRenderType(LLPipeline::RENDER_TYPE_HUD); - } - - //ati doesn't seem to love actually using the stencil buffer on FBO's - LLGLEnable stencil(GL_STENCIL_TEST); - glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - - gGL.setColorMask(true, true); - - //draw a cube around every light - LLVertexBuffer::unbind(); - - LLGLEnable cull(GL_CULL_FACE); - LLGLEnable blend(GL_BLEND); - - glh::matrix4f mat = glh_copy_matrix(gGLModelView); - - F32 vert[] = - { - -1,1, - -1,-3, - 3,1, - }; - glVertexPointer(2, GL_FLOAT, 0, vert); - glColor3f(1,1,1); - - { - setupHWLights(NULL); //to set mSunDir; - LLVector4 dir(mSunDir, 0.f); - glh::vec4f tc(dir.mV); - mat.mult_matrix_vec(tc); - glTexCoord4f(tc.v[0], tc.v[1], tc.v[2], 0); - } - - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - - mDeferredLight[0].bindTarget(); - - if (gSavedSettings.getBOOL("RenderDeferredSSAO") || gSavedSettings.getS32("RenderShadowDetail") > 0) - { - { //paint shadow/SSAO light map (direct lighting lightmap) - LLFastTimer ftm(FTM_SUN_SHADOW); - bindDeferredShader(gDeferredSunProgram, 0); - - glClearColor(1,1,1,1); - mDeferredLight[0].clear(GL_COLOR_BUFFER_BIT); - glClearColor(0,0,0,0); - - glh::matrix4f inv_trans = glh_get_current_modelview().inverse().transpose(); - - const U32 slice = 32; - F32 offset[slice*3]; - for (U32 i = 0; i < 4; i++) - { - for (U32 j = 0; j < 8; j++) - { - glh::vec3f v; - v.set_value(sinf(6.284f/8*j), cosf(6.284f/8*j), -(F32) i); - v.normalize(); - inv_trans.mult_matrix_vec(v); - v.normalize(); - offset[(i*8+j)*3+0] = v.v[0]; - offset[(i*8+j)*3+1] = v.v[2]; - offset[(i*8+j)*3+2] = v.v[1]; - } - } - - gDeferredSunProgram.uniform3fv("offset", slice, offset); - gDeferredSunProgram.uniform2f("screenRes", mDeferredLight[0].getWidth(), mDeferredLight[0].getHeight()); - - { - LLGLDisable blend(GL_BLEND); - LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); - stop_glerror(); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); - stop_glerror(); - } - - unbindDeferredShader(gDeferredSunProgram); - } - } - else - { - glClearColor(1,1,1,1); - mDeferredLight[0].clear(GL_COLOR_BUFFER_BIT); - glClearColor(0,0,0,0); - } - - mDeferredLight[0].flush(); - - { //global illumination specific block (still experimental) - if (gSavedSettings.getBOOL("RenderDeferredBlurLight") && - gSavedSettings.getBOOL("RenderDeferredGI")) - { - LLFastTimer ftm(FTM_EDGE_DETECTION); - //generate edge map - LLGLDisable blend(GL_BLEND); - LLGLDisable test(GL_ALPHA_TEST); - LLGLDepthTest depth(GL_FALSE); - LLGLDisable stencil(GL_STENCIL_TEST); - - { - gDeferredEdgeProgram.bind(); - mEdgeMap.bindTarget(); - bindDeferredShader(gDeferredEdgeProgram); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); - unbindDeferredShader(gDeferredEdgeProgram); - mEdgeMap.flush(); - } - } - - if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 2) - { - { //get luminance map from previous frame's light map - LLGLEnable blend(GL_BLEND); - LLGLDisable test(GL_ALPHA_TEST); - LLGLDepthTest depth(GL_FALSE); - LLGLDisable stencil(GL_STENCIL_TEST); - - //static F32 fade = 1.f; - - { - gGL.setSceneBlendType(LLRender::BT_ALPHA); - gLuminanceGatherProgram.bind(); - gLuminanceGatherProgram.uniform2f("screen_res", mDeferredLight[0].getWidth(), mDeferredLight[0].getHeight()); - mLuminanceMap.bindTarget(); - bindDeferredShader(gLuminanceGatherProgram); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); - unbindDeferredShader(gLuminanceGatherProgram); - mLuminanceMap.flush(); - gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mLuminanceMap.getTexture(), true); - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_TRILINEAR); - glGenerateMipmapEXT(GL_TEXTURE_2D); - } - } - - { //paint noisy GI map (bounce lighting lightmap) - LLFastTimer ftm(FTM_GI_TRACE); - LLGLDisable blend(GL_BLEND); - LLGLDepthTest depth(GL_FALSE); - LLGLDisable test(GL_ALPHA_TEST); - - mGIMapPost[0].bindTarget(); - - bindDeferredShader(gDeferredGIProgram, 0, &mGIMap, 0, mTrueNoiseMap); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); - unbindDeferredShader(gDeferredGIProgram); - mGIMapPost[0].flush(); - } - - U32 pass_count = 0; - if (gSavedSettings.getBOOL("RenderDeferredBlurLight")) - { - pass_count = llclamp(gSavedSettings.getU32("RenderGIBlurPasses"), (U32) 1, (U32) 128); - } - - for (U32 i = 0; i < pass_count; ++i) - { //gather/soften indirect lighting map - LLFastTimer ftm(FTM_GI_GATHER); - bindDeferredShader(gDeferredPostGIProgram, 0, &mGIMapPost[0], NULL, mTrueNoiseMap); - F32 blur_size = gSavedSettings.getF32("RenderGIBlurSize")/((F32) i * gSavedSettings.getF32("RenderGIBlurIncrement")+1.f); - gDeferredPostGIProgram.uniform2f("delta", 1.f, 0.f); - gDeferredPostGIProgram.uniform1f("kern_scale", blur_size); - gDeferredPostGIProgram.uniform1f("gi_blur_brightness", gSavedSettings.getF32("RenderGIBlurBrightness")); - - mGIMapPost[1].bindTarget(); - { - LLGLDisable blend(GL_BLEND); - LLGLDepthTest depth(GL_FALSE); - stop_glerror(); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); - stop_glerror(); - } - - mGIMapPost[1].flush(); - unbindDeferredShader(gDeferredPostGIProgram); - bindDeferredShader(gDeferredPostGIProgram, 0, &mGIMapPost[1], NULL, mTrueNoiseMap); - mGIMapPost[0].bindTarget(); - - gDeferredPostGIProgram.uniform2f("delta", 0.f, 1.f); - - { - LLGLDisable blend(GL_BLEND); - LLGLDepthTest depth(GL_FALSE); - stop_glerror(); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - stop_glerror(); - } - mGIMapPost[0].flush(); - unbindDeferredShader(gDeferredPostGIProgram); - } - } - } - - if (gSavedSettings.getBOOL("RenderDeferredSSAO")) - { //soften direct lighting lightmap - LLFastTimer ftm(FTM_SOFTEN_SHADOW); - //blur lightmap - mDeferredLight[1].bindTarget(); - - glClearColor(1,1,1,1); - mDeferredLight[1].clear(GL_COLOR_BUFFER_BIT); - glClearColor(0,0,0,0); - - bindDeferredShader(gDeferredBlurLightProgram); - - LLVector3 go = gSavedSettings.getVector3("RenderShadowGaussian"); - const U32 kern_length = 4; - F32 blur_size = gSavedSettings.getF32("RenderShadowBlurSize"); - F32 dist_factor = gSavedSettings.getF32("RenderShadowBlurDistFactor"); - - // sample symmetrically with the middle sample falling exactly on 0.0 - F32 x = 0.f; - - LLVector3 gauss[32]; // xweight, yweight, offset - - for (U32 i = 0; i < kern_length; i++) - { - gauss[i].mV[0] = llgaussian(x, go.mV[0]); - gauss[i].mV[1] = llgaussian(x, go.mV[1]); - gauss[i].mV[2] = x; - x += 1.f; - } - - gDeferredBlurLightProgram.uniform2f("delta", 1.f, 0.f); - gDeferredBlurLightProgram.uniform1f("dist_factor", dist_factor); - gDeferredBlurLightProgram.uniform3fv("kern[0]", kern_length, gauss[0].mV); - gDeferredBlurLightProgram.uniform3fv("kern", kern_length, gauss[0].mV); - gDeferredBlurLightProgram.uniform1f("kern_scale", blur_size * (kern_length/2.f - 0.5f)); - - { - LLGLDisable blend(GL_BLEND); - LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); - stop_glerror(); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); - stop_glerror(); - } - - mDeferredLight[1].flush(); - unbindDeferredShader(gDeferredBlurLightProgram); - - bindDeferredShader(gDeferredBlurLightProgram, 1); - mDeferredLight[0].bindTarget(); - - gDeferredBlurLightProgram.uniform2f("delta", 0.f, 1.f); - - { - LLGLDisable blend(GL_BLEND); - LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); - stop_glerror(); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); - stop_glerror(); - } - mDeferredLight[0].flush(); - unbindDeferredShader(gDeferredBlurLightProgram); - } - - stop_glerror(); - glPopMatrix(); - stop_glerror(); - glMatrixMode(GL_MODELVIEW); - stop_glerror(); - glPopMatrix(); - stop_glerror(); - - //copy depth and stencil from deferred screen - //mScreen.copyContents(mDeferredScreen, 0, 0, mDeferredScreen.getWidth(), mDeferredScreen.getHeight(), - // 0, 0, mScreen.getWidth(), mScreen.getHeight(), GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); - - if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 2) - { - mDeferredLight[1].bindTarget(); - // clear color buffer here (GI) - zeroing alpha (glow) is important or it will accumulate against sky - glClearColor(0,0,0,0); - mScreen.clear(GL_COLOR_BUFFER_BIT); - } - else - { - mScreen.bindTarget(); - // clear color buffer here - zeroing alpha (glow) is important or it will accumulate against sky - glClearColor(0,0,0,0); - mScreen.clear(GL_COLOR_BUFFER_BIT); - } - - if (gSavedSettings.getBOOL("RenderDeferredAtmospheric")) - { //apply sunlight contribution - LLFastTimer ftm(FTM_ATMOSPHERICS); - bindDeferredShader(gDeferredSoftenProgram, 0, &mGIMapPost[0]); - { - LLGLDepthTest depth(GL_FALSE); - LLGLDisable blend(GL_BLEND); - LLGLDisable test(GL_ALPHA_TEST); - - //full screen blit - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - - glVertexPointer(2, GL_FLOAT, 0, vert); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); - - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - } - - unbindDeferredShader(gDeferredSoftenProgram); - } - - { //render sky - LLGLDisable blend(GL_BLEND); - LLGLDisable stencil(GL_STENCIL_TEST); - gGL.setSceneBlendType(LLRender::BT_ALPHA); - - gPipeline.pushRenderTypeMask(); - - gPipeline.andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY, - LLPipeline::RENDER_TYPE_CLOUDS, - LLPipeline::RENDER_TYPE_WL_SKY, - LLPipeline::END_RENDER_TYPES); - - - renderGeomPostDeferred(*LLViewerCamera::getInstance()); - gPipeline.popRenderTypeMask(); - } - - BOOL render_local = gSavedSettings.getBOOL("RenderDeferredLocalLights"); - BOOL render_fullscreen = gSavedSettings.getBOOL("RenderDeferredFullscreenLights"); - - - if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 2) - { - mDeferredLight[1].flush(); - mDeferredLight[2].bindTarget(); - mDeferredLight[2].clear(GL_COLOR_BUFFER_BIT); - } - - if (render_local || render_fullscreen) - { - gGL.setSceneBlendType(LLRender::BT_ADD); - std::list fullscreen_lights; - LLDrawable::drawable_list_t spot_lights; - LLDrawable::drawable_list_t fullscreen_spot_lights; - - for (U32 i = 0; i < 2; i++) - { - mTargetShadowSpotLight[i] = NULL; - } - - std::list light_colors; - - F32 v[24]; - glVertexPointer(3, GL_FLOAT, 0, v); - BOOL render_local = gSavedSettings.getBOOL("RenderDeferredLocalLights"); - - { - bindDeferredShader(gDeferredLightProgram); - LLGLDepthTest depth(GL_TRUE, GL_FALSE); - for (LLDrawable::drawable_set_t::iterator iter = mLights.begin(); iter != mLights.end(); ++iter) - { - LLDrawable* drawablep = *iter; - - LLVOVolume* volume = drawablep->getVOVolume(); - if (!volume) - { - continue; - } - - if (volume->isAttachment()) - { - if (!sRenderAttachedLights) - { - continue; - } - } - - - LLVector3 center = drawablep->getPositionAgent(); - F32* c = center.mV; - F32 s = volume->getLightRadius()*1.5f; - - LLColor3 col = volume->getLightColor(); - col *= volume->getLightIntensity(); - - if (col.magVecSquared() < 0.001f) - { - continue; - } - - if (s <= 0.001f) - { - continue; - } - - if (camera->AABBInFrustumNoFarClip(center, LLVector3(s,s,s)) == 0) - { - continue; - } - - sVisibleLightCount++; - - glh::vec3f tc(c); - mat.mult_matrix_vec(tc); - - //vertex positions are encoded so the 3 bits of their vertex index - //correspond to their axis facing, with bit position 3,2,1 matching - //axis facing x,y,z, bit set meaning positive facing, bit clear - //meaning negative facing - v[0] = c[0]-s; v[1] = c[1]-s; v[2] = c[2]-s; // 0 - 0000 - v[3] = c[0]-s; v[4] = c[1]-s; v[5] = c[2]+s; // 1 - 0001 - v[6] = c[0]-s; v[7] = c[1]+s; v[8] = c[2]-s; // 2 - 0010 - v[9] = c[0]-s; v[10] = c[1]+s; v[11] = c[2]+s; // 3 - 0011 - - v[12] = c[0]+s; v[13] = c[1]-s; v[14] = c[2]-s; // 4 - 0100 - v[15] = c[0]+s; v[16] = c[1]-s; v[17] = c[2]+s; // 5 - 0101 - v[18] = c[0]+s; v[19] = c[1]+s; v[20] = c[2]-s; // 6 - 0110 - v[21] = c[0]+s; v[22] = c[1]+s; v[23] = c[2]+s; // 7 - 0111 - - if (camera->getOrigin().mV[0] > c[0] + s + 0.2f || - camera->getOrigin().mV[0] < c[0] - s - 0.2f || - camera->getOrigin().mV[1] > c[1] + s + 0.2f || - camera->getOrigin().mV[1] < c[1] - s - 0.2f || - camera->getOrigin().mV[2] > c[2] + s + 0.2f || - camera->getOrigin().mV[2] < c[2] - s - 0.2f) - { //draw box if camera is outside box - if (render_local) - { - if (volume->isLightSpotlight()) - { - drawablep->getVOVolume()->updateSpotLightPriority(); - spot_lights.push_back(drawablep); - continue; - } - - LLFastTimer ftm(FTM_LOCAL_LIGHTS); - glTexCoord4f(tc.v[0], tc.v[1], tc.v[2], s*s); - glColor4f(col.mV[0], col.mV[1], col.mV[2], volume->getLightFalloff()*0.5f); - glDrawRangeElements(GL_TRIANGLE_FAN, 0, 7, 8, - GL_UNSIGNED_BYTE, get_box_fan_indices(camera, center)); - stop_glerror(); - } - } - else if (render_fullscreen) - { - if (volume->isLightSpotlight()) - { - drawablep->getVOVolume()->updateSpotLightPriority(); - fullscreen_spot_lights.push_back(drawablep); - continue; - } - - fullscreen_lights.push_back(LLVector4(tc.v[0], tc.v[1], tc.v[2], s*s)); - light_colors.push_back(LLVector4(col.mV[0], col.mV[1], col.mV[2], volume->getLightFalloff()*0.5f)); - } - } - unbindDeferredShader(gDeferredLightProgram); - } - - if (!spot_lights.empty()) - { - LLGLDepthTest depth(GL_TRUE, GL_FALSE); - bindDeferredShader(gDeferredSpotLightProgram); - - gDeferredSpotLightProgram.enableTexture(LLViewerShaderMgr::DEFERRED_PROJECTION); - - for (LLDrawable::drawable_list_t::iterator iter = spot_lights.begin(); iter != spot_lights.end(); ++iter) - { - LLFastTimer ftm(FTM_PROJECTORS); - LLDrawable* drawablep = *iter; - - LLVOVolume* volume = drawablep->getVOVolume(); - - LLVector3 center = drawablep->getPositionAgent(); - F32* c = center.mV; - F32 s = volume->getLightRadius()*1.5f; - - sVisibleLightCount++; - - glh::vec3f tc(c); - mat.mult_matrix_vec(tc); - - setupSpotLight(gDeferredSpotLightProgram, drawablep); - - LLColor3 col = volume->getLightColor(); - col *= volume->getLightIntensity(); - - //vertex positions are encoded so the 3 bits of their vertex index - //correspond to their axis facing, with bit position 3,2,1 matching - //axis facing x,y,z, bit set meaning positive facing, bit clear - //meaning negative facing - v[0] = c[0]-s; v[1] = c[1]-s; v[2] = c[2]-s; // 0 - 0000 - v[3] = c[0]-s; v[4] = c[1]-s; v[5] = c[2]+s; // 1 - 0001 - v[6] = c[0]-s; v[7] = c[1]+s; v[8] = c[2]-s; // 2 - 0010 - v[9] = c[0]-s; v[10] = c[1]+s; v[11] = c[2]+s; // 3 - 0011 - - v[12] = c[0]+s; v[13] = c[1]-s; v[14] = c[2]-s; // 4 - 0100 - v[15] = c[0]+s; v[16] = c[1]-s; v[17] = c[2]+s; // 5 - 0101 - v[18] = c[0]+s; v[19] = c[1]+s; v[20] = c[2]-s; // 6 - 0110 - v[21] = c[0]+s; v[22] = c[1]+s; v[23] = c[2]+s; // 7 - 0111 - - glTexCoord4f(tc.v[0], tc.v[1], tc.v[2], s*s); - glColor4f(col.mV[0], col.mV[1], col.mV[2], volume->getLightFalloff()*0.5f); - glDrawRangeElements(GL_TRIANGLE_FAN, 0, 7, 8, - GL_UNSIGNED_BYTE, get_box_fan_indices(camera, center)); - } - gDeferredSpotLightProgram.disableTexture(LLViewerShaderMgr::DEFERRED_PROJECTION); - unbindDeferredShader(gDeferredSpotLightProgram); - } - - { - bindDeferredShader(gDeferredMultiLightProgram); - - LLGLDepthTest depth(GL_FALSE); - - //full screen blit - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - - U32 count = 0; - - const U32 max_count = 8; - LLVector4 light[max_count]; - LLVector4 col[max_count]; - - glVertexPointer(2, GL_FLOAT, 0, vert); - - F32 far_z = 0.f; - - while (!fullscreen_lights.empty()) - { - LLFastTimer ftm(FTM_FULLSCREEN_LIGHTS); - light[count] = fullscreen_lights.front(); - fullscreen_lights.pop_front(); - col[count] = light_colors.front(); - light_colors.pop_front(); - - far_z = llmin(light[count].mV[2]-sqrtf(light[count].mV[3]), far_z); - - count++; - if (count == max_count || fullscreen_lights.empty()) - { - gDeferredMultiLightProgram.uniform1i("light_count", count); - gDeferredMultiLightProgram.uniform4fv("light[0]", count, (GLfloat*) light); - gDeferredMultiLightProgram.uniform4fv("light", count, (GLfloat*) light); - gDeferredMultiLightProgram.uniform4fv("light_col[0]", count, (GLfloat*) col); - gDeferredMultiLightProgram.uniform4fv("light_col", count, (GLfloat*) col); - gDeferredMultiLightProgram.uniform1f("far_z", far_z); - far_z = 0.f; - count = 0; - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); - } - } - - unbindDeferredShader(gDeferredMultiLightProgram); - - bindDeferredShader(gDeferredMultiSpotLightProgram); - - gDeferredMultiSpotLightProgram.enableTexture(LLViewerShaderMgr::DEFERRED_PROJECTION); - - for (LLDrawable::drawable_list_t::iterator iter = fullscreen_spot_lights.begin(); iter != fullscreen_spot_lights.end(); ++iter) - { - LLFastTimer ftm(FTM_PROJECTORS); - LLDrawable* drawablep = *iter; - - LLVOVolume* volume = drawablep->getVOVolume(); - - LLVector3 center = drawablep->getPositionAgent(); - F32* c = center.mV; - F32 s = volume->getLightRadius()*1.5f; - - sVisibleLightCount++; - - glh::vec3f tc(c); - mat.mult_matrix_vec(tc); - - setupSpotLight(gDeferredMultiSpotLightProgram, drawablep); - - LLColor3 col = volume->getLightColor(); - col *= volume->getLightIntensity(); - - glTexCoord4f(tc.v[0], tc.v[1], tc.v[2], s*s); - glColor4f(col.mV[0], col.mV[1], col.mV[2], volume->getLightFalloff()*0.5f); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); - } - - gDeferredMultiSpotLightProgram.disableTexture(LLViewerShaderMgr::DEFERRED_PROJECTION); - unbindDeferredShader(gDeferredMultiSpotLightProgram); - - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - } - } - - gGL.setColorMask(true, true); - - if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 2) - { - mDeferredLight[2].flush(); - - mScreen.bindTarget(); - mScreen.clear(GL_COLOR_BUFFER_BIT); - - gGL.setSceneBlendType(LLRender::BT_ALPHA); - - { //mix various light maps (local, sun, gi) - LLFastTimer ftm(FTM_POST); - LLGLDisable blend(GL_BLEND); - LLGLDisable test(GL_ALPHA_TEST); - LLGLDepthTest depth(GL_FALSE); - LLGLDisable stencil(GL_STENCIL_TEST); - - bindDeferredShader(gDeferredPostProgram, 0, &mGIMapPost[0]); - - gDeferredPostProgram.bind(); - - LLVertexBuffer::unbind(); - - glVertexPointer(2, GL_FLOAT, 0, vert); - glColor3f(1,1,1); - - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - - glDrawArrays(GL_TRIANGLES, 0, 3); - - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - unbindDeferredShader(gDeferredPostProgram); - } - } - } - - { //render non-deferred geometry (alpha, fullbright, glow) - LLGLDisable blend(GL_BLEND); - LLGLDisable stencil(GL_STENCIL_TEST); - - pushRenderTypeMask(); - andRenderTypeMask(LLPipeline::RENDER_TYPE_ALPHA, - LLPipeline::RENDER_TYPE_FULLBRIGHT, - LLPipeline::RENDER_TYPE_VOLUME, - LLPipeline::RENDER_TYPE_GLOW, - LLPipeline::RENDER_TYPE_BUMP, - LLPipeline::RENDER_TYPE_PASS_SIMPLE, - LLPipeline::RENDER_TYPE_PASS_ALPHA, - LLPipeline::RENDER_TYPE_PASS_ALPHA_MASK, - LLPipeline::RENDER_TYPE_PASS_BUMP, - LLPipeline::RENDER_TYPE_PASS_POST_BUMP, - LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT, - LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_ALPHA_MASK, - LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_SHINY, - LLPipeline::RENDER_TYPE_PASS_GLOW, - LLPipeline::RENDER_TYPE_PASS_GRASS, - LLPipeline::RENDER_TYPE_PASS_SHINY, - LLPipeline::RENDER_TYPE_PASS_INVISIBLE, - LLPipeline::RENDER_TYPE_PASS_INVISI_SHINY, - LLPipeline::RENDER_TYPE_AVATAR, - END_RENDER_TYPES); - - renderGeomPostDeferred(*LLViewerCamera::getInstance()); - popRenderTypeMask(); - } - - { - //render highlights, etc. - renderHighlights(); - mHighlightFaces.clear(); - - renderDebug(); - - LLVertexBuffer::unbind(); - - if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) - { - // Render debugging beacons. - gObjectList.renderObjectBeacons(); - gObjectList.resetObjectBeacons(); - } - } - - mScreen.flush(); - -} - -void LLPipeline::setupSpotLight(LLGLSLShader& shader, LLDrawable* drawablep) -{ - //construct frustum - LLVOVolume* volume = drawablep->getVOVolume(); - LLVector3 params = volume->getSpotLightParams(); - - F32 fov = params.mV[0]; - F32 focus = params.mV[1]; - - LLVector3 pos = drawablep->getPositionAgent(); - LLQuaternion quat = volume->getRenderRotation(); - LLVector3 scale = volume->getScale(); - - //get near clip plane - LLVector3 at_axis(0,0,-scale.mV[2]*0.5f); - at_axis *= quat; - - LLVector3 np = pos+at_axis; - at_axis.normVec(); - - //get origin that has given fov for plane np, at_axis, and given scale - F32 dist = (scale.mV[1]*0.5f)/tanf(fov*0.5f); - - LLVector3 origin = np - at_axis*dist; - - //matrix from volume space to agent space - LLMatrix4 light_mat(quat, LLVector4(origin,1.f)); - - glh::matrix4f light_to_agent((F32*) light_mat.mMatrix); - glh::matrix4f light_to_screen = glh_get_current_modelview() * light_to_agent; - - glh::matrix4f screen_to_light = light_to_screen.inverse(); - - F32 s = volume->getLightRadius()*1.5f; - F32 near_clip = dist; - F32 width = scale.mV[VX]; - F32 height = scale.mV[VY]; - F32 far_clip = s+dist-scale.mV[VZ]; - - F32 fovy = fov * RAD_TO_DEG; - F32 aspect = width/height; - - glh::matrix4f trans(0.5f, 0.f, 0.f, 0.5f, - 0.f, 0.5f, 0.f, 0.5f, - 0.f, 0.f, 0.5f, 0.5f, - 0.f, 0.f, 0.f, 1.f); - - glh::vec3f p1(0, 0, -(near_clip+0.01f)); - glh::vec3f p2(0, 0, -(near_clip+1.f)); - - glh::vec3f screen_origin(0, 0, 0); - - light_to_screen.mult_matrix_vec(p1); - light_to_screen.mult_matrix_vec(p2); - light_to_screen.mult_matrix_vec(screen_origin); - - glh::vec3f n = p2-p1; - n.normalize(); - - F32 proj_range = far_clip - near_clip; - glh::matrix4f light_proj = gl_perspective(fovy, aspect, near_clip, far_clip); - screen_to_light = trans * light_proj * screen_to_light; - shader.uniformMatrix4fv("proj_mat", 1, FALSE, screen_to_light.m); - shader.uniform1f("proj_near", near_clip); - shader.uniform3fv("proj_p", 1, p1.v); - shader.uniform3fv("proj_n", 1, n.v); - shader.uniform3fv("proj_origin", 1, screen_origin.v); - shader.uniform1f("proj_range", proj_range); - shader.uniform1f("proj_ambiance", params.mV[2]); - S32 s_idx = -1; - - for (U32 i = 0; i < 2; i++) - { - if (mShadowSpotLight[i] == drawablep) - { - s_idx = i; - } - } - - shader.uniform1i("proj_shadow_idx", s_idx); - - if (s_idx >= 0) - { - shader.uniform1f("shadow_fade", 1.f-mSpotLightFade[s_idx]); - } - else - { - shader.uniform1f("shadow_fade", 1.f); - } - - { - LLDrawable* potential = drawablep; - //determine if this is a good light for casting shadows - F32 m_pri = volume->getSpotLightPriority(); - - for (U32 i = 0; i < 2; i++) - { - F32 pri = 0.f; - - if (mTargetShadowSpotLight[i].notNull()) - { - pri = mTargetShadowSpotLight[i]->getVOVolume()->getSpotLightPriority(); - } - - if (m_pri > pri) - { - LLDrawable* temp = mTargetShadowSpotLight[i]; - mTargetShadowSpotLight[i] = potential; - potential = temp; - m_pri = pri; - } - } - } - - LLViewerTexture* img = volume->getLightTexture(); - - S32 channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_PROJECTION); - - if (channel > -1 && img) - { - gGL.getTexUnit(channel)->bind(img); - - F32 lod_range = logf(img->getWidth())/logf(2.f); - - shader.uniform1f("proj_focus", focus); - shader.uniform1f("proj_lod", lod_range); - shader.uniform1f("proj_ambient_lod", llclamp((proj_range-focus)/proj_range*lod_range, 0.f, 1.f)); - } -} - -void LLPipeline::unbindDeferredShader(LLGLSLShader &shader) -{ - stop_glerror(); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_POSITION, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_NORMAL, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_SPECULAR, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_LIGHT, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_LIGHT, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_EDGE, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_SUN_LIGHT, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_LOCAL_LIGHT, LLTexUnit::TT_RECT_TEXTURE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_LUMINANCE); - shader.disableTexture(LLViewerShaderMgr::DIFFUSE_MAP); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_MIP); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_BLOOM); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_NORMAL); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_DIFFUSE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_SPECULAR); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_DEPTH); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_MIN_POS); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_MAX_POS); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_NORMAL); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_DIFFUSE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_MIN_POS); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_MAX_POS); - - for (U32 i = 0; i < 4; i++) - { - if (shader.disableTexture(LLViewerShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_RECT_TEXTURE) > -1) - { - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); - } - } - - for (U32 i = 4; i < 6; i++) - { - if (shader.disableTexture(LLViewerShaderMgr::DEFERRED_SHADOW0+i) > -1) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); - } - } - - shader.disableTexture(LLViewerShaderMgr::DEFERRED_NOISE); - shader.disableTexture(LLViewerShaderMgr::DEFERRED_LIGHTFUNC); - - S32 channel = shader.disableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP); - if (channel > -1) - { - LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL; - if (cube_map) - { - cube_map->disable(); - } - } - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.getTexUnit(0)->activate(); - shader.unbind(); - - LLGLState::checkTextureChannels(); -} - -inline float sgn(float a) -{ - if (a > 0.0F) return (1.0F); - if (a < 0.0F) return (-1.0F); - return (0.0F); -} - -void LLPipeline::generateWaterReflection(LLCamera& camera_in) -{ - if (LLPipeline::sWaterReflections && assertInitialized() && LLDrawPoolWater::sNeedsReflectionUpdate) - { - BOOL skip_avatar_update = FALSE; - if (!isAgentAvatarValid() || gAgentCamera.getCameraAnimating() || gAgentCamera.getCameraMode() != CAMERA_MODE_MOUSELOOK) - { - skip_avatar_update = TRUE; - } - - if (!skip_avatar_update) - { - gAgentAvatarp->updateAttachmentVisibility(CAMERA_MODE_THIRD_PERSON); - } - LLVertexBuffer::unbind(); - - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); - - LLCamera camera = camera_in; - camera.setFar(camera.getFar()*0.87654321f); - LLPipeline::sReflectionRender = TRUE; - S32 occlusion = LLPipeline::sUseOcclusion; - - LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD; - - LLPipeline::sUseOcclusion = llmin(occlusion, 1); - - gPipeline.pushRenderTypeMask(); - - glh::matrix4f projection = glh_get_current_projection(); - glh::matrix4f mat; - - stop_glerror(); - LLPlane plane; - - F32 height = gAgent.getRegion()->getWaterHeight(); - F32 to_clip = fabsf(camera.getOrigin().mV[2]-height); - F32 pad = -to_clip*0.05f; //amount to "pad" clip plane by - - //plane params - LLVector3 pnorm; - F32 pd; - - S32 water_clip = 0; - if (!LLViewerCamera::getInstance()->cameraUnderWater()) - { //camera is above water, clip plane points up - pnorm.setVec(0,0,1); - pd = -height; - plane.setVec(pnorm, pd); - water_clip = -1; - } - else - { //camera is below water, clip plane points down - pnorm = LLVector3(0,0,-1); - pd = height; - plane.setVec(pnorm, pd); - water_clip = 1; - } - - if (!LLViewerCamera::getInstance()->cameraUnderWater()) - { //generate planar reflection map - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - glClearColor(0,0,0,0); - mWaterRef.bindTarget(); - gGL.setColorMask(true, true); - mWaterRef.clear(); - gGL.setColorMask(true, false); - - mWaterRef.getViewport(gGLViewport); - - stop_glerror(); - - glPushMatrix(); - - mat.set_scale(glh::vec3f(1,1,-1)); - mat.set_translate(glh::vec3f(0,0,height*2.f)); - - glh::matrix4f current = glh_get_current_modelview(); - - mat = current * mat; - - glh_set_current_modelview(mat); - glLoadMatrixf(mat.m); - - LLViewerCamera::updateFrustumPlanes(camera, FALSE, TRUE); - - glh::matrix4f inv_mat = mat.inverse(); - - glh::vec3f origin(0,0,0); - inv_mat.mult_matrix_vec(origin); - - camera.setOrigin(origin.v); - - glCullFace(GL_FRONT); - - static LLCullResult ref_result; - - if (LLDrawPoolWater::sNeedsDistortionUpdate) - { - //initial sky pass (no user clip plane) - { //mask out everything but the sky - gPipeline.pushRenderTypeMask(); - gPipeline.andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY, - LLPipeline::RENDER_TYPE_WL_SKY, - LLPipeline::END_RENDER_TYPES); - static LLCullResult result; - updateCull(camera, result); - stateSort(camera, result); - andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY, - LLPipeline::RENDER_TYPE_CLOUDS, - LLPipeline::RENDER_TYPE_WL_SKY, - LLPipeline::END_RENDER_TYPES); - - renderGeom(camera, TRUE); - gPipeline.popRenderTypeMask(); - } - - gPipeline.pushRenderTypeMask(); - - clearRenderTypeMask(LLPipeline::RENDER_TYPE_WATER, - LLPipeline::RENDER_TYPE_VOIDWATER, - LLPipeline::RENDER_TYPE_GROUND, - LLPipeline::RENDER_TYPE_SKY, - LLPipeline::RENDER_TYPE_CLOUDS, - LLPipeline::END_RENDER_TYPES); - - S32 detail = gSavedSettings.getS32("RenderReflectionDetail"); - if (detail > 0) - { //mask out selected geometry based on reflection detail - if (detail < 4) - { - clearRenderTypeMask(LLPipeline::RENDER_TYPE_PARTICLES, END_RENDER_TYPES); - if (detail < 3) - { - clearRenderTypeMask(LLPipeline::RENDER_TYPE_AVATAR, END_RENDER_TYPES); - if (detail < 2) - { - clearRenderTypeMask(LLPipeline::RENDER_TYPE_VOLUME, END_RENDER_TYPES); - } - } - } - - LLGLUserClipPlane clip_plane(plane, mat, projection); - LLGLDisable cull(GL_CULL_FACE); - updateCull(camera, ref_result, 1); - stateSort(camera, ref_result); - } - - if (LLDrawPoolWater::sNeedsDistortionUpdate) - { - if (gSavedSettings.getS32("RenderReflectionDetail") > 0) - { - gPipeline.grabReferences(ref_result); - LLGLUserClipPlane clip_plane(plane, mat, projection); - renderGeom(camera); - } - } - - gPipeline.popRenderTypeMask(); - } - glCullFace(GL_BACK); - glPopMatrix(); - mWaterRef.flush(); - glh_set_current_modelview(current); - } - - camera.setOrigin(camera_in.getOrigin()); - //render distortion map - static BOOL last_update = TRUE; - if (last_update) - { - camera.setFar(camera_in.getFar()); - clearRenderTypeMask(LLPipeline::RENDER_TYPE_WATER, - LLPipeline::RENDER_TYPE_VOIDWATER, - LLPipeline::RENDER_TYPE_GROUND, - END_RENDER_TYPES); - stop_glerror(); - - LLPipeline::sUnderWaterRender = LLViewerCamera::getInstance()->cameraUnderWater() ? FALSE : TRUE; - - if (LLPipeline::sUnderWaterRender) - { - clearRenderTypeMask(LLPipeline::RENDER_TYPE_GROUND, - LLPipeline::RENDER_TYPE_SKY, - LLPipeline::RENDER_TYPE_CLOUDS, - LLPipeline::RENDER_TYPE_WL_SKY, - END_RENDER_TYPES); - } - LLViewerCamera::updateFrustumPlanes(camera); - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - LLColor4& col = LLDrawPoolWater::sWaterFogColor; - glClearColor(col.mV[0], col.mV[1], col.mV[2], 0.f); - mWaterDis.bindTarget(); - mWaterDis.getViewport(gGLViewport); - - if (!LLPipeline::sUnderWaterRender || LLDrawPoolWater::sNeedsReflectionUpdate) - { - //clip out geometry on the same side of water as the camera - mat = glh_get_current_modelview(); - LLGLUserClipPlane clip_plane(LLPlane(-pnorm, -(pd+pad)), mat, projection); - static LLCullResult result; - updateCull(camera, result, water_clip); - stateSort(camera, result); - - gGL.setColorMask(true, true); - mWaterDis.clear(); - gGL.setColorMask(true, false); - - renderGeom(camera); - } - - LLPipeline::sUnderWaterRender = FALSE; - mWaterDis.flush(); - } - last_update = LLDrawPoolWater::sNeedsReflectionUpdate && LLDrawPoolWater::sNeedsDistortionUpdate; - - LLRenderTarget::unbindTarget(); - - LLPipeline::sReflectionRender = FALSE; - - if (!LLRenderTarget::sUseFBO) - { - glClear(GL_DEPTH_BUFFER_BIT); - } - glClearColor(0.f, 0.f, 0.f, 0.f); - gViewerWindow->setup3DViewport(); - gPipeline.popRenderTypeMask(); - LLDrawPoolWater::sNeedsReflectionUpdate = FALSE; - LLDrawPoolWater::sNeedsDistortionUpdate = FALSE; - LLViewerCamera::getInstance()->setUserClipPlane(LLPlane(-pnorm, -pd)); - LLPipeline::sUseOcclusion = occlusion; - - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); - - if (!skip_avatar_update) - { - gAgentAvatarp->updateAttachmentVisibility(gAgentCamera.getCameraMode()); - } - } -} - -glh::matrix4f look(const LLVector3 pos, const LLVector3 dir, const LLVector3 up) -{ - glh::matrix4f ret; - - LLVector3 dirN; - LLVector3 upN; - LLVector3 lftN; - - lftN = dir % up; - lftN.normVec(); - - upN = lftN % dir; - upN.normVec(); - - dirN = dir; - dirN.normVec(); - - ret.m[ 0] = lftN[0]; - ret.m[ 1] = upN[0]; - ret.m[ 2] = -dirN[0]; - ret.m[ 3] = 0.f; - - ret.m[ 4] = lftN[1]; - ret.m[ 5] = upN[1]; - ret.m[ 6] = -dirN[1]; - ret.m[ 7] = 0.f; - - ret.m[ 8] = lftN[2]; - ret.m[ 9] = upN[2]; - ret.m[10] = -dirN[2]; - ret.m[11] = 0.f; - - ret.m[12] = -(lftN*pos); - ret.m[13] = -(upN*pos); - ret.m[14] = dirN*pos; - ret.m[15] = 1.f; - - return ret; -} - -glh::matrix4f scale_translate_to_fit(const LLVector3 min, const LLVector3 max) -{ - glh::matrix4f ret; - ret.m[ 0] = 2/(max[0]-min[0]); - ret.m[ 4] = 0; - ret.m[ 8] = 0; - ret.m[12] = -(max[0]+min[0])/(max[0]-min[0]); - - ret.m[ 1] = 0; - ret.m[ 5] = 2/(max[1]-min[1]); - ret.m[ 9] = 0; - ret.m[13] = -(max[1]+min[1])/(max[1]-min[1]); - - ret.m[ 2] = 0; - ret.m[ 6] = 0; - ret.m[10] = 2/(max[2]-min[2]); - ret.m[14] = -(max[2]+min[2])/(max[2]-min[2]); - - ret.m[ 3] = 0; - ret.m[ 7] = 0; - ret.m[11] = 0; - ret.m[15] = 1; - - return ret; -} - -static LLFastTimer::DeclareTimer FTM_SHADOW_RENDER("Render Shadows"); -static LLFastTimer::DeclareTimer FTM_SHADOW_ALPHA("Alpha Shadow"); -static LLFastTimer::DeclareTimer FTM_SHADOW_SIMPLE("Simple Shadow"); - -void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera& shadow_cam, LLCullResult &result, BOOL use_shader, BOOL use_occlusion) -{ - LLFastTimer t(FTM_SHADOW_RENDER); - - //clip out geometry on the same side of water as the camera - S32 occlude = LLPipeline::sUseOcclusion; - if (!use_occlusion) - { - LLPipeline::sUseOcclusion = 0; - } - LLPipeline::sShadowRender = TRUE; - - U32 types[] = { LLRenderPass::PASS_SIMPLE, LLRenderPass::PASS_FULLBRIGHT, LLRenderPass::PASS_SHINY, LLRenderPass::PASS_BUMP, LLRenderPass::PASS_FULLBRIGHT_SHINY }; - LLGLEnable cull(GL_CULL_FACE); - - if (use_shader) - { - gDeferredShadowProgram.bind(); - } - - updateCull(shadow_cam, result); - stateSort(shadow_cam, result); - - //generate shadow map - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadMatrixf(proj.m); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadMatrixf(view.m); - - stop_glerror(); - gGLLastMatrix = NULL; - - { - LLGLDepthTest depth(GL_TRUE); - glClear(GL_DEPTH_BUFFER_BIT); - } - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - glColor4f(1,1,1,1); - - stop_glerror(); - - gGL.setColorMask(false, false); - - //glCullFace(GL_FRONT); - - { - LLFastTimer ftm(FTM_SHADOW_SIMPLE); - LLGLDisable test(GL_ALPHA_TEST); - gGL.getTexUnit(0)->disable(); - for (U32 i = 0; i < sizeof(types)/sizeof(U32); ++i) - { - renderObjects(types[i], LLVertexBuffer::MAP_VERTEX, FALSE); - } - gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); - } - - if (use_shader) - { - gDeferredShadowProgram.unbind(); - renderGeomShadow(shadow_cam); - gDeferredShadowProgram.bind(); - } - else - { - renderGeomShadow(shadow_cam); - } - - { - LLFastTimer ftm(FTM_SHADOW_ALPHA); - LLGLEnable test(GL_ALPHA_TEST); - gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.6f); - renderObjects(LLRenderPass::PASS_ALPHA_SHADOW, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_COLOR, TRUE); - glColor4f(1,1,1,1); - renderObjects(LLRenderPass::PASS_GRASS, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, TRUE); - gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); - } - - //glCullFace(GL_BACK); - - gGLLastMatrix = NULL; - glLoadMatrixd(gGLModelView); - doOcclusion(shadow_cam); - - if (use_shader) - { - gDeferredShadowProgram.unbind(); - } - - gGL.setColorMask(true, true); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - gGLLastMatrix = NULL; - - LLPipeline::sUseOcclusion = occlude; - LLPipeline::sShadowRender = FALSE; -} - -static LLFastTimer::DeclareTimer FTM_VISIBLE_CLOUD("Visible Cloud"); -BOOL LLPipeline::getVisiblePointCloud(LLCamera& camera, LLVector3& min, LLVector3& max, std::vector& fp, LLVector3 light_dir) -{ - LLFastTimer t(FTM_VISIBLE_CLOUD); - //get point cloud of intersection of frust and min, max - - if (getVisibleExtents(camera, min, max)) - { - return FALSE; - } - - //get set of planes on bounding box - std::vector bp; - - bp.push_back(LLPlane(min, LLVector3(-1,0,0))); - bp.push_back(LLPlane(min, LLVector3(0,-1,0))); - bp.push_back(LLPlane(min, LLVector3(0,0,-1))); - bp.push_back(LLPlane(max, LLVector3(1,0,0))); - bp.push_back(LLPlane(max, LLVector3(0,1,0))); - bp.push_back(LLPlane(max, LLVector3(0,0,1))); - - //potential points - std::vector pp; - - //add corners of AABB - pp.push_back(LLVector3(min.mV[0], min.mV[1], min.mV[2])); - pp.push_back(LLVector3(max.mV[0], min.mV[1], min.mV[2])); - pp.push_back(LLVector3(min.mV[0], max.mV[1], min.mV[2])); - pp.push_back(LLVector3(max.mV[0], max.mV[1], min.mV[2])); - pp.push_back(LLVector3(min.mV[0], min.mV[1], max.mV[2])); - pp.push_back(LLVector3(max.mV[0], min.mV[1], max.mV[2])); - pp.push_back(LLVector3(min.mV[0], max.mV[1], max.mV[2])); - pp.push_back(LLVector3(max.mV[0], max.mV[1], max.mV[2])); - - //add corners of camera frustum - for (U32 i = 0; i < 8; i++) - { - pp.push_back(camera.mAgentFrustum[i]); - } - - - //bounding box line segments - U32 bs[] = - { - 0,1, - 1,3, - 3,2, - 2,0, - - 4,5, - 5,7, - 7,6, - 6,4, - - 0,4, - 1,5, - 3,7, - 2,6 - }; - - for (U32 i = 0; i < 12; i++) - { //for each line segment in bounding box - for (U32 j = 0; j < 6; j++) - { //for each plane in camera frustum - const LLPlane& cp = camera.getAgentPlane(j); - const LLVector3& v1 = pp[bs[i*2+0]]; - const LLVector3& v2 = pp[bs[i*2+1]]; - const LLVector3 n(cp.mV); - - LLVector3 line = v1-v2; - - F32 d1 = line*n; - F32 d2 = -cp.dist(v2); - - F32 t = d2/d1; - - if (t > 0.f && t < 1.f) - { - LLVector3 intersect = v2+line*t; - pp.push_back(intersect); - } - } - } - - //camera frustum line segments - const U32 fs[] = - { - 0,1, - 1,2, - 2,3, - 3,1, - - 4,5, - 5,6, - 6,7, - 7,4, - - 0,4, - 1,5, - 2,6, - 3,7 - }; - - LLVector3 center = (max+min)*0.5f; - LLVector3 size = (max-min)*0.5f; - - for (U32 i = 0; i < 12; i++) - { - for (U32 j = 0; j < 6; ++j) - { - const LLVector3& v1 = pp[fs[i*2+0]+8]; - const LLVector3& v2 = pp[fs[i*2+1]+8]; - const LLPlane& cp = bp[j]; - const LLVector3 n(cp.mV); - - LLVector3 line = v1-v2; - - F32 d1 = line*n; - F32 d2 = -cp.dist(v2); - - F32 t = d2/d1; - - if (t > 0.f && t < 1.f) - { - LLVector3 intersect = v2+line*t; - pp.push_back(intersect); - } - } - } - - LLVector3 ext[] = { min-LLVector3(0.05f,0.05f,0.05f), - max+LLVector3(0.05f,0.05f,0.05f) }; - - for (U32 i = 0; i < pp.size(); ++i) - { - bool found = true; - - const F32* p = pp[i].mV; - - for (U32 j = 0; j < 3; ++j) - { - if (p[j] < ext[0].mV[j] || - p[j] > ext[1].mV[j]) - { - found = false; - break; - } - } - - for (U32 j = 0; j < 6; ++j) - { - const LLPlane& cp = camera.getAgentPlane(j); - F32 dist = cp.dist(pp[i]); - if (dist > 0.05f) //point is above some plane, not contained - { - found = false; - break; - } - } - - if (found) - { - fp.push_back(pp[i]); - } - } - - if (fp.empty()) - { - return FALSE; - } - - return TRUE; -} - -void LLPipeline::generateGI(LLCamera& camera, LLVector3& lightDir, std::vector& vpc) -{ - if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) < 3) - { - return; - } - - LLVector3 up; - - //LLGLEnable depth_clamp(GL_DEPTH_CLAMP_NV); - - if (lightDir.mV[2] > 0.5f) - { - up = LLVector3(1,0,0); - } - else - { - up = LLVector3(0, 0, 1); - } - - - F32 gi_range = gSavedSettings.getF32("RenderGIRange"); - - U32 res = mGIMap.getWidth(); - - F32 atten = llmax(gSavedSettings.getF32("RenderGIAttenuation"), 0.001f); - - //set radius to range at which distance attenuation of incoming photons is near 0 - - F32 lrad = sqrtf(1.f/(atten*0.01f)); - - F32 lrange = lrad+gi_range*0.5f; - - LLVector3 pad(lrange,lrange,lrange); - - glh::matrix4f view = look(LLVector3(128.f,128.f,128.f), lightDir, up); - - LLVector3 cp = camera.getOrigin()+camera.getAtAxis()*(gi_range*0.5f); - - glh::vec3f scp(cp.mV); - view.mult_matrix_vec(scp); - cp.setVec(scp.v); - - F32 pix_width = lrange/(res*0.5f); - - //move cp to the nearest pix_width - for (U32 i = 0; i < 3; i++) - { - cp.mV[i] = llround(cp.mV[i], pix_width); - } - - LLVector3 min = cp-pad; - LLVector3 max = cp+pad; - - //set mGIRange to range in tc space[0,1] that covers texture block of intersecting lights around a point - mGIRange.mV[0] = (max.mV[0]-min.mV[0])/res; - mGIRange.mV[1] = (max.mV[1]-min.mV[1])/res; - mGILightRadius = lrad/lrange*0.5f; - - glh::matrix4f proj = gl_ortho(min.mV[0], max.mV[0], - min.mV[1], max.mV[1], - -max.mV[2], -min.mV[2]); - - LLCamera sun_cam = camera; - - glh::matrix4f eye_view = glh_get_current_modelview(); - - //get eye space to camera space matrix - mGIMatrix = view*eye_view.inverse(); - mGINormalMatrix = mGIMatrix.inverse().transpose(); - mGIInvProj = proj.inverse(); - mGIMatrixProj = proj*mGIMatrix; - - //translate and scale to [0,1] - glh::matrix4f trans(.5f, 0.f, 0.f, .5f, - 0.f, 0.5f, 0.f, 0.5f, - 0.f, 0.f, 0.5f, 0.5f, - 0.f, 0.f, 0.f, 1.f); - - mGIMatrixProj = trans*mGIMatrixProj; - - glh_set_current_modelview(view); - glh_set_current_projection(proj); - - LLViewerCamera::updateFrustumPlanes(sun_cam, TRUE, FALSE, TRUE); - - sun_cam.ignoreAgentFrustumPlane(LLCamera::AGENT_PLANE_NEAR); - static LLCullResult result; - - pushRenderTypeMask(); - - andRenderTypeMask(LLPipeline::RENDER_TYPE_SIMPLE, - LLPipeline::RENDER_TYPE_FULLBRIGHT, - LLPipeline::RENDER_TYPE_BUMP, - LLPipeline::RENDER_TYPE_VOLUME, - LLPipeline::RENDER_TYPE_TREE, - LLPipeline::RENDER_TYPE_TERRAIN, - LLPipeline::RENDER_TYPE_WATER, - LLPipeline::RENDER_TYPE_VOIDWATER, - LLPipeline::RENDER_TYPE_PASS_ALPHA_SHADOW, - LLPipeline::RENDER_TYPE_AVATAR, - LLPipeline::RENDER_TYPE_PASS_SIMPLE, - LLPipeline::RENDER_TYPE_PASS_BUMP, - LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT, - LLPipeline::RENDER_TYPE_PASS_SHINY, - END_RENDER_TYPES); - - - - S32 occlude = LLPipeline::sUseOcclusion; - //LLPipeline::sUseOcclusion = 0; - LLPipeline::sShadowRender = TRUE; - - //only render large objects into GI map - sMinRenderSize = gSavedSettings.getF32("RenderGIMinRenderSize"); - - LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_GI_SOURCE; - mGIMap.bindTarget(); - - F64 last_modelview[16]; - F64 last_projection[16]; - for (U32 i = 0; i < 16; i++) - { - last_modelview[i] = gGLLastModelView[i]; - last_projection[i] = gGLLastProjection[i]; - gGLLastModelView[i] = mGIModelview.m[i]; - gGLLastProjection[i] = mGIProjection.m[i]; - } - - sun_cam.setOrigin(0.f, 0.f, 0.f); - updateCull(sun_cam, result); - stateSort(sun_cam, result); - - for (U32 i = 0; i < 16; i++) - { - gGLLastModelView[i] = last_modelview[i]; - gGLLastProjection[i] = last_projection[i]; - } - - mGIProjection = proj; - mGIModelview = view; - - LLGLEnable cull(GL_CULL_FACE); - - //generate GI map - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadMatrixf(proj.m); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadMatrixf(view.m); - - stop_glerror(); - gGLLastMatrix = NULL; - - mGIMap.clear(); - - { - //LLGLEnable enable(GL_DEPTH_CLAMP_NV); - renderGeomDeferred(camera); - } - - mGIMap.flush(); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - gGLLastMatrix = NULL; - - LLPipeline::sUseOcclusion = occlude; - LLPipeline::sShadowRender = FALSE; - sMinRenderSize = 0.f; - - popRenderTypeMask(); - -} - -void LLPipeline::renderHighlight(const LLViewerObject* obj, F32 fade) -{ - if (obj && obj->getVolume()) - { - for (LLViewerObject::child_list_t::const_iterator iter = obj->getChildren().begin(); iter != obj->getChildren().end(); ++iter) - { - renderHighlight(*iter, fade); - } - - LLDrawable* drawable = obj->mDrawable; - if (drawable) - { - for (S32 i = 0; i < drawable->getNumFaces(); ++i) - { - LLFace* face = drawable->getFace(i); - if (face) - { - face->renderSelected(LLViewerTexture::sNullImagep, LLColor4(1,1,1,fade)); - } - } - } - } -} - -void LLPipeline::generateHighlight(LLCamera& camera) -{ - //render highlighted object as white into offscreen render target - if (mHighlightObject.notNull()) - { - mHighlightSet.insert(HighlightItem(mHighlightObject)); - } - - if (!mHighlightSet.empty()) - { - F32 transition = gFrameIntervalSeconds/gSavedSettings.getF32("RenderHighlightFadeTime"); - - LLGLDisable test(GL_ALPHA_TEST); - LLGLDepthTest depth(GL_FALSE); - mHighlight.bindTarget(); - disableLights(); - gGL.setColorMask(true, true); - mHighlight.clear(); - - gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep); - for (std::set::iterator iter = mHighlightSet.begin(); iter != mHighlightSet.end(); ) - { - std::set::iterator cur_iter = iter++; - - if (cur_iter->mItem.isNull()) - { - mHighlightSet.erase(cur_iter); - continue; - } - - if (cur_iter->mItem == mHighlightObject) - { - cur_iter->incrFade(transition); - } - else - { - cur_iter->incrFade(-transition); - if (cur_iter->mFade <= 0.f) - { - mHighlightSet.erase(cur_iter); - continue; - } - } - - renderHighlight(cur_iter->mItem->getVObj(), cur_iter->mFade); - } - - mHighlight.flush(); - gGL.setColorMask(true, false); - gViewerWindow->setup3DViewport(); - } -} - - -void LLPipeline::generateSunShadow(LLCamera& camera) -{ - if (!sRenderDeferred || gSavedSettings.getS32("RenderShadowDetail") <= 0) - { - return; - } - - F64 last_modelview[16]; - F64 last_projection[16]; - for (U32 i = 0; i < 16; i++) - { //store last_modelview of world camera - last_modelview[i] = gGLLastModelView[i]; - last_projection[i] = gGLLastProjection[i]; - } - - pushRenderTypeMask(); - andRenderTypeMask(LLPipeline::RENDER_TYPE_SIMPLE, - LLPipeline::RENDER_TYPE_ALPHA, - LLPipeline::RENDER_TYPE_GRASS, - LLPipeline::RENDER_TYPE_FULLBRIGHT, - LLPipeline::RENDER_TYPE_BUMP, - LLPipeline::RENDER_TYPE_VOLUME, - LLPipeline::RENDER_TYPE_AVATAR, - LLPipeline::RENDER_TYPE_TREE, - LLPipeline::RENDER_TYPE_TERRAIN, - LLPipeline::RENDER_TYPE_WATER, - LLPipeline::RENDER_TYPE_VOIDWATER, - LLPipeline::RENDER_TYPE_PASS_ALPHA_SHADOW, - LLPipeline::RENDER_TYPE_PASS_SIMPLE, - LLPipeline::RENDER_TYPE_PASS_BUMP, - LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT, - LLPipeline::RENDER_TYPE_PASS_SHINY, - LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_SHINY, - END_RENDER_TYPES); - - gGL.setColorMask(false, false); - - //get sun view matrix - - //store current projection/modelview matrix - glh::matrix4f saved_proj = glh_get_current_projection(); - glh::matrix4f saved_view = glh_get_current_modelview(); - glh::matrix4f inv_view = saved_view.inverse(); - - glh::matrix4f view[6]; - glh::matrix4f proj[6]; - - //clip contains parallel split distances for 3 splits - LLVector3 clip = gSavedSettings.getVector3("RenderShadowClipPlanes"); - - //F32 slope_threshold = gSavedSettings.getF32("RenderShadowSlopeThreshold"); - - //far clip on last split is minimum of camera view distance and 128 - mSunClipPlanes = LLVector4(clip, clip.mV[2] * clip.mV[2]/clip.mV[1]); - - clip = gSavedSettings.getVector3("RenderShadowOrthoClipPlanes"); - mSunOrthoClipPlanes = LLVector4(clip, clip.mV[2]*clip.mV[2]/clip.mV[1]); - - //currently used for amount to extrude frusta corners for constructing shadow frusta - LLVector3 n = gSavedSettings.getVector3("RenderShadowNearDist"); - //F32 nearDist[] = { n.mV[0], n.mV[1], n.mV[2], n.mV[2] }; - - LLVector3 lightDir = -mSunDir; - lightDir.normVec(); - - glh::vec3f light_dir(lightDir.mV); - - //create light space camera matrix - - LLVector3 at = lightDir; - - LLVector3 up = camera.getAtAxis(); - - if (fabsf(up*lightDir) > 0.75f) - { - up = camera.getUpAxis(); - } - - /*LLVector3 left = up%at; - up = at%left;*/ - - up.normVec(); - at.normVec(); - - - LLCamera main_camera = camera; - - F32 near_clip = 0.f; - { - //get visible point cloud - std::vector fp; - - main_camera.calcAgentFrustumPlanes(main_camera.mAgentFrustum); - - LLVector3 min,max; - getVisiblePointCloud(main_camera,min,max,fp); - - if (fp.empty()) - { - if (!hasRenderDebugMask(RENDER_DEBUG_SHADOW_FRUSTA)) - { - mShadowCamera[0] = main_camera; - mShadowExtents[0][0] = min; - mShadowExtents[0][1] = max; - - mShadowFrustPoints[0].clear(); - mShadowFrustPoints[1].clear(); - mShadowFrustPoints[2].clear(); - mShadowFrustPoints[3].clear(); - } - popRenderTypeMask(); - return; - } - - generateGI(camera, lightDir, fp); - - //get good split distances for frustum - for (U32 i = 0; i < fp.size(); ++i) - { - glh::vec3f v(fp[i].mV); - saved_view.mult_matrix_vec(v); - fp[i].setVec(v.v); - } - - min = fp[0]; - max = fp[0]; - - //get camera space bounding box - for (U32 i = 1; i < fp.size(); ++i) - { - update_min_max(min, max, fp[i]); - } - - near_clip = -max.mV[2]; - F32 far_clip = -min.mV[2]*2.f; - - far_clip = llmin(far_clip, 128.f); - far_clip = llmin(far_clip, camera.getFar()); - - F32 range = far_clip-near_clip; - - LLVector3 split_exp = gSavedSettings.getVector3("RenderShadowSplitExponent"); - - F32 da = 1.f-llmax( fabsf(lightDir*up), fabsf(lightDir*camera.getLeftAxis()) ); - - da = powf(da, split_exp.mV[2]); - - - F32 sxp = split_exp.mV[1] + (split_exp.mV[0]-split_exp.mV[1])*da; - - - for (U32 i = 0; i < 4; ++i) - { - F32 x = (F32)(i+1)/4.f; - x = powf(x, sxp); - mSunClipPlanes.mV[i] = near_clip+range*x; - } - } - - // convenience array of 4 near clip plane distances - F32 dist[] = { near_clip, mSunClipPlanes.mV[0], mSunClipPlanes.mV[1], mSunClipPlanes.mV[2], mSunClipPlanes.mV[3] }; - - for (S32 j = 0; j < 4; j++) - { - if (!hasRenderDebugMask(RENDER_DEBUG_SHADOW_FRUSTA)) - { - mShadowFrustPoints[j].clear(); - } - - LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_SHADOW0+j; - - //restore render matrices - glh_set_current_modelview(saved_view); - glh_set_current_projection(saved_proj); - - LLVector3 eye = camera.getOrigin(); - - //camera used for shadow cull/render - LLCamera shadow_cam; - - //create world space camera frustum for this split - shadow_cam = camera; - shadow_cam.setFar(16.f); - - LLViewerCamera::updateFrustumPlanes(shadow_cam, FALSE, FALSE, TRUE); - - LLVector3* frust = shadow_cam.mAgentFrustum; - - LLVector3 pn = shadow_cam.getAtAxis(); - - LLVector3 min, max; - - //construct 8 corners of split frustum section - for (U32 i = 0; i < 4; i++) - { - LLVector3 delta = frust[i+4]-eye; - delta += (frust[i+4]-frust[(i+2)%4+4])*0.05f; - delta.normVec(); - F32 dp = delta*pn; - frust[i] = eye + (delta*dist[j]*0.95f)/dp; - frust[i+4] = eye + (delta*dist[j+1]*1.05f)/dp; - } - - shadow_cam.calcAgentFrustumPlanes(frust); - shadow_cam.mFrustumCornerDist = 0.f; - - if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA)) - { - mShadowCamera[j] = shadow_cam; - } - - std::vector fp; - - if (!gPipeline.getVisiblePointCloud(shadow_cam, min, max, fp, lightDir)) - { - //no possible shadow receivers - if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA)) - { - mShadowExtents[j][0] = LLVector3(); - mShadowExtents[j][1] = LLVector3(); - mShadowCamera[j+4] = shadow_cam; - } - - mShadow[j].bindTarget(); - { - LLGLDepthTest depth(GL_TRUE); - mShadow[j].clear(); - } - mShadow[j].flush(); - - mShadowError.mV[j] = 0.f; - mShadowFOV.mV[j] = 0.f; - - continue; - } - - if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA)) - { - mShadowExtents[j][0] = min; - mShadowExtents[j][1] = max; - mShadowFrustPoints[j] = fp; - } - - - //find a good origin for shadow projection - LLVector3 origin; - - //get a temporary view projection - view[j] = look(camera.getOrigin(), lightDir, -up); - - std::vector wpf; - - for (U32 i = 0; i < fp.size(); i++) - { - glh::vec3f p = glh::vec3f(fp[i].mV); - view[j].mult_matrix_vec(p); - wpf.push_back(LLVector3(p.v)); - } - - min = wpf[0]; - max = wpf[0]; - - for (U32 i = 0; i < fp.size(); ++i) - { //get AABB in camera space - update_min_max(min, max, wpf[i]); - } - - // Construct a perspective transform with perspective along y-axis that contains - // points in wpf - //Known: - // - far clip plane - // - near clip plane - // - points in frustum - //Find: - // - origin - - //get some "interesting" points of reference - LLVector3 center = (min+max)*0.5f; - LLVector3 size = (max-min)*0.5f; - LLVector3 near_center = center; - near_center.mV[1] += size.mV[1]*2.f; - - - //put all points in wpf in quadrant 0, reletive to center of min/max - //get the best fit line using least squares - F32 bfm = 0.f; - F32 bfb = 0.f; - - for (U32 i = 0; i < wpf.size(); ++i) - { - wpf[i] -= center; - wpf[i].mV[0] = fabsf(wpf[i].mV[0]); - wpf[i].mV[2] = fabsf(wpf[i].mV[2]); - } - - if (!wpf.empty()) - { - F32 sx = 0.f; - F32 sx2 = 0.f; - F32 sy = 0.f; - F32 sxy = 0.f; - - for (U32 i = 0; i < wpf.size(); ++i) - { - sx += wpf[i].mV[0]; - sx2 += wpf[i].mV[0]*wpf[i].mV[0]; - sy += wpf[i].mV[1]; - sxy += wpf[i].mV[0]*wpf[i].mV[1]; - } - - bfm = (sy*sx-wpf.size()*sxy)/(sx*sx-wpf.size()*sx2); - bfb = (sx*sxy-sy*sx2)/(sx*sx-bfm*sx2); - } - - { - // best fit line is y=bfm*x+bfb - - //find point that is furthest to the right of line - F32 off_x = -1.f; - LLVector3 lp; - - for (U32 i = 0; i < wpf.size(); ++i) - { - //y = bfm*x+bfb - //x = (y-bfb)/bfm - F32 lx = (wpf[i].mV[1]-bfb)/bfm; - - lx = wpf[i].mV[0]-lx; - - if (off_x < lx) - { - off_x = lx; - lp = wpf[i]; - } - } - - //get line with slope bfm through lp - // bfb = y-bfm*x - bfb = lp.mV[1]-bfm*lp.mV[0]; - - //calculate error - mShadowError.mV[j] = 0.f; - - for (U32 i = 0; i < wpf.size(); ++i) - { - F32 lx = (wpf[i].mV[1]-bfb)/bfm; - mShadowError.mV[j] += fabsf(wpf[i].mV[0]-lx); - } - - mShadowError.mV[j] /= wpf.size(); - mShadowError.mV[j] /= size.mV[0]; - - if (mShadowError.mV[j] > gSavedSettings.getF32("RenderShadowErrorCutoff")) - { //just use ortho projection - mShadowFOV.mV[j] = -1.f; - origin.clearVec(); - proj[j] = gl_ortho(min.mV[0], max.mV[0], - min.mV[1], max.mV[1], - -max.mV[2], -min.mV[2]); - } - else - { - //origin is where line x = 0; - origin.setVec(0,bfb,0); - - F32 fovz = 1.f; - F32 fovx = 1.f; - - LLVector3 zp; - LLVector3 xp; - - for (U32 i = 0; i < wpf.size(); ++i) - { - LLVector3 atz = wpf[i]-origin; - atz.mV[0] = 0.f; - atz.normVec(); - if (fovz > -atz.mV[1]) - { - zp = wpf[i]; - fovz = -atz.mV[1]; - } - - LLVector3 atx = wpf[i]-origin; - atx.mV[2] = 0.f; - atx.normVec(); - if (fovx > -atx.mV[1]) - { - fovx = -atx.mV[1]; - xp = wpf[i]; - } - } - - fovx = acos(fovx); - fovz = acos(fovz); - - F32 cutoff = llmin(gSavedSettings.getF32("RenderShadowFOVCutoff"), 1.4f); - - mShadowFOV.mV[j] = fovx; - - if (fovx < cutoff && fovz > cutoff) - { - //x is a good fit, but z is too big, move away from zp enough so that fovz matches cutoff - F32 d = zp.mV[2]/tan(cutoff); - F32 ny = zp.mV[1] + fabsf(d); - - origin.mV[1] = ny; - - fovz = 1.f; - fovx = 1.f; - - for (U32 i = 0; i < wpf.size(); ++i) - { - LLVector3 atz = wpf[i]-origin; - atz.mV[0] = 0.f; - atz.normVec(); - fovz = llmin(fovz, -atz.mV[1]); - - LLVector3 atx = wpf[i]-origin; - atx.mV[2] = 0.f; - atx.normVec(); - fovx = llmin(fovx, -atx.mV[1]); - } - - fovx = acos(fovx); - fovz = acos(fovz); - - if (fovx > cutoff || llround(fovz, 0.01f) > cutoff) - { - // llerrs << "WTF?" << llendl; - } - - mShadowFOV.mV[j] = cutoff; - } - - - origin += center; - - F32 ynear = -(max.mV[1]-origin.mV[1]); - F32 yfar = -(min.mV[1]-origin.mV[1]); - - if (ynear < 0.1f) //keep a sensible near clip plane - { - F32 diff = 0.1f-ynear; - origin.mV[1] += diff; - ynear += diff; - yfar += diff; - } - - if (fovx > cutoff) - { //just use ortho projection - origin.clearVec(); - mShadowError.mV[j] = -1.f; - proj[j] = gl_ortho(min.mV[0], max.mV[0], - min.mV[1], max.mV[1], - -max.mV[2], -min.mV[2]); - } - else - { - //get perspective projection - view[j] = view[j].inverse(); - - glh::vec3f origin_agent(origin.mV); - - //translate view to origin - view[j].mult_matrix_vec(origin_agent); - - eye = LLVector3(origin_agent.v); - - if (!hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA)) - { - mShadowFrustOrigin[j] = eye; - } - - view[j] = look(LLVector3(origin_agent.v), lightDir, -up); - - F32 fx = 1.f/tanf(fovx); - F32 fz = 1.f/tanf(fovz); - - proj[j] = glh::matrix4f(-fx, 0, 0, 0, - 0, (yfar+ynear)/(ynear-yfar), 0, (2.f*yfar*ynear)/(ynear-yfar), - 0, 0, -fz, 0, - 0, -1.f, 0, 0); - } - } - } - - shadow_cam.setFar(128.f); - shadow_cam.setOriginAndLookAt(eye, up, center); - - shadow_cam.setOrigin(0,0,0); - - glh_set_current_modelview(view[j]); - glh_set_current_projection(proj[j]); - - LLViewerCamera::updateFrustumPlanes(shadow_cam, FALSE, FALSE, TRUE); - - shadow_cam.ignoreAgentFrustumPlane(LLCamera::AGENT_PLANE_NEAR); - - //translate and scale to from [-1, 1] to [0, 1] - glh::matrix4f trans(0.5f, 0.f, 0.f, 0.5f, - 0.f, 0.5f, 0.f, 0.5f, - 0.f, 0.f, 0.5f, 0.5f, - 0.f, 0.f, 0.f, 1.f); - - glh_set_current_modelview(view[j]); - glh_set_current_projection(proj[j]); - - for (U32 i = 0; i < 16; i++) - { - gGLLastModelView[i] = mShadowModelview[j].m[i]; - gGLLastProjection[i] = mShadowProjection[j].m[i]; - } - - mShadowModelview[j] = view[j]; - mShadowProjection[j] = proj[j]; - - - mSunShadowMatrix[j] = trans*proj[j]*view[j]*inv_view; - - stop_glerror(); - - mShadow[j].bindTarget(); - mShadow[j].getViewport(gGLViewport); - - { - static LLCullResult result[4]; - - //LLGLEnable enable(GL_DEPTH_CLAMP_NV); - renderShadow(view[j], proj[j], shadow_cam, result[j], TRUE); - } - - mShadow[j].flush(); - - if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA)) - { - LLViewerCamera::updateFrustumPlanes(shadow_cam, FALSE, FALSE, TRUE); - mShadowCamera[j+4] = shadow_cam; - } - } - - - //hack to disable projector shadows - static bool clear = true; - bool gen_shadow = gSavedSettings.getS32("RenderShadowDetail") > 1; - - if (gen_shadow) - { - clear = true; - F32 fade_amt = gFrameIntervalSeconds * llmax(LLViewerCamera::getInstance()->getVelocityStat()->getCurrentPerSec(), 1.f); - - //update shadow targets - for (U32 i = 0; i < 2; i++) - { //for each current shadow - LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_SHADOW4+i; - - if (mShadowSpotLight[i].notNull() && - (mShadowSpotLight[i] == mTargetShadowSpotLight[0] || - mShadowSpotLight[i] == mTargetShadowSpotLight[1])) - { //keep this spotlight - mSpotLightFade[i] = llmin(mSpotLightFade[i]+fade_amt, 1.f); - } - else - { //fade out this light - mSpotLightFade[i] = llmax(mSpotLightFade[i]-fade_amt, 0.f); - - if (mSpotLightFade[i] == 0.f || mShadowSpotLight[i].isNull()) - { //faded out, grab one of the pending spots (whichever one isn't already taken) - if (mTargetShadowSpotLight[0] != mShadowSpotLight[(i+1)%2]) - { - mShadowSpotLight[i] = mTargetShadowSpotLight[0]; - } - else - { - mShadowSpotLight[i] = mTargetShadowSpotLight[1]; - } - } - } - } - - for (S32 i = 0; i < 2; i++) - { - glh_set_current_modelview(saved_view); - glh_set_current_projection(saved_proj); - - if (mShadowSpotLight[i].isNull()) - { - continue; - } - - LLVOVolume* volume = mShadowSpotLight[i]->getVOVolume(); - - if (!volume) - { - mShadowSpotLight[i] = NULL; - continue; - } - - LLDrawable* drawable = mShadowSpotLight[i]; - - LLVector3 params = volume->getSpotLightParams(); - F32 fov = params.mV[0]; - - //get agent->light space matrix (modelview) - LLVector3 center = drawable->getPositionAgent(); - LLQuaternion quat = volume->getRenderRotation(); - - //get near clip plane - LLVector3 scale = volume->getScale(); - LLVector3 at_axis(0,0,-scale.mV[2]*0.5f); - at_axis *= quat; - - LLVector3 np = center+at_axis; - at_axis.normVec(); - - //get origin that has given fov for plane np, at_axis, and given scale - F32 dist = (scale.mV[1]*0.5f)/tanf(fov*0.5f); - - LLVector3 origin = np - at_axis*dist; - - LLMatrix4 mat(quat, LLVector4(origin, 1.f)); - - view[i+4] = glh::matrix4f((F32*) mat.mMatrix); - - view[i+4] = view[i+4].inverse(); - - //get perspective matrix - F32 near_clip = dist+0.01f; - F32 width = scale.mV[VX]; - F32 height = scale.mV[VY]; - F32 far_clip = dist+volume->getLightRadius()*1.5f; - - F32 fovy = fov * RAD_TO_DEG; - F32 aspect = width/height; - - proj[i+4] = gl_perspective(fovy, aspect, near_clip, far_clip); - - //translate and scale to from [-1, 1] to [0, 1] - glh::matrix4f trans(0.5f, 0.f, 0.f, 0.5f, - 0.f, 0.5f, 0.f, 0.5f, - 0.f, 0.f, 0.5f, 0.5f, - 0.f, 0.f, 0.f, 1.f); - - glh_set_current_modelview(view[i+4]); - glh_set_current_projection(proj[i+4]); - - mSunShadowMatrix[i+4] = trans*proj[i+4]*view[i+4]*inv_view; - - for (U32 j = 0; j < 16; j++) - { - gGLLastModelView[j] = mShadowModelview[i+4].m[j]; - gGLLastProjection[j] = mShadowProjection[i+4].m[j]; - } - - mShadowModelview[i+4] = view[i+4]; - mShadowProjection[i+4] = proj[i+4]; - - LLCamera shadow_cam = camera; - shadow_cam.setFar(far_clip); - shadow_cam.setOrigin(origin); - - LLViewerCamera::updateFrustumPlanes(shadow_cam, FALSE, FALSE, TRUE); - - stop_glerror(); - - mShadow[i+4].bindTarget(); - mShadow[i+4].getViewport(gGLViewport); - - static LLCullResult result[2]; - - LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_SHADOW0+i+4; - - renderShadow(view[i+4], proj[i+4], shadow_cam, result[i], FALSE, FALSE); - - mShadow[i+4].flush(); - } - } - else - { - if (clear) - { - clear = false; - for (U32 i = 4; i < 6; i++) - { - mShadow[i].bindTarget(); - mShadow[i].clear(); - mShadow[i].flush(); - } - } - } - - if (!gSavedSettings.getBOOL("CameraOffset")) - { - glh_set_current_modelview(saved_view); - glh_set_current_projection(saved_proj); - } - else - { - glh_set_current_modelview(view[1]); - glh_set_current_projection(proj[1]); - glLoadMatrixf(view[1].m); - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(proj[1].m); - glMatrixMode(GL_MODELVIEW); - } - gGL.setColorMask(true, false); - - for (U32 i = 0; i < 16; i++) - { - gGLLastModelView[i] = last_modelview[i]; - gGLLastProjection[i] = last_projection[i]; - } - - popRenderTypeMask(); -} - -void LLPipeline::renderGroups(LLRenderPass* pass, U32 type, U32 mask, BOOL texture) -{ - for (LLCullResult::sg_list_t::iterator i = sCull->beginVisibleGroups(); i != sCull->endVisibleGroups(); ++i) - { - LLSpatialGroup* group = *i; - if (!group->isDead() && - (!sUseOcclusion || !group->isOcclusionState(LLSpatialGroup::OCCLUDED)) && - gPipeline.hasRenderType(group->mSpatialPartition->mDrawableType) && - group->mDrawMap.find(type) != group->mDrawMap.end()) - { - pass->renderGroup(group,type,mask,texture); - } - } -} - -void LLPipeline::generateImpostor(LLVOAvatar* avatar) -{ - LLMemType mt_gi(LLMemType::MTYPE_PIPELINE_GENERATE_IMPOSTOR); - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); - - static LLCullResult result; - result.clear(); - grabReferences(result); - - if (!avatar || !avatar->mDrawable) - { - return; - } - - assertInitialized(); - - BOOL muted = LLMuteList::getInstance()->isMuted(avatar->getID()); - - pushRenderTypeMask(); - - if (muted) - { - andRenderTypeMask(LLPipeline::RENDER_TYPE_AVATAR, END_RENDER_TYPES); - } - else - { - andRenderTypeMask(LLPipeline::RENDER_TYPE_VOLUME, - LLPipeline::RENDER_TYPE_AVATAR, - LLPipeline::RENDER_TYPE_BUMP, - LLPipeline::RENDER_TYPE_GRASS, - LLPipeline::RENDER_TYPE_SIMPLE, - LLPipeline::RENDER_TYPE_FULLBRIGHT, - LLPipeline::RENDER_TYPE_ALPHA, - LLPipeline::RENDER_TYPE_INVISIBLE, - LLPipeline::RENDER_TYPE_PASS_SIMPLE, - LLPipeline::RENDER_TYPE_PASS_ALPHA, - LLPipeline::RENDER_TYPE_PASS_ALPHA_MASK, - LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT, - LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_ALPHA_MASK, - LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_SHINY, - LLPipeline::RENDER_TYPE_PASS_SHINY, - LLPipeline::RENDER_TYPE_PASS_INVISIBLE, - LLPipeline::RENDER_TYPE_PASS_INVISI_SHINY, - END_RENDER_TYPES); - } - - S32 occlusion = sUseOcclusion; - sUseOcclusion = 0; - sReflectionRender = sRenderDeferred ? FALSE : TRUE; - sShadowRender = TRUE; - sImpostorRender = TRUE; - - LLViewerCamera* viewer_camera = LLViewerCamera::getInstance(); - markVisible(avatar->mDrawable, *viewer_camera); - LLVOAvatar::sUseImpostors = FALSE; - - LLVOAvatar::attachment_map_t::iterator iter; - for (iter = avatar->mAttachmentPoints.begin(); - iter != avatar->mAttachmentPoints.end(); - ++iter) - { - LLViewerJointAttachment *attachment = iter->second; - for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); - attachment_iter != attachment->mAttachedObjects.end(); - ++attachment_iter) - { - if (LLViewerObject* attached_object = (*attachment_iter)) - { - markVisible(attached_object->mDrawable->getSpatialBridge(), *viewer_camera); - } - } - } - - stateSort(*LLViewerCamera::getInstance(), result); - - const LLVector3* ext = avatar->mDrawable->getSpatialExtents(); - LLVector3 pos(avatar->getRenderPosition()+avatar->getImpostorOffset()); - - LLCamera camera = *viewer_camera; - - camera.lookAt(viewer_camera->getOrigin(), pos, viewer_camera->getUpAxis()); - - LLVector2 tdim; - - LLVector3 half_height = (ext[1]-ext[0])*0.5f; - - LLVector3 left = camera.getLeftAxis(); - left *= left; - left.normalize(); - - LLVector3 up = camera.getUpAxis(); - up *= up; - up.normalize(); - - tdim.mV[0] = fabsf(half_height * left); - tdim.mV[1] = fabsf(half_height * up); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - //glh::matrix4f ortho = gl_ortho(-tdim.mV[0], tdim.mV[0], -tdim.mV[1], tdim.mV[1], 1.0, 256.0); - F32 distance = (pos-camera.getOrigin()).length(); - F32 fov = atanf(tdim.mV[1]/distance)*2.f*RAD_TO_DEG; - F32 aspect = tdim.mV[0]/tdim.mV[1]; //128.f/256.f; - glh::matrix4f persp = gl_perspective(fov, aspect, 1.f, 256.f); - glh_set_current_projection(persp); - glLoadMatrixf(persp.m); - - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glh::matrix4f mat; - camera.getOpenGLTransform(mat.m); - - mat = glh::matrix4f((GLfloat*) OGL_TO_CFR_ROTATION) * mat; - - glLoadMatrixf(mat.m); - glh_set_current_modelview(mat); - - glClearColor(0.0f,0.0f,0.0f,0.0f); - gGL.setColorMask(true, true); - glStencilMask(0xFFFFFFFF); - glClearStencil(0); - - // get the number of pixels per angle - F32 pa = gViewerWindow->getWindowHeightRaw() / (RAD_TO_DEG * viewer_camera->getView()); - - //get resolution based on angle width and height of impostor (double desired resolution to prevent aliasing) - U32 resY = llmin(nhpo2((U32) (fov*pa)), (U32) 512); - U32 resX = llmin(nhpo2((U32) (atanf(tdim.mV[0]/distance)*2.f*RAD_TO_DEG*pa)), (U32) 512); - - if (!avatar->mImpostor.isComplete() || resX != avatar->mImpostor.getWidth() || - resY != avatar->mImpostor.getHeight()) - { - avatar->mImpostor.allocate(resX,resY,GL_RGBA,TRUE,TRUE); - - if (LLPipeline::sRenderDeferred) - { - addDeferredAttachments(avatar->mImpostor); - } - - gGL.getTexUnit(0)->bind(&avatar->mImpostor); - gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - } - - LLGLEnable stencil(GL_STENCIL_TEST); - glStencilMask(0xFFFFFFFF); - glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - - { - LLGLEnable scissor(GL_SCISSOR_TEST); - glScissor(0, 0, resX, resY); - avatar->mImpostor.bindTarget(); - avatar->mImpostor.clear(); - } - - if (LLPipeline::sRenderDeferred) - { - stop_glerror(); - renderGeomDeferred(camera); - renderGeomPostDeferred(camera); - } - else - { - renderGeom(camera); - } - - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilFunc(GL_EQUAL, 1, 0xFFFFFF); - - { //create alpha mask based on stencil buffer (grey out if muted) - LLVector3 left = camera.getLeftAxis()*tdim.mV[0]*2.f; - LLVector3 up = camera.getUpAxis()*tdim.mV[1]*2.f; - - if (LLPipeline::sRenderDeferred) - { - GLuint buff = GL_COLOR_ATTACHMENT0_EXT; - glDrawBuffersARB(1, &buff); - } - - LLGLEnable blend(muted ? 0 : GL_BLEND); - - if (muted) - { - gGL.setColorMask(true, true); - } - else - { - gGL.setColorMask(false, true); - } - - gGL.setSceneBlendType(LLRender::BT_ADD); - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - LLGLDepthTest depth(GL_FALSE, GL_FALSE); - - gGL.color4f(1,1,1,1); - gGL.color4ub(64,64,64,255); - gGL.begin(LLRender::QUADS); - gGL.vertex3fv((pos+left-up).mV); - gGL.vertex3fv((pos-left-up).mV); - gGL.vertex3fv((pos-left+up).mV); - gGL.vertex3fv((pos+left+up).mV); - gGL.end(); - gGL.flush(); - - gGL.setSceneBlendType(LLRender::BT_ALPHA); - } - - - avatar->mImpostor.flush(); - - avatar->setImpostorDim(tdim); - - LLVOAvatar::sUseImpostors = TRUE; - sUseOcclusion = occlusion; - sReflectionRender = FALSE; - sImpostorRender = FALSE; - sShadowRender = FALSE; - popRenderTypeMask(); - - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - avatar->mNeedsImpostorUpdate = FALSE; - avatar->cacheImpostorValues(); - - LLVertexBuffer::unbind(); - LLGLState::checkStates(); - LLGLState::checkTextureChannels(); - LLGLState::checkClientArrays(); -} - -BOOL LLPipeline::hasRenderBatches(const U32 type) const -{ - return sCull->getRenderMapSize(type) > 0; -} - -LLCullResult::drawinfo_list_t::iterator LLPipeline::beginRenderMap(U32 type) -{ - return sCull->beginRenderMap(type); -} - -LLCullResult::drawinfo_list_t::iterator LLPipeline::endRenderMap(U32 type) -{ - return sCull->endRenderMap(type); -} - -LLCullResult::sg_list_t::iterator LLPipeline::beginAlphaGroups() -{ - return sCull->beginAlphaGroups(); -} - -LLCullResult::sg_list_t::iterator LLPipeline::endAlphaGroups() -{ - return sCull->endAlphaGroups(); -} - -BOOL LLPipeline::hasRenderType(const U32 type) const -{ - // STORM-365 : LLViewerJointAttachment::setAttachmentVisibility() is setting type to 0 to actually mean "do not render" - // We then need to test that value here and return FALSE to prevent attachment to render (in mouselook for instance) - // TODO: reintroduce RENDER_TYPE_NONE in LLRenderTypeMask and initialize its mRenderTypeEnabled[RENDER_TYPE_NONE] to FALSE explicitely - return (type == 0 ? FALSE : mRenderTypeEnabled[type]); -} - -void LLPipeline::setRenderTypeMask(U32 type, ...) -{ - va_list args; - - va_start(args, type); - while (type < END_RENDER_TYPES) - { - mRenderTypeEnabled[type] = TRUE; - type = va_arg(args, U32); - } - va_end(args); - - if (type > END_RENDER_TYPES) - { - llerrs << "Invalid render type." << llendl; - } -} - -BOOL LLPipeline::hasAnyRenderType(U32 type, ...) const -{ - va_list args; - - va_start(args, type); - while (type < END_RENDER_TYPES) - { - if (mRenderTypeEnabled[type]) - { - return TRUE; - } - type = va_arg(args, U32); - } - va_end(args); - - if (type > END_RENDER_TYPES) - { - llerrs << "Invalid render type." << llendl; - } - - return FALSE; -} - -void LLPipeline::pushRenderTypeMask() -{ - std::string cur_mask; - cur_mask.assign((const char*) mRenderTypeEnabled, sizeof(mRenderTypeEnabled)); - mRenderTypeEnableStack.push(cur_mask); -} - -void LLPipeline::popRenderTypeMask() -{ - if (mRenderTypeEnableStack.empty()) - { - llerrs << "Depleted render type stack." << llendl; - } - - memcpy(mRenderTypeEnabled, mRenderTypeEnableStack.top().data(), sizeof(mRenderTypeEnabled)); - mRenderTypeEnableStack.pop(); -} - -void LLPipeline::andRenderTypeMask(U32 type, ...) -{ - va_list args; - - BOOL tmp[NUM_RENDER_TYPES]; - for (U32 i = 0; i < NUM_RENDER_TYPES; ++i) - { - tmp[i] = FALSE; - } - - va_start(args, type); - while (type < END_RENDER_TYPES) - { - if (mRenderTypeEnabled[type]) - { - tmp[type] = TRUE; - } - - type = va_arg(args, U32); - } - va_end(args); - - if (type > END_RENDER_TYPES) - { - llerrs << "Invalid render type." << llendl; - } - - for (U32 i = 0; i < LLPipeline::NUM_RENDER_TYPES; ++i) - { - mRenderTypeEnabled[i] = tmp[i]; - } - -} - -void LLPipeline::clearRenderTypeMask(U32 type, ...) -{ - va_list args; - - va_start(args, type); - while (type < END_RENDER_TYPES) - { - mRenderTypeEnabled[type] = FALSE; - - type = va_arg(args, U32); - } - va_end(args); - - if (type > END_RENDER_TYPES) - { - llerrs << "Invalid render type." << llendl; - } -} - - +/** + * @file pipeline.cpp + * @brief Rendering pipeline. + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "pipeline.h" + +// library includes +#include "llaudioengine.h" // For debugging. +#include "imageids.h" +#include "llerror.h" +#include "llviewercontrol.h" +#include "llfasttimer.h" +#include "llfontgl.h" +#include "llmemtype.h" +#include "llnamevalue.h" +#include "llpointer.h" +#include "llprimitive.h" +#include "llvolume.h" +#include "material_codes.h" +#include "timing.h" +#include "v3color.h" +#include "llui.h" +#include "llglheaders.h" +#include "llrender.h" +#include "llwindow.h" // swapBuffers() + +// newview includes +#include "llagent.h" +#include "llagentcamera.h" +#include "lldrawable.h" +#include "lldrawpoolalpha.h" +#include "lldrawpoolavatar.h" +#include "lldrawpoolground.h" +#include "lldrawpoolbump.h" +#include "lldrawpooltree.h" +#include "lldrawpoolwater.h" +#include "llface.h" +#include "llfeaturemanager.h" +#include "llfloatertelehub.h" +#include "llfloaterreg.h" +#include "llgldbg.h" +#include "llhudmanager.h" +#include "llhudnametag.h" +#include "llhudtext.h" +#include "lllightconstants.h" +#include "llresmgr.h" +#include "llselectmgr.h" +#include "llsky.h" +#include "lltracker.h" +#include "lltool.h" +#include "lltoolmgr.h" +#include "llviewercamera.h" +#include "llviewertexturelist.h" +#include "llviewerobject.h" +#include "llviewerobjectlist.h" +#include "llviewerparcelmgr.h" +#include "llviewerregion.h" // for audio debugging. +#include "llviewerwindow.h" // For getSpinAxis +#include "llvoavatarself.h" +#include "llvoground.h" +#include "llvosky.h" +#include "llvotree.h" +#include "llvovolume.h" +#include "llvosurfacepatch.h" +#include "llvowater.h" +#include "llvotree.h" +#include "llvopartgroup.h" +#include "llworld.h" +#include "llcubemap.h" +#include "llviewershadermgr.h" +#include "llviewerstats.h" +#include "llviewerjoystick.h" +#include "llviewerdisplay.h" +#include "llwlparammanager.h" +#include "llwaterparammanager.h" +#include "llspatialpartition.h" +#include "llmutelist.h" +#include "lltoolpie.h" + + +#ifdef _DEBUG +// Debug indices is disabled for now for debug performance - djs 4/24/02 +//#define DEBUG_INDICES +#else +//#define DEBUG_INDICES +#endif + +const F32 BACKLIGHT_DAY_MAGNITUDE_AVATAR = 0.2f; +const F32 BACKLIGHT_NIGHT_MAGNITUDE_AVATAR = 0.1f; +const F32 BACKLIGHT_DAY_MAGNITUDE_OBJECT = 0.1f; +const F32 BACKLIGHT_NIGHT_MAGNITUDE_OBJECT = 0.08f; +const S32 MAX_OFFSCREEN_GEOMETRY_CHANGES_PER_FRAME = 10; +const U32 REFLECTION_MAP_RES = 128; + +// Max number of occluders to search for. JC +const S32 MAX_OCCLUDER_COUNT = 2; + +extern S32 gBoxFrame; +//extern BOOL gHideSelectedObjects; +extern BOOL gDisplaySwapBuffers; +extern BOOL gDebugGL; + +// hack counter for rendering a fixed number of frames after toggling +// fullscreen to work around DEV-5361 +static S32 sDelayedVBOEnable = 0; + +BOOL gAvatarBacklight = FALSE; + +BOOL gDebugPipeline = FALSE; +LLPipeline gPipeline; +const LLMatrix4* gGLLastMatrix = NULL; + +LLFastTimer::DeclareTimer FTM_RENDER_GEOMETRY("Geometry"); +LLFastTimer::DeclareTimer FTM_RENDER_GRASS("Grass"); +LLFastTimer::DeclareTimer FTM_RENDER_INVISIBLE("Invisible"); +LLFastTimer::DeclareTimer FTM_RENDER_OCCLUSION("Occlusion"); +LLFastTimer::DeclareTimer FTM_RENDER_SHINY("Shiny"); +LLFastTimer::DeclareTimer FTM_RENDER_SIMPLE("Simple"); +LLFastTimer::DeclareTimer FTM_RENDER_TERRAIN("Terrain"); +LLFastTimer::DeclareTimer FTM_RENDER_TREES("Trees"); +LLFastTimer::DeclareTimer FTM_RENDER_UI("UI"); +LLFastTimer::DeclareTimer FTM_RENDER_WATER("Water"); +LLFastTimer::DeclareTimer FTM_RENDER_WL_SKY("Windlight Sky"); +LLFastTimer::DeclareTimer FTM_RENDER_ALPHA("Alpha Objects"); +LLFastTimer::DeclareTimer FTM_RENDER_CHARACTERS("Avatars"); +LLFastTimer::DeclareTimer FTM_RENDER_BUMP("Bump"); +LLFastTimer::DeclareTimer FTM_RENDER_FULLBRIGHT("Fullbright"); +LLFastTimer::DeclareTimer FTM_RENDER_GLOW("Glow"); +LLFastTimer::DeclareTimer FTM_GEO_UPDATE("Geo Update"); +LLFastTimer::DeclareTimer FTM_POOLRENDER("RenderPool"); +LLFastTimer::DeclareTimer FTM_POOLS("Pools"); +LLFastTimer::DeclareTimer FTM_RENDER_BLOOM_FBO("First FBO"); +LLFastTimer::DeclareTimer FTM_STATESORT("Sort Draw State"); +LLFastTimer::DeclareTimer FTM_PIPELINE("Pipeline"); +LLFastTimer::DeclareTimer FTM_CLIENT_COPY("Client Copy"); +LLFastTimer::DeclareTimer FTM_RENDER_DEFERRED("Deferred Shading"); + + +static LLFastTimer::DeclareTimer FTM_STATESORT_DRAWABLE("Sort Drawables"); +static LLFastTimer::DeclareTimer FTM_STATESORT_POSTSORT("Post Sort"); + +//---------------------------------------- +std::string gPoolNames[] = +{ + // Correspond to LLDrawpool enum render type + "NONE", + "POOL_SIMPLE", + "POOL_TERRAIN", + "POOL_BUMP", + "POOL_TREE", + "POOL_SKY", + "POOL_WL_SKY", + "POOL_GROUND", + "POOL_INVISIBLE", + "POOL_AVATAR", + "POOL_WATER", + "POOL_GRASS", + "POOL_FULLBRIGHT", + "POOL_GLOW", + "POOL_ALPHA", +}; + +void drawBox(const LLVector3& c, const LLVector3& r); +void drawBoxOutline(const LLVector3& pos, const LLVector3& size); + +U32 nhpo2(U32 v) +{ + U32 r = 1; + while (r < v) { + r *= 2; + } + return r; +} + +glh::matrix4f glh_copy_matrix(GLdouble* src) +{ + glh::matrix4f ret; + for (U32 i = 0; i < 16; i++) + { + ret.m[i] = (F32) src[i]; + } + return ret; +} + +glh::matrix4f glh_get_current_modelview() +{ + return glh_copy_matrix(gGLModelView); +} + +glh::matrix4f glh_get_current_projection() +{ + return glh_copy_matrix(gGLProjection); +} + +void glh_copy_matrix(const glh::matrix4f& src, GLdouble* dst) +{ + for (U32 i = 0; i < 16; i++) + { + dst[i] = src.m[i]; + } +} + +void glh_set_current_modelview(const glh::matrix4f& mat) +{ + glh_copy_matrix(mat, gGLModelView); +} + +void glh_set_current_projection(glh::matrix4f& mat) +{ + glh_copy_matrix(mat, gGLProjection); +} + +glh::matrix4f gl_ortho(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat znear, GLfloat zfar) +{ + glh::matrix4f ret( + 2.f/(right-left), 0.f, 0.f, -(right+left)/(right-left), + 0.f, 2.f/(top-bottom), 0.f, -(top+bottom)/(top-bottom), + 0.f, 0.f, -2.f/(zfar-znear), -(zfar+znear)/(zfar-znear), + 0.f, 0.f, 0.f, 1.f); + + return ret; +} + +void display_update_camera(); +//---------------------------------------- + +S32 LLPipeline::sCompiles = 0; + +BOOL LLPipeline::sPickAvatar = TRUE; +BOOL LLPipeline::sDynamicLOD = TRUE; +BOOL LLPipeline::sShowHUDAttachments = TRUE; +BOOL LLPipeline::sRenderPhysicalBeacons = TRUE; +BOOL LLPipeline::sRenderScriptedBeacons = FALSE; +BOOL LLPipeline::sRenderScriptedTouchBeacons = TRUE; +BOOL LLPipeline::sRenderParticleBeacons = FALSE; +BOOL LLPipeline::sRenderSoundBeacons = FALSE; +BOOL LLPipeline::sRenderBeacons = FALSE; +BOOL LLPipeline::sRenderHighlight = TRUE; +BOOL LLPipeline::sForceOldBakedUpload = FALSE; +S32 LLPipeline::sUseOcclusion = 0; +BOOL LLPipeline::sDelayVBUpdate = TRUE; +BOOL LLPipeline::sAutoMaskAlphaDeferred = TRUE; +BOOL LLPipeline::sAutoMaskAlphaNonDeferred = FALSE; +BOOL LLPipeline::sDisableShaders = FALSE; +BOOL LLPipeline::sRenderBump = TRUE; +BOOL LLPipeline::sUseTriStrips = TRUE; +BOOL LLPipeline::sUseFarClip = TRUE; +BOOL LLPipeline::sShadowRender = FALSE; +BOOL LLPipeline::sWaterReflections = FALSE; +BOOL LLPipeline::sRenderGlow = FALSE; +BOOL LLPipeline::sReflectionRender = FALSE; +BOOL LLPipeline::sImpostorRender = FALSE; +BOOL LLPipeline::sUnderWaterRender = FALSE; +BOOL LLPipeline::sTextureBindTest = FALSE; +BOOL LLPipeline::sRenderFrameTest = FALSE; +BOOL LLPipeline::sRenderAttachedLights = TRUE; +BOOL LLPipeline::sRenderAttachedParticles = TRUE; +BOOL LLPipeline::sRenderDeferred = FALSE; +BOOL LLPipeline::sAllowRebuildPriorityGroup = FALSE ; +S32 LLPipeline::sVisibleLightCount = 0; +F32 LLPipeline::sMinRenderSize = 0.f; + + +static LLCullResult* sCull = NULL; + +static const U32 gl_cube_face[] = +{ + GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, +}; + +void validate_framebuffer_object(); + + +void addDeferredAttachments(LLRenderTarget& target) +{ + target.addColorAttachment(GL_RGBA); //specular + target.addColorAttachment(GL_RGBA); //normal+z +} + +LLPipeline::LLPipeline() : + mBackfaceCull(FALSE), + mBatchCount(0), + mMatrixOpCount(0), + mTextureMatrixOps(0), + mMaxBatchSize(0), + mMinBatchSize(0), + mMeanBatchSize(0), + mTrianglesDrawn(0), + mNumVisibleNodes(0), + mVerticesRelit(0), + mLightingChanges(0), + mGeometryChanges(0), + mNumVisibleFaces(0), + + mInitialized(FALSE), + mVertexShadersEnabled(FALSE), + mVertexShadersLoaded(0), + mRenderDebugFeatureMask(0), + mRenderDebugMask(0), + mOldRenderDebugMask(0), + mLastRebuildPool(NULL), + mAlphaPool(NULL), + mSkyPool(NULL), + mTerrainPool(NULL), + mWaterPool(NULL), + mGroundPool(NULL), + mSimplePool(NULL), + mFullbrightPool(NULL), + mInvisiblePool(NULL), + mGlowPool(NULL), + mBumpPool(NULL), + mWLSkyPool(NULL), + mLightMask(0), + mLightMovingMask(0), + mLightingDetail(0), + mScreenWidth(0), + mScreenHeight(0) +{ + mNoiseMap = 0; + mTrueNoiseMap = 0; + mLightFunc = 0; +} + +void LLPipeline::init() +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_INIT); + + sDynamicLOD = gSavedSettings.getBOOL("RenderDynamicLOD"); + sRenderBump = gSavedSettings.getBOOL("RenderObjectBump"); + sUseTriStrips = gSavedSettings.getBOOL("RenderUseTriStrips"); + LLVertexBuffer::sUseStreamDraw = gSavedSettings.getBOOL("RenderUseStreamVBO"); + sRenderAttachedLights = gSavedSettings.getBOOL("RenderAttachedLights"); + sRenderAttachedParticles = gSavedSettings.getBOOL("RenderAttachedParticles"); + + mInitialized = TRUE; + + stop_glerror(); + + //create render pass pools + getPool(LLDrawPool::POOL_ALPHA); + getPool(LLDrawPool::POOL_SIMPLE); + getPool(LLDrawPool::POOL_GRASS); + getPool(LLDrawPool::POOL_FULLBRIGHT); + getPool(LLDrawPool::POOL_INVISIBLE); + getPool(LLDrawPool::POOL_BUMP); + getPool(LLDrawPool::POOL_GLOW); + + LLViewerStats::getInstance()->mTrianglesDrawnStat.reset(); + resetFrameStats(); + + for (U32 i = 0; i < NUM_RENDER_TYPES; ++i) + { + mRenderTypeEnabled[i] = TRUE; //all rendering types start enabled + } + + mRenderDebugFeatureMask = 0xffffffff; // All debugging features on + mRenderDebugMask = 0; // All debug starts off + + // Don't turn on ground when this is set + // Mac Books with intel 950s need this + if(!gSavedSettings.getBOOL("RenderGround")) + { + toggleRenderType(RENDER_TYPE_GROUND); + } + + mOldRenderDebugMask = mRenderDebugMask; + + mBackfaceCull = TRUE; + + stop_glerror(); + + // Enable features + + LLViewerShaderMgr::instance()->setShaders(); + + stop_glerror(); + + for (U32 i = 0; i < 2; ++i) + { + mSpotLightFade[i] = 1.f; + } + + setLightingDetail(-1); +} + +LLPipeline::~LLPipeline() +{ + +} + +void LLPipeline::cleanup() +{ + assertInitialized(); + + mGroupQ1.clear() ; + mGroupQ2.clear() ; + + for(pool_set_t::iterator iter = mPools.begin(); + iter != mPools.end(); ) + { + pool_set_t::iterator curiter = iter++; + LLDrawPool* poolp = *curiter; + if (poolp->isFacePool()) + { + LLFacePool* face_pool = (LLFacePool*) poolp; + if (face_pool->mReferences.empty()) + { + mPools.erase(curiter); + removeFromQuickLookup( poolp ); + delete poolp; + } + } + else + { + mPools.erase(curiter); + removeFromQuickLookup( poolp ); + delete poolp; + } + } + + if (!mTerrainPools.empty()) + { + llwarns << "Terrain Pools not cleaned up" << llendl; + } + if (!mTreePools.empty()) + { + llwarns << "Tree Pools not cleaned up" << llendl; + } + + delete mAlphaPool; + mAlphaPool = NULL; + delete mSkyPool; + mSkyPool = NULL; + delete mTerrainPool; + mTerrainPool = NULL; + delete mWaterPool; + mWaterPool = NULL; + delete mGroundPool; + mGroundPool = NULL; + delete mSimplePool; + mSimplePool = NULL; + delete mFullbrightPool; + mFullbrightPool = NULL; + delete mInvisiblePool; + mInvisiblePool = NULL; + delete mGlowPool; + mGlowPool = NULL; + delete mBumpPool; + mBumpPool = NULL; + // don't delete wl sky pool it was handled above in the for loop + //delete mWLSkyPool; + mWLSkyPool = NULL; + + releaseGLBuffers(); + + mFaceSelectImagep = NULL; + + mMovedBridge.clear(); + + mInitialized = FALSE; +} + +//============================================================================ + +void LLPipeline::destroyGL() +{ + stop_glerror(); + unloadShaders(); + mHighlightFaces.clear(); + + resetDrawOrders(); + + resetVertexBuffers(); + + releaseGLBuffers(); + + if (LLVertexBuffer::sEnableVBOs) + { + // render 30 frames after switching to work around DEV-5361 + sDelayedVBOEnable = 30; + LLVertexBuffer::sEnableVBOs = FALSE; + } +} + +static LLFastTimer::DeclareTimer FTM_RESIZE_SCREEN_TEXTURE("Resize Screen Texture"); + +void LLPipeline::resizeScreenTexture() +{ + LLFastTimer ft(FTM_RESIZE_SCREEN_TEXTURE); + if (gPipeline.canUseVertexShaders() && assertInitialized()) + { + GLuint resX = gViewerWindow->getWorldViewWidthRaw(); + GLuint resY = gViewerWindow->getWorldViewHeightRaw(); + + allocateScreenBuffer(resX,resY); + } +} + +void LLPipeline::allocateScreenBuffer(U32 resX, U32 resY) +{ + // remember these dimensions + mScreenWidth = resX; + mScreenHeight = resY; + + //never use more than 4 samples for render targets + U32 samples = llmin(gSavedSettings.getU32("RenderFSAASamples"), (U32) 4); + U32 res_mod = gSavedSettings.getU32("RenderResolutionDivisor"); + + if (res_mod > 1 && res_mod < resX && res_mod < resY) + { + resX /= res_mod; + resY /= res_mod; + } + + if (gSavedSettings.getBOOL("RenderUIBuffer")) + { + mUIScreen.allocate(resX,resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); + } + + if (LLPipeline::sRenderDeferred) + { + //allocate deferred rendering color buffers + mDeferredScreen.allocate(resX, resY, GL_RGBA, TRUE, TRUE, LLTexUnit::TT_RECT_TEXTURE, FALSE); + mDeferredDepth.allocate(resX, resY, 0, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); + addDeferredAttachments(mDeferredScreen); + + mScreen.allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); + mEdgeMap.allocate(resX, resY, GL_ALPHA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE, FALSE); + + for (U32 i = 0; i < 3; i++) + { + mDeferredLight[i].allocate(resX, resY, GL_RGBA, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE); + } + + for (U32 i = 0; i < 2; i++) + { + mGIMapPost[i].allocate(resX,resY, GL_RGB, FALSE, FALSE, LLTexUnit::TT_RECT_TEXTURE); + } + + F32 scale = gSavedSettings.getF32("RenderShadowResolutionScale"); + + //HACK: make alpha masking work on ATI depth shadows (work around for ATI driver bug) + U32 shadow_fmt = gGLManager.mIsATI ? GL_ALPHA : 0; + + for (U32 i = 0; i < 4; i++) + { + mShadow[i].allocate(U32(resX*scale),U32(resY*scale), shadow_fmt, TRUE, FALSE, LLTexUnit::TT_RECT_TEXTURE); + } + + + U32 width = nhpo2(U32(resX*scale))/2; + U32 height = width; + + for (U32 i = 4; i < 6; i++) + { + mShadow[i].allocate(width, height, shadow_fmt, TRUE, FALSE); + } + + width = nhpo2(resX)/2; + height = nhpo2(resY)/2; + mLuminanceMap.allocate(width,height, GL_RGBA, FALSE, FALSE); + } + else + { + mScreen.allocate(resX, resY, GL_RGBA, TRUE, TRUE, LLTexUnit::TT_RECT_TEXTURE, FALSE); + } + + + if (LLRenderTarget::sUseFBO && gGLManager.mHasFramebufferMultisample && samples > 1) + { + mSampleBuffer.allocate(resX,resY,GL_RGBA,TRUE,TRUE,LLTexUnit::TT_RECT_TEXTURE,FALSE,samples); + if (LLPipeline::sRenderDeferred) + { + addDeferredAttachments(mSampleBuffer); + mDeferredScreen.setSampleBuffer(&mSampleBuffer); + } + + mScreen.setSampleBuffer(&mSampleBuffer); + + stop_glerror(); + } + + if (LLPipeline::sRenderDeferred) + { //share depth buffer between deferred targets + mDeferredScreen.shareDepthBuffer(mScreen); + for (U32 i = 0; i < 3; i++) + { //share stencil buffer with screen space lightmap to stencil out sky + mDeferredScreen.shareDepthBuffer(mDeferredLight[i]); + } + } + + gGL.getTexUnit(0)->disable(); + + stop_glerror(); + +} + +//static +void LLPipeline::updateRenderDeferred() +{ + BOOL deferred = ((gSavedSettings.getBOOL("RenderDeferred") && + LLRenderTarget::sUseFBO && + LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") && + gSavedSettings.getBOOL("VertexShaderEnable") && + gSavedSettings.getBOOL("RenderAvatarVP") && + gSavedSettings.getBOOL("WindLightUseAtmosShaders")) ? TRUE : FALSE) && + !gUseWireframe; + + sRenderDeferred = deferred; +} + +void LLPipeline::releaseGLBuffers() +{ + assertInitialized(); + + if (mNoiseMap) + { + LLImageGL::deleteTextures(1, &mNoiseMap); + mNoiseMap = 0; + } + + if (mTrueNoiseMap) + { + LLImageGL::deleteTextures(1, &mTrueNoiseMap); + mTrueNoiseMap = 0; + } + + if (mLightFunc) + { + LLImageGL::deleteTextures(1, &mLightFunc); + mLightFunc = 0; + } + + mWaterRef.release(); + mWaterDis.release(); + mScreen.release(); + mUIScreen.release(); + mSampleBuffer.releaseSampleBuffer(); + mDeferredScreen.release(); + mDeferredDepth.release(); + for (U32 i = 0; i < 3; i++) + { + mDeferredLight[i].release(); + } + + mEdgeMap.release(); + mGIMap.release(); + mGIMapPost[0].release(); + mGIMapPost[1].release(); + mHighlight.release(); + mLuminanceMap.release(); + + for (U32 i = 0; i < 6; i++) + { + mShadow[i].release(); + } + + for (U32 i = 0; i < 3; i++) + { + mGlow[i].release(); + } + + LLVOAvatar::resetImpostors(); +} + +void LLPipeline::createGLBuffers() +{ + LLMemType mt_cb(LLMemType::MTYPE_PIPELINE_CREATE_BUFFERS); + assertInitialized(); + + updateRenderDeferred(); + + if (LLPipeline::sWaterReflections) + { //water reflection texture + U32 res = (U32) gSavedSettings.getS32("RenderWaterRefResolution"); + + mWaterRef.allocate(res,res,GL_RGBA,TRUE,FALSE); + mWaterDis.allocate(res,res,GL_RGBA,TRUE,FALSE); + } + + mHighlight.allocate(256,256,GL_RGBA, FALSE, FALSE); + + stop_glerror(); + + GLuint resX = gViewerWindow->getWorldViewWidthRaw(); + GLuint resY = gViewerWindow->getWorldViewHeightRaw(); + + if (LLPipeline::sRenderGlow) + { //screen space glow buffers + const U32 glow_res = llmax(1, + llmin(512, 1 << gSavedSettings.getS32("RenderGlowResolutionPow"))); + + for (U32 i = 0; i < 3; i++) + { + mGlow[i].allocate(512,glow_res,GL_RGBA,FALSE,FALSE); + } + + allocateScreenBuffer(resX,resY); + mScreenWidth = 0; + mScreenHeight = 0; + + } + + if (sRenderDeferred) + { + if (!mNoiseMap) + { + const U32 noiseRes = 128; + LLVector3 noise[noiseRes*noiseRes]; + + F32 scaler = gSavedSettings.getF32("RenderDeferredNoise")/100.f; + for (U32 i = 0; i < noiseRes*noiseRes; ++i) + { + noise[i] = LLVector3(ll_frand()-0.5f, ll_frand()-0.5f, 0.f); + noise[i].normVec(); + noise[i].mV[2] = ll_frand()*scaler+1.f-scaler/2.f; + } + + LLImageGL::generateTextures(1, &mNoiseMap); + + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mNoiseMap); + LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_RGB16F_ARB, noiseRes, noiseRes, GL_RGB, GL_FLOAT, noise); + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + } + + if (!mTrueNoiseMap) + { + const U32 noiseRes = 128; + F32 noise[noiseRes*noiseRes*3]; + for (U32 i = 0; i < noiseRes*noiseRes*3; i++) + { + noise[i] = ll_frand()*2.0-1.0; + } + + LLImageGL::generateTextures(1, &mTrueNoiseMap); + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mTrueNoiseMap); + LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_RGB16F_ARB, noiseRes, noiseRes, GL_RGB,GL_FLOAT, noise); + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + } + + if (!mLightFunc) + { + U32 lightResX = gSavedSettings.getU32("RenderSpecularResX"); + U32 lightResY = gSavedSettings.getU32("RenderSpecularResY"); + U8* lg = new U8[lightResX*lightResY]; + + for (U32 y = 0; y < lightResY; ++y) + { + for (U32 x = 0; x < lightResX; ++x) + { + //spec func + F32 sa = (F32) x/(lightResX-1); + F32 spec = (F32) y/(lightResY-1); + //lg[y*lightResX+x] = (U8) (powf(sa, 128.f*spec*spec)*255); + + //F32 sp = acosf(sa)/(1.f-spec); + + sa = powf(sa, gSavedSettings.getF32("RenderSpecularExponent")); + F32 a = acosf(sa*0.25f+0.75f); + F32 m = llmax(0.5f-spec*0.5f, 0.001f); + F32 t2 = tanf(a)/m; + t2 *= t2; + + F32 c4a = (3.f+4.f*cosf(2.f*a)+cosf(4.f*a))/8.f; + F32 bd = 1.f/(4.f*m*m*c4a)*powf(F_E, -t2); + + lg[y*lightResX+x] = (U8) (llclamp(bd, 0.f, 1.f)*255); + } + } + + LLImageGL::generateTextures(1, &mLightFunc); + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mLightFunc); + LLImageGL::setManualImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_ALPHA, lightResX, lightResY, GL_ALPHA, GL_UNSIGNED_BYTE, lg); + gGL.getTexUnit(0)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_TRILINEAR); + + delete [] lg; + } + + if (gSavedSettings.getBOOL("RenderDeferredGI")) + { + mGIMap.allocate(512,512,GL_RGBA, TRUE, FALSE); + addDeferredAttachments(mGIMap); + } + } +} + +void LLPipeline::restoreGL() +{ + LLMemType mt_cb(LLMemType::MTYPE_PIPELINE_RESTORE_GL); + assertInitialized(); + + if (mVertexShadersEnabled) + { + LLViewerShaderMgr::instance()->setShaders(); + } + + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) + { + LLSpatialPartition* part = region->getSpatialPartition(i); + if (part) + { + part->restoreGL(); + } + } + } +} + + +BOOL LLPipeline::canUseVertexShaders() +{ + if (sDisableShaders || + !gGLManager.mHasVertexShader || + !gGLManager.mHasFragmentShader || + !LLFeatureManager::getInstance()->isFeatureAvailable("VertexShaderEnable") || + (assertInitialized() && mVertexShadersLoaded != 1) ) + { + return FALSE; + } + else + { + return TRUE; + } +} + +BOOL LLPipeline::canUseWindLightShaders() const +{ + return (!LLPipeline::sDisableShaders && + gWLSkyProgram.mProgramObject != 0 && + LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_WINDLIGHT) > 1); +} + +BOOL LLPipeline::canUseWindLightShadersOnObjects() const +{ + return (canUseWindLightShaders() + && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_OBJECT) > 0); +} + +BOOL LLPipeline::canUseAntiAliasing() const +{ + return TRUE; //(gSavedSettings.getBOOL("RenderUseFBO")); +} + +void LLPipeline::unloadShaders() +{ + LLMemType mt_us(LLMemType::MTYPE_PIPELINE_UNLOAD_SHADERS); + LLViewerShaderMgr::instance()->unloadShaders(); + + mVertexShadersLoaded = 0; +} + +void LLPipeline::assertInitializedDoError() +{ + llerrs << "LLPipeline used when uninitialized." << llendl; +} + +//============================================================================ + +void LLPipeline::enableShadows(const BOOL enable_shadows) +{ + //should probably do something here to wrangle shadows.... +} + +S32 LLPipeline::getMaxLightingDetail() const +{ + /*if (mVertexShaderLevel[SHADER_OBJECT] >= LLDrawPoolSimple::SHADER_LEVEL_LOCAL_LIGHTS) + { + return 3; + } + else*/ + { + return 1; + } +} + +S32 LLPipeline::setLightingDetail(S32 level) +{ + LLMemType mt_ld(LLMemType::MTYPE_PIPELINE_LIGHTING_DETAIL); + assertInitialized(); + + if (level < 0) + { + if (gSavedSettings.getBOOL("VertexShaderEnable")) + { + level = 1; + } + else + { + level = 0; + } + } + level = llclamp(level, 0, getMaxLightingDetail()); + if (level != mLightingDetail) + { + mLightingDetail = level; + + if (mVertexShadersLoaded == 1) + { + LLViewerShaderMgr::instance()->setShaders(); + } + } + return mLightingDetail; +} + +class LLOctreeDirtyTexture : public LLOctreeTraveler +{ +public: + const std::set& mTextures; + + LLOctreeDirtyTexture(const std::set& textures) : mTextures(textures) { } + + virtual void visit(const LLOctreeNode* node) + { + LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0); + + if (!group->isState(LLSpatialGroup::GEOM_DIRTY) && !group->getData().empty()) + { + for (LLSpatialGroup::draw_map_t::iterator i = group->mDrawMap.begin(); i != group->mDrawMap.end(); ++i) + { + for (LLSpatialGroup::drawmap_elem_t::iterator j = i->second.begin(); j != i->second.end(); ++j) + { + LLDrawInfo* params = *j; + LLViewerFetchedTexture* tex = LLViewerTextureManager::staticCastToFetchedTexture(params->mTexture); + if (tex && mTextures.find(tex) != mTextures.end()) + { + group->setState(LLSpatialGroup::GEOM_DIRTY); + } + } + } + } + + for (LLSpatialGroup::bridge_list_t::iterator i = group->mBridgeList.begin(); i != group->mBridgeList.end(); ++i) + { + LLSpatialBridge* bridge = *i; + traverse(bridge->mOctree); + } + } +}; + +// Called when a texture changes # of channels (causes faces to move to alpha pool) +void LLPipeline::dirtyPoolObjectTextures(const std::set& textures) +{ + assertInitialized(); + + // *TODO: This is inefficient and causes frame spikes; need a better way to do this + // Most of the time is spent in dirty.traverse. + + for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) + { + LLDrawPool *poolp = *iter; + if (poolp->isFacePool()) + { + ((LLFacePool*) poolp)->dirtyTextures(textures); + } + } + + LLOctreeDirtyTexture dirty(textures); + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) + { + LLSpatialPartition* part = region->getSpatialPartition(i); + if (part) + { + dirty.traverse(part->mOctree); + } + } + } +} + +LLDrawPool *LLPipeline::findPool(const U32 type, LLViewerTexture *tex0) +{ + assertInitialized(); + + LLDrawPool *poolp = NULL; + switch( type ) + { + case LLDrawPool::POOL_SIMPLE: + poolp = mSimplePool; + break; + + case LLDrawPool::POOL_GRASS: + poolp = mGrassPool; + break; + + case LLDrawPool::POOL_FULLBRIGHT: + poolp = mFullbrightPool; + break; + + case LLDrawPool::POOL_INVISIBLE: + poolp = mInvisiblePool; + break; + + case LLDrawPool::POOL_GLOW: + poolp = mGlowPool; + break; + + case LLDrawPool::POOL_TREE: + poolp = get_if_there(mTreePools, (uintptr_t)tex0, (LLDrawPool*)0 ); + break; + + case LLDrawPool::POOL_TERRAIN: + poolp = get_if_there(mTerrainPools, (uintptr_t)tex0, (LLDrawPool*)0 ); + break; + + case LLDrawPool::POOL_BUMP: + poolp = mBumpPool; + break; + + case LLDrawPool::POOL_ALPHA: + poolp = mAlphaPool; + break; + + case LLDrawPool::POOL_AVATAR: + break; // Do nothing + + case LLDrawPool::POOL_SKY: + poolp = mSkyPool; + break; + + case LLDrawPool::POOL_WATER: + poolp = mWaterPool; + break; + + case LLDrawPool::POOL_GROUND: + poolp = mGroundPool; + break; + + case LLDrawPool::POOL_WL_SKY: + poolp = mWLSkyPool; + break; + + default: + llassert(0); + llerrs << "Invalid Pool Type in LLPipeline::findPool() type=" << type << llendl; + break; + } + + return poolp; +} + + +LLDrawPool *LLPipeline::getPool(const U32 type, LLViewerTexture *tex0) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE); + LLDrawPool *poolp = findPool(type, tex0); + if (poolp) + { + return poolp; + } + + LLDrawPool *new_poolp = LLDrawPool::createPool(type, tex0); + addPool( new_poolp ); + + return new_poolp; +} + + +// static +LLDrawPool* LLPipeline::getPoolFromTE(const LLTextureEntry* te, LLViewerTexture* imagep) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE); + U32 type = getPoolTypeFromTE(te, imagep); + return gPipeline.getPool(type, imagep); +} + +//static +U32 LLPipeline::getPoolTypeFromTE(const LLTextureEntry* te, LLViewerTexture* imagep) +{ + LLMemType mt_gpt(LLMemType::MTYPE_PIPELINE_GET_POOL_TYPE); + + if (!te || !imagep) + { + return 0; + } + + bool alpha = te->getColor().mV[3] < 0.999f; + if (imagep) + { + alpha = alpha || (imagep->getComponents() == 4 && imagep->getType() != LLViewerTexture::MEDIA_TEXTURE) || (imagep->getComponents() == 2); + } + + if (alpha) + { + return LLDrawPool::POOL_ALPHA; + } + else if ((te->getBumpmap() || te->getShiny())) + { + return LLDrawPool::POOL_BUMP; + } + else + { + return LLDrawPool::POOL_SIMPLE; + } +} + + +void LLPipeline::addPool(LLDrawPool *new_poolp) +{ + LLMemType mt_a(LLMemType::MTYPE_PIPELINE_ADD_POOL); + assertInitialized(); + mPools.insert(new_poolp); + addToQuickLookup( new_poolp ); +} + +void LLPipeline::allocDrawable(LLViewerObject *vobj) +{ + LLMemType mt_ad(LLMemType::MTYPE_PIPELINE_ALLOCATE_DRAWABLE); + LLDrawable *drawable = new LLDrawable(); + vobj->mDrawable = drawable; + + drawable->mVObjp = vobj; + + //encompass completely sheared objects by taking + //the most extreme point possible (<1,1,0.5>) + drawable->setRadius(LLVector3(1,1,0.5f).scaleVec(vobj->getScale()).length()); + if (vobj->isOrphaned()) + { + drawable->setState(LLDrawable::FORCE_INVISIBLE); + } + drawable->updateXform(TRUE); +} + + +void LLPipeline::unlinkDrawable(LLDrawable *drawable) +{ + LLFastTimer t(FTM_PIPELINE); + + assertInitialized(); + + LLPointer drawablep = drawable; // make sure this doesn't get deleted before we are done + + // Based on flags, remove the drawable from the queues that it's on. + if (drawablep->isState(LLDrawable::ON_MOVE_LIST)) + { + LLDrawable::drawable_vector_t::iterator iter = std::find(mMovedList.begin(), mMovedList.end(), drawablep); + if (iter != mMovedList.end()) + { + mMovedList.erase(iter); + } + } + + if (drawablep->getSpatialGroup()) + { + if (!drawablep->getSpatialGroup()->mSpatialPartition->remove(drawablep, drawablep->getSpatialGroup())) + { +#ifdef LL_RELEASE_FOR_DOWNLOAD + llwarns << "Couldn't remove object from spatial group!" << llendl; +#else + llerrs << "Couldn't remove object from spatial group!" << llendl; +#endif + } + } + + mLights.erase(drawablep); + for (light_set_t::iterator iter = mNearbyLights.begin(); + iter != mNearbyLights.end(); iter++) + { + if (iter->drawable == drawablep) + { + mNearbyLights.erase(iter); + break; + } + } + + { + HighlightItem item(drawablep); + mHighlightSet.erase(item); + + if (mHighlightObject == drawablep) + { + mHighlightObject = NULL; + } + } + + for (U32 i = 0; i < 2; ++i) + { + if (mShadowSpotLight[i] == drawablep) + { + mShadowSpotLight[i] = NULL; + } + + if (mTargetShadowSpotLight[i] == drawablep) + { + mTargetShadowSpotLight[i] = NULL; + } + } + + +} + +U32 LLPipeline::addObject(LLViewerObject *vobj) +{ + LLMemType mt_ao(LLMemType::MTYPE_PIPELINE_ADD_OBJECT); + if (gNoRender) + { + return 0; + } + + if (gSavedSettings.getBOOL("RenderDelayCreation")) + { + mCreateQ.push_back(vobj); + } + else + { + createObject(vobj); + } + + return 1; +} + +void LLPipeline::createObjects(F32 max_dtime) +{ + LLFastTimer ftm(FTM_GEO_UPDATE); + LLMemType mt(LLMemType::MTYPE_PIPELINE_CREATE_OBJECTS); + + LLTimer update_timer; + + while (!mCreateQ.empty() && update_timer.getElapsedTimeF32() < max_dtime) + { + LLViewerObject* vobj = mCreateQ.front(); + if (!vobj->isDead()) + { + createObject(vobj); + } + mCreateQ.pop_front(); + } + + //for (LLViewerObject::vobj_list_t::iterator iter = mCreateQ.begin(); iter != mCreateQ.end(); ++iter) + //{ + // createObject(*iter); + //} + + //mCreateQ.clear(); +} + +void LLPipeline::createObject(LLViewerObject* vobj) +{ + LLDrawable* drawablep = vobj->mDrawable; + + if (!drawablep) + { + drawablep = vobj->createDrawable(this); + } + else + { + llerrs << "Redundant drawable creation!" << llendl; + } + + llassert(drawablep); + + if (vobj->getParent()) + { + vobj->setDrawableParent(((LLViewerObject*)vobj->getParent())->mDrawable); // LLPipeline::addObject 1 + } + else + { + vobj->setDrawableParent(NULL); // LLPipeline::addObject 2 + } + + markRebuild(drawablep, LLDrawable::REBUILD_ALL, TRUE); + + if (drawablep->getVOVolume() && gSavedSettings.getBOOL("RenderAnimateRes")) + { + // fun animated res + drawablep->updateXform(TRUE); + drawablep->clearState(LLDrawable::MOVE_UNDAMPED); + drawablep->setScale(LLVector3(0,0,0)); + drawablep->makeActive(); + } +} + + +void LLPipeline::resetFrameStats() +{ + assertInitialized(); + + LLViewerStats::getInstance()->mTrianglesDrawnStat.addValue(mTrianglesDrawn/1000.f); + + if (mBatchCount > 0) + { + mMeanBatchSize = gPipeline.mTrianglesDrawn/gPipeline.mBatchCount; + } + mTrianglesDrawn = 0; + sCompiles = 0; + mVerticesRelit = 0; + mLightingChanges = 0; + mGeometryChanges = 0; + mNumVisibleFaces = 0; + + if (mOldRenderDebugMask != mRenderDebugMask) + { + gObjectList.clearDebugText(); + mOldRenderDebugMask = mRenderDebugMask; + } + +} + +//external functions for asynchronous updating +void LLPipeline::updateMoveDampedAsync(LLDrawable* drawablep) +{ + if (gSavedSettings.getBOOL("FreezeTime")) + { + return; + } + if (!drawablep) + { + llerrs << "updateMove called with NULL drawablep" << llendl; + return; + } + if (drawablep->isState(LLDrawable::EARLY_MOVE)) + { + return; + } + + assertInitialized(); + + // update drawable now + drawablep->clearState(LLDrawable::MOVE_UNDAMPED); // force to DAMPED + drawablep->updateMove(); // returns done + drawablep->setState(LLDrawable::EARLY_MOVE); // flag says we already did an undamped move this frame + // Put on move list so that EARLY_MOVE gets cleared + if (!drawablep->isState(LLDrawable::ON_MOVE_LIST)) + { + mMovedList.push_back(drawablep); + drawablep->setState(LLDrawable::ON_MOVE_LIST); + } +} + +void LLPipeline::updateMoveNormalAsync(LLDrawable* drawablep) +{ + if (gSavedSettings.getBOOL("FreezeTime")) + { + return; + } + if (!drawablep) + { + llerrs << "updateMove called with NULL drawablep" << llendl; + return; + } + if (drawablep->isState(LLDrawable::EARLY_MOVE)) + { + return; + } + + assertInitialized(); + + // update drawable now + drawablep->setState(LLDrawable::MOVE_UNDAMPED); // force to UNDAMPED + drawablep->updateMove(); + drawablep->setState(LLDrawable::EARLY_MOVE); // flag says we already did an undamped move this frame + // Put on move list so that EARLY_MOVE gets cleared + if (!drawablep->isState(LLDrawable::ON_MOVE_LIST)) + { + mMovedList.push_back(drawablep); + drawablep->setState(LLDrawable::ON_MOVE_LIST); + } +} + +void LLPipeline::updateMovedList(LLDrawable::drawable_vector_t& moved_list) +{ + for (LLDrawable::drawable_vector_t::iterator iter = moved_list.begin(); + iter != moved_list.end(); ) + { + LLDrawable::drawable_vector_t::iterator curiter = iter++; + LLDrawable *drawablep = *curiter; + BOOL done = TRUE; + if (!drawablep->isDead() && (!drawablep->isState(LLDrawable::EARLY_MOVE))) + { + done = drawablep->updateMove(); + } + drawablep->clearState(LLDrawable::EARLY_MOVE | LLDrawable::MOVE_UNDAMPED); + if (done) + { + drawablep->clearState(LLDrawable::ON_MOVE_LIST); + iter = moved_list.erase(curiter); + } + } +} + +static LLFastTimer::DeclareTimer FTM_OCTREE_BALANCE("Balance Octree"); +static LLFastTimer::DeclareTimer FTM_UPDATE_MOVE("Update Move"); + +void LLPipeline::updateMove() +{ + LLFastTimer t(FTM_UPDATE_MOVE); + LLMemType mt_um(LLMemType::MTYPE_PIPELINE_UPDATE_MOVE); + + if (gSavedSettings.getBOOL("FreezeTime")) + { + return; + } + + assertInitialized(); + + { + static LLFastTimer::DeclareTimer ftm("Retexture"); + LLFastTimer t(ftm); + + for (LLDrawable::drawable_set_t::iterator iter = mRetexturedList.begin(); + iter != mRetexturedList.end(); ++iter) + { + LLDrawable* drawablep = *iter; + if (drawablep && !drawablep->isDead()) + { + drawablep->updateTexture(); + } + } + mRetexturedList.clear(); + } + + { + static LLFastTimer::DeclareTimer ftm("Moved List"); + LLFastTimer t(ftm); + updateMovedList(mMovedList); + } + + //balance octrees + { + LLFastTimer ot(FTM_OCTREE_BALANCE); + + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) + { + LLSpatialPartition* part = region->getSpatialPartition(i); + if (part) + { + part->mOctree->balance(); + } + } + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// Culling and occlusion testing +///////////////////////////////////////////////////////////////////////////// + +//static +F32 LLPipeline::calcPixelArea(LLVector3 center, LLVector3 size, LLCamera &camera) +{ + LLVector3 lookAt = center - camera.getOrigin(); + F32 dist = lookAt.length(); + + //ramp down distance for nearby objects + //shrink dist by dist/16. + if (dist < 16.f) + { + dist /= 16.f; + dist *= dist; + dist *= 16.f; + } + + //get area of circle around node + F32 app_angle = atanf(size.length()/dist); + F32 radius = app_angle*LLDrawable::sCurPixelAngle; + return radius*radius * F_PI; +} + +void LLPipeline::grabReferences(LLCullResult& result) +{ + sCull = &result; +} + +BOOL LLPipeline::visibleObjectsInFrustum(LLCamera& camera) +{ + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + + for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) + { + LLSpatialPartition* part = region->getSpatialPartition(i); + if (part) + { + if (hasRenderType(part->mDrawableType)) + { + if (part->visibleObjectsInFrustum(camera)) + { + return TRUE; + } + } + } + } + } + + return FALSE; +} + +BOOL LLPipeline::getVisibleExtents(LLCamera& camera, LLVector3& min, LLVector3& max) +{ + const F32 X = 65536.f; + + min = LLVector3(X,X,X); + max = LLVector3(-X,-X,-X); + + U32 saved_camera_id = LLViewerCamera::sCurCameraID; + LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD; + + BOOL res = TRUE; + + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + + for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) + { + LLSpatialPartition* part = region->getSpatialPartition(i); + if (part) + { + if (hasRenderType(part->mDrawableType)) + { + if (!part->getVisibleExtents(camera, min, max)) + { + res = FALSE; + } + } + } + } + } + + LLViewerCamera::sCurCameraID = saved_camera_id; + + return res; +} + +static LLFastTimer::DeclareTimer FTM_CULL("Object Culling"); + +void LLPipeline::updateCull(LLCamera& camera, LLCullResult& result, S32 water_clip) +{ + LLFastTimer t(FTM_CULL); + LLMemType mt_uc(LLMemType::MTYPE_PIPELINE_UPDATE_CULL); + + grabReferences(result); + + sCull->clear(); + + BOOL to_texture = LLPipeline::sUseOcclusion > 1 && + !hasRenderType(LLPipeline::RENDER_TYPE_HUD) && + LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD && + gPipeline.canUseVertexShaders() && + sRenderGlow; + + if (to_texture) + { + mScreen.bindTarget(); + } + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixd(gGLLastProjection); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + gGLLastMatrix = NULL; + glLoadMatrixd(gGLLastModelView); + + + LLVertexBuffer::unbind(); + LLGLDisable blend(GL_BLEND); + LLGLDisable test(GL_ALPHA_TEST); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + if (sUseOcclusion > 1) + { + gGL.setColorMask(false, false); + } + + LLGLDepthTest depth(GL_TRUE, GL_FALSE); + + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + if (water_clip != 0) + { + LLPlane plane(LLVector3(0,0, (F32) -water_clip), (F32) water_clip*region->getWaterHeight()); + camera.setUserClipPlane(plane); + } + else + { + camera.disableUserClipPlane(); + } + + for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) + { + LLSpatialPartition* part = region->getSpatialPartition(i); + if (part) + { + if (hasRenderType(part->mDrawableType)) + { + part->cull(camera); + } + } + } + } + + camera.disableUserClipPlane(); + + if (hasRenderType(LLPipeline::RENDER_TYPE_SKY) && + gSky.mVOSkyp.notNull() && + gSky.mVOSkyp->mDrawable.notNull()) + { + gSky.mVOSkyp->mDrawable->setVisible(camera); + sCull->pushDrawable(gSky.mVOSkyp->mDrawable); + gSky.updateCull(); + stop_glerror(); + } + + if (hasRenderType(LLPipeline::RENDER_TYPE_GROUND) && + !gPipeline.canUseWindLightShaders() && + gSky.mVOGroundp.notNull() && + gSky.mVOGroundp->mDrawable.notNull() && + !LLPipeline::sWaterReflections) + { + gSky.mVOGroundp->mDrawable->setVisible(camera); + sCull->pushDrawable(gSky.mVOGroundp->mDrawable); + } + + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + if (sUseOcclusion > 1) + { + gGL.setColorMask(true, false); + } + + if (to_texture) + { + mScreen.flush(); + } +} + +void LLPipeline::markNotCulled(LLSpatialGroup* group, LLCamera& camera) +{ + if (group->getData().empty()) + { + return; + } + + group->setVisible(); + + if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD) + { + group->updateDistance(camera); + } + + const F32 MINIMUM_PIXEL_AREA = 16.f; + + if (group->mPixelArea < MINIMUM_PIXEL_AREA) + { + return; + } + + if (sMinRenderSize > 0.f && + llmax(llmax(group->mBounds[1].mV[0], group->mBounds[1].mV[1]), group->mBounds[1].mV[2]) < sMinRenderSize) + { + return; + } + + assertInitialized(); + + if (!group->mSpatialPartition->mRenderByGroup) + { //render by drawable + sCull->pushDrawableGroup(group); + } + else + { //render by group + sCull->pushVisibleGroup(group); + } + + mNumVisibleNodes++; +} + +void LLPipeline::markOccluder(LLSpatialGroup* group) +{ + if (sUseOcclusion > 1 && group && !group->isOcclusionState(LLSpatialGroup::ACTIVE_OCCLUSION)) + { + LLSpatialGroup* parent = group->getParent(); + + if (!parent || !parent->isOcclusionState(LLSpatialGroup::OCCLUDED)) + { //only mark top most occluders as active occlusion + sCull->pushOcclusionGroup(group); + group->setOcclusionState(LLSpatialGroup::ACTIVE_OCCLUSION); + + if (parent && + !parent->isOcclusionState(LLSpatialGroup::ACTIVE_OCCLUSION) && + parent->getElementCount() == 0 && + parent->needsUpdate()) + { + sCull->pushOcclusionGroup(group); + parent->setOcclusionState(LLSpatialGroup::ACTIVE_OCCLUSION); + } + } + } +} + +void LLPipeline::doOcclusion(LLCamera& camera) +{ + LLVertexBuffer::unbind(); + + if (hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION)) + { + gGL.setColorMask(true, false, false, false); + } + else + { + gGL.setColorMask(false, false); + } + LLGLDisable blend(GL_BLEND); + LLGLDisable test(GL_ALPHA_TEST); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + LLGLDepthTest depth(GL_TRUE, GL_FALSE); + + LLGLDisable cull(GL_CULL_FACE); + if (LLPipeline::sUseOcclusion > 1) + { + for (LLCullResult::sg_list_t::iterator iter = sCull->beginOcclusionGroups(); iter != sCull->endOcclusionGroups(); ++iter) + { + LLSpatialGroup* group = *iter; + group->doOcclusion(&camera); + group->clearOcclusionState(LLSpatialGroup::ACTIVE_OCCLUSION); + } + } + + gGL.setColorMask(true, false); +} + +BOOL LLPipeline::updateDrawableGeom(LLDrawable* drawablep, BOOL priority) +{ + BOOL update_complete = drawablep->updateGeometry(priority); + if (update_complete && assertInitialized()) + { + drawablep->setState(LLDrawable::BUILT); + mGeometryChanges++; + } + return update_complete; +} + +void LLPipeline::updateGL() +{ + while (!LLGLUpdate::sGLQ.empty()) + { + LLGLUpdate* glu = LLGLUpdate::sGLQ.front(); + glu->updateGL(); + glu->mInQ = FALSE; + LLGLUpdate::sGLQ.pop_front(); + } +} + +void LLPipeline::rebuildPriorityGroups() +{ + if(!sAllowRebuildPriorityGroup) + { + return ; + } + sAllowRebuildPriorityGroup = FALSE ; + + LLTimer update_timer; + LLMemType mt(LLMemType::MTYPE_PIPELINE); + + assertInitialized(); + + // Iterate through all drawables on the priority build queue, + for (LLSpatialGroup::sg_vector_t::iterator iter = mGroupQ1.begin(); + iter != mGroupQ1.end(); ++iter) + { + LLSpatialGroup* group = *iter; + group->rebuildGeom(); + group->clearState(LLSpatialGroup::IN_BUILD_Q1); + } + + mGroupQ1.clear(); +} + +void LLPipeline::rebuildGroups() +{ + // Iterate through some drawables on the non-priority build queue + S32 size = (S32) mGroupQ2.size(); + S32 min_count = llclamp((S32) ((F32) (size * size)/4096*0.25f), 1, size); + + S32 count = 0; + + std::sort(mGroupQ2.begin(), mGroupQ2.end(), LLSpatialGroup::CompareUpdateUrgency()); + + LLSpatialGroup::sg_vector_t::iterator iter; + for (iter = mGroupQ2.begin(); + iter != mGroupQ2.end(); ++iter) + { + LLSpatialGroup* group = *iter; + + if (group->isDead()) + { + continue; + } + + group->rebuildGeom(); + + if (group->mSpatialPartition->mRenderByGroup) + { + count++; + } + + group->clearState(LLSpatialGroup::IN_BUILD_Q2); + + if (count > min_count) + { + ++iter; + break; + } + } + + mGroupQ2.erase(mGroupQ2.begin(), iter); + + updateMovedList(mMovedBridge); +} + +void LLPipeline::updateGeom(F32 max_dtime) +{ + LLTimer update_timer; + LLMemType mt(LLMemType::MTYPE_PIPELINE_UPDATE_GEOM); + LLPointer drawablep; + + LLFastTimer t(FTM_GEO_UPDATE); + + assertInitialized(); + + if (sDelayedVBOEnable > 0) + { + if (--sDelayedVBOEnable <= 0) + { + resetVertexBuffers(); + LLVertexBuffer::sEnableVBOs = TRUE; + } + } + + // notify various object types to reset internal cost metrics, etc. + // for now, only LLVOVolume does this to throttle LOD changes + LLVOVolume::preUpdateGeom(); + + // Iterate through all drawables on the priority build queue, + for (LLDrawable::drawable_list_t::iterator iter = mBuildQ1.begin(); + iter != mBuildQ1.end();) + { + LLDrawable::drawable_list_t::iterator curiter = iter++; + LLDrawable* drawablep = *curiter; + if (drawablep && !drawablep->isDead()) + { + if (drawablep->isState(LLDrawable::IN_REBUILD_Q2)) + { + drawablep->clearState(LLDrawable::IN_REBUILD_Q2); + LLDrawable::drawable_list_t::iterator find = std::find(mBuildQ2.begin(), mBuildQ2.end(), drawablep); + if (find != mBuildQ2.end()) + { + mBuildQ2.erase(find); + } + } + + if (updateDrawableGeom(drawablep, TRUE)) + { + drawablep->clearState(LLDrawable::IN_REBUILD_Q1); + mBuildQ1.erase(curiter); + } + } + else + { + mBuildQ1.erase(curiter); + } + } + + // Iterate through some drawables on the non-priority build queue + S32 min_count = 16; + S32 size = (S32) mBuildQ2.size(); + if (size > 1024) + { + min_count = llclamp((S32) (size * (F32) size/4096), 16, size); + } + + S32 count = 0; + + max_dtime = llmax(update_timer.getElapsedTimeF32()+0.001f, max_dtime); + LLSpatialGroup* last_group = NULL; + LLSpatialBridge* last_bridge = NULL; + + for (LLDrawable::drawable_list_t::iterator iter = mBuildQ2.begin(); + iter != mBuildQ2.end(); ) + { + LLDrawable::drawable_list_t::iterator curiter = iter++; + LLDrawable* drawablep = *curiter; + + LLSpatialBridge* bridge = drawablep->isRoot() ? drawablep->getSpatialBridge() : + drawablep->getParent()->getSpatialBridge(); + + if (drawablep->getSpatialGroup() != last_group && + (!last_bridge || bridge != last_bridge) && + (update_timer.getElapsedTimeF32() >= max_dtime) && count > min_count) + { + break; + } + + //make sure updates don't stop in the middle of a spatial group + //to avoid thrashing (objects are enqueued by group) + last_group = drawablep->getSpatialGroup(); + last_bridge = bridge; + + BOOL update_complete = TRUE; + if (!drawablep->isDead()) + { + update_complete = updateDrawableGeom(drawablep, FALSE); + count++; + } + if (update_complete) + { + drawablep->clearState(LLDrawable::IN_REBUILD_Q2); + mBuildQ2.erase(curiter); + } + } + + updateMovedList(mMovedBridge); +} + +void LLPipeline::markVisible(LLDrawable *drawablep, LLCamera& camera) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_MARK_VISIBLE); + + if(drawablep && !drawablep->isDead()) + { + if (drawablep->isSpatialBridge()) + { + const LLDrawable* root = ((LLSpatialBridge*) drawablep)->mDrawable; + llassert(root); // trying to catch a bad assumption + if (root && // // this test may not be needed, see above + root->getVObj()->isAttachment()) + { + LLDrawable* rootparent = root->getParent(); + if (rootparent) // this IS sometimes NULL + { + LLViewerObject *vobj = rootparent->getVObj(); + llassert(vobj); // trying to catch a bad assumption + if (vobj) // this test may not be needed, see above + { + const LLVOAvatar* av = vobj->asAvatar(); + if (av && av->isImpostor()) + { + return; + } + } + } + } + sCull->pushBridge((LLSpatialBridge*) drawablep); + } + else + { + sCull->pushDrawable(drawablep); + } + + drawablep->setVisible(camera); + } +} + +void LLPipeline::markMoved(LLDrawable *drawablep, BOOL damped_motion) +{ + LLMemType mt_mm(LLMemType::MTYPE_PIPELINE_MARK_MOVED); + + if (!drawablep) + { + //llerrs << "Sending null drawable to moved list!" << llendl; + return; + } + + if (drawablep->isDead()) + { + llwarns << "Marking NULL or dead drawable moved!" << llendl; + return; + } + + if (drawablep->getParent()) + { + //ensure that parent drawables are moved first + markMoved(drawablep->getParent(), damped_motion); + } + + assertInitialized(); + + if (!drawablep->isState(LLDrawable::ON_MOVE_LIST)) + { + if (drawablep->isSpatialBridge()) + { + mMovedBridge.push_back(drawablep); + } + else + { + mMovedList.push_back(drawablep); + } + drawablep->setState(LLDrawable::ON_MOVE_LIST); + } + if (damped_motion == FALSE) + { + drawablep->setState(LLDrawable::MOVE_UNDAMPED); // UNDAMPED trumps DAMPED + } + else if (drawablep->isState(LLDrawable::MOVE_UNDAMPED)) + { + drawablep->clearState(LLDrawable::MOVE_UNDAMPED); + } +} + +void LLPipeline::markShift(LLDrawable *drawablep) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_MARK_SHIFT); + + if (!drawablep || drawablep->isDead()) + { + return; + } + + assertInitialized(); + + if (!drawablep->isState(LLDrawable::ON_SHIFT_LIST)) + { + drawablep->getVObj()->setChanged(LLXform::SHIFTED | LLXform::SILHOUETTE); + if (drawablep->getParent()) + { + markShift(drawablep->getParent()); + } + mShiftList.push_back(drawablep); + drawablep->setState(LLDrawable::ON_SHIFT_LIST); + } +} + +void LLPipeline::shiftObjects(const LLVector3 &offset) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_SHIFT_OBJECTS); + + assertInitialized(); + + glClear(GL_DEPTH_BUFFER_BIT); + gDepthDirty = TRUE; + + for (LLDrawable::drawable_vector_t::iterator iter = mShiftList.begin(); + iter != mShiftList.end(); iter++) + { + LLDrawable *drawablep = *iter; + if (drawablep->isDead()) + { + continue; + } + drawablep->shiftPos(offset); + drawablep->clearState(LLDrawable::ON_SHIFT_LIST); + } + mShiftList.resize(0); + + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) + { + LLSpatialPartition* part = region->getSpatialPartition(i); + if (part) + { + part->shift(offset); + } + } + } + + LLHUDText::shiftAll(offset); + LLHUDNameTag::shiftAll(offset); + display_update_camera(); +} + +void LLPipeline::markTextured(LLDrawable *drawablep) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_MARK_TEXTURED); + + if (drawablep && !drawablep->isDead() && assertInitialized()) + { + mRetexturedList.insert(drawablep); + } +} + +void LLPipeline::markGLRebuild(LLGLUpdate* glu) +{ + if (glu && !glu->mInQ) + { + LLGLUpdate::sGLQ.push_back(glu); + glu->mInQ = TRUE; + } +} + +void LLPipeline::markRebuild(LLSpatialGroup* group, BOOL priority) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE); + //assert_main_thread(); + + if (group && !group->isDead() && group->mSpatialPartition) + { + if (group->mSpatialPartition->mPartitionType == LLViewerRegion::PARTITION_HUD) + { + priority = TRUE; + } + + if (priority) + { + if (!group->isState(LLSpatialGroup::IN_BUILD_Q1)) + { + mGroupQ1.push_back(group); + group->setState(LLSpatialGroup::IN_BUILD_Q1); + + if (group->isState(LLSpatialGroup::IN_BUILD_Q2)) + { + LLSpatialGroup::sg_vector_t::iterator iter = std::find(mGroupQ2.begin(), mGroupQ2.end(), group); + if (iter != mGroupQ2.end()) + { + mGroupQ2.erase(iter); + } + group->clearState(LLSpatialGroup::IN_BUILD_Q2); + } + } + } + else if (!group->isState(LLSpatialGroup::IN_BUILD_Q2 | LLSpatialGroup::IN_BUILD_Q1)) + { + //llerrs << "Non-priority updates not yet supported!" << llendl; + if (std::find(mGroupQ2.begin(), mGroupQ2.end(), group) != mGroupQ2.end()) + { + llerrs << "WTF?" << llendl; + } + mGroupQ2.push_back(group); + group->setState(LLSpatialGroup::IN_BUILD_Q2); + + } + } +} + +void LLPipeline::markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags flag, BOOL priority) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_MARK_REBUILD); + + if (drawablep && !drawablep->isDead() && assertInitialized()) + { + if (!drawablep->isState(LLDrawable::BUILT)) + { + priority = TRUE; + } + if (priority) + { + if (!drawablep->isState(LLDrawable::IN_REBUILD_Q1)) + { + mBuildQ1.push_back(drawablep); + drawablep->setState(LLDrawable::IN_REBUILD_Q1); // mark drawable as being in priority queue + } + } + else if (!drawablep->isState(LLDrawable::IN_REBUILD_Q2)) + { + mBuildQ2.push_back(drawablep); + drawablep->setState(LLDrawable::IN_REBUILD_Q2); // need flag here because it is just a list + } + if (flag & (LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION)) + { + drawablep->getVObj()->setChanged(LLXform::SILHOUETTE); + } + drawablep->setState(flag); + } +} + +static LLFastTimer::DeclareTimer FTM_RESET_DRAWORDER("Reset Draw Order"); + +void LLPipeline::stateSort(LLCamera& camera, LLCullResult &result) +{ + if (hasAnyRenderType(LLPipeline::RENDER_TYPE_AVATAR, + LLPipeline::RENDER_TYPE_GROUND, + LLPipeline::RENDER_TYPE_TERRAIN, + LLPipeline::RENDER_TYPE_TREE, + LLPipeline::RENDER_TYPE_SKY, + LLPipeline::RENDER_TYPE_VOIDWATER, + LLPipeline::RENDER_TYPE_WATER, + LLPipeline::END_RENDER_TYPES)) + { + //clear faces from face pools + LLFastTimer t(FTM_RESET_DRAWORDER); + gPipeline.resetDrawOrders(); + } + + LLFastTimer ftm(FTM_STATESORT); + LLMemType mt(LLMemType::MTYPE_PIPELINE_STATE_SORT); + + //LLVertexBuffer::unbind(); + + grabReferences(result); + for (LLCullResult::sg_list_t::iterator iter = sCull->beginDrawableGroups(); iter != sCull->endDrawableGroups(); ++iter) + { + LLSpatialGroup* group = *iter; + group->checkOcclusion(); + if (sUseOcclusion > 1 && group->isOcclusionState(LLSpatialGroup::OCCLUDED)) + { + markOccluder(group); + } + else + { + group->setVisible(); + for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i) + { + markVisible(*i, camera); + } + } + } + for (LLCullResult::sg_list_t::iterator iter = sCull->beginVisibleGroups(); iter != sCull->endVisibleGroups(); ++iter) + { + LLSpatialGroup* group = *iter; + group->checkOcclusion(); + if (sUseOcclusion > 1 && group->isOcclusionState(LLSpatialGroup::OCCLUDED)) + { + markOccluder(group); + } + else + { + group->setVisible(); + stateSort(group, camera); + } + } + + if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD) + { + for (LLCullResult::bridge_list_t::iterator i = sCull->beginVisibleBridge(); i != sCull->endVisibleBridge(); ++i) + { + LLCullResult::bridge_list_t::iterator cur_iter = i; + LLSpatialBridge* bridge = *cur_iter; + LLSpatialGroup* group = bridge->getSpatialGroup(); + if (!bridge->isDead() && group && !group->isOcclusionState(LLSpatialGroup::OCCLUDED)) + { + stateSort(bridge, camera); + } + } + } + { + LLFastTimer ftm(FTM_STATESORT_DRAWABLE); + for (LLCullResult::drawable_list_t::iterator iter = sCull->beginVisibleList(); + iter != sCull->endVisibleList(); ++iter) + { + LLDrawable *drawablep = *iter; + if (!drawablep->isDead()) + { + stateSort(drawablep, camera); + } + } + } + { + LLFastTimer ftm(FTM_CLIENT_COPY); + LLVertexBuffer::clientCopy(); + } + + postSort(camera); +} + +void LLPipeline::stateSort(LLSpatialGroup* group, LLCamera& camera) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_STATE_SORT); + if (group->changeLOD()) + { + for (LLSpatialGroup::element_iter i = group->getData().begin(); i != group->getData().end(); ++i) + { + LLDrawable* drawablep = *i; + stateSort(drawablep, camera); + } + } + +} + +void LLPipeline::stateSort(LLSpatialBridge* bridge, LLCamera& camera) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_STATE_SORT); + if (!sShadowRender && bridge->getSpatialGroup()->changeLOD()) + { + bool force_update = false; + bridge->updateDistance(camera, force_update); + } +} + +void LLPipeline::stateSort(LLDrawable* drawablep, LLCamera& camera) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_STATE_SORT); + + if (!drawablep + || drawablep->isDead() + || !hasRenderType(drawablep->getRenderType())) + { + return; + } + + if (LLSelectMgr::getInstance()->mHideSelectedObjects) + { + if (drawablep->getVObj().notNull() && + drawablep->getVObj()->isSelected()) + { + return; + } + } + + if (drawablep->isAvatar()) + { //don't draw avatars beyond render distance or if we don't have a spatial group. + if ((drawablep->getSpatialGroup() == NULL) || + (drawablep->getSpatialGroup()->mDistance > LLVOAvatar::sRenderDistance)) + { + return; + } + + LLVOAvatar* avatarp = (LLVOAvatar*) drawablep->getVObj().get(); + if (!avatarp->isVisible()) + { + return; + } + } + + assertInitialized(); + + if (hasRenderType(drawablep->mRenderType)) + { + if (!drawablep->isState(LLDrawable::INVISIBLE|LLDrawable::FORCE_INVISIBLE)) + { + drawablep->setVisible(camera, NULL, FALSE); + } + else if (drawablep->isState(LLDrawable::CLEAR_INVISIBLE)) + { + // clear invisible flag here to avoid single frame glitch + drawablep->clearState(LLDrawable::FORCE_INVISIBLE|LLDrawable::CLEAR_INVISIBLE); + } + } + + if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD) + { + LLSpatialGroup* group = drawablep->getSpatialGroup(); + if (!group || group->changeLOD()) + { + if (drawablep->isVisible()) + { + if (!drawablep->isActive()) + { + bool force_update = false; + drawablep->updateDistance(camera, force_update); + } + else if (drawablep->isAvatar()) + { + bool force_update = false; + drawablep->updateDistance(camera, force_update); // calls vobj->updateLOD() which calls LLVOAvatar::updateVisibility() + } + } + } + } + + if (!drawablep->getVOVolume()) + { + for (LLDrawable::face_list_t::iterator iter = drawablep->mFaces.begin(); + iter != drawablep->mFaces.end(); iter++) + { + LLFace* facep = *iter; + + if (facep->hasGeometry()) + { + if (facep->getPool()) + { + facep->getPool()->enqueue(facep); + } + else + { + break; + } + } + } + } + + + mNumVisibleFaces += drawablep->getNumFaces(); +} + + +void forAllDrawables(LLCullResult::sg_list_t::iterator begin, + LLCullResult::sg_list_t::iterator end, + void (*func)(LLDrawable*)) +{ + for (LLCullResult::sg_list_t::iterator i = begin; i != end; ++i) + { + for (LLSpatialGroup::element_iter j = (*i)->getData().begin(); j != (*i)->getData().end(); ++j) + { + func(*j); + } + } +} + +void LLPipeline::forAllVisibleDrawables(void (*func)(LLDrawable*)) +{ + forAllDrawables(sCull->beginDrawableGroups(), sCull->endDrawableGroups(), func); + forAllDrawables(sCull->beginVisibleGroups(), sCull->endVisibleGroups(), func); +} + +//function for creating scripted beacons +void renderScriptedBeacons(LLDrawable* drawablep) +{ + LLViewerObject *vobj = drawablep->getVObj(); + if (vobj + && !vobj->isAvatar() + && !vobj->getParent() + && vobj->flagScripted()) + { + if (gPipeline.sRenderBeacons) + { + gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(1.f, 0.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f), gSavedSettings.getS32("DebugBeaconLineWidth")); + } + + if (gPipeline.sRenderHighlight) + { + S32 face_id; + S32 count = drawablep->getNumFaces(); + for (face_id = 0; face_id < count; face_id++) + { + gPipeline.mHighlightFaces.push_back(drawablep->getFace(face_id) ); + } + } + } +} + +void renderScriptedTouchBeacons(LLDrawable* drawablep) +{ + LLViewerObject *vobj = drawablep->getVObj(); + if (vobj + && !vobj->isAvatar() + && !vobj->getParent() + && vobj->flagScripted() + && vobj->flagHandleTouch()) + { + if (gPipeline.sRenderBeacons) + { + gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(1.f, 0.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f), gSavedSettings.getS32("DebugBeaconLineWidth")); + } + + if (gPipeline.sRenderHighlight) + { + S32 face_id; + S32 count = drawablep->getNumFaces(); + for (face_id = 0; face_id < count; face_id++) + { + gPipeline.mHighlightFaces.push_back(drawablep->getFace(face_id) ); + } + } + } +} + +void renderPhysicalBeacons(LLDrawable* drawablep) +{ + LLViewerObject *vobj = drawablep->getVObj(); + if (vobj + && !vobj->isAvatar() + //&& !vobj->getParent() + && vobj->usePhysics()) + { + if (gPipeline.sRenderBeacons) + { + gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(0.f, 1.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f), gSavedSettings.getS32("DebugBeaconLineWidth")); + } + + if (gPipeline.sRenderHighlight) + { + S32 face_id; + S32 count = drawablep->getNumFaces(); + for (face_id = 0; face_id < count; face_id++) + { + gPipeline.mHighlightFaces.push_back(drawablep->getFace(face_id) ); + } + } + } +} + +void renderParticleBeacons(LLDrawable* drawablep) +{ + // Look for attachments, objects, etc. + LLViewerObject *vobj = drawablep->getVObj(); + if (vobj + && vobj->isParticleSource()) + { + if (gPipeline.sRenderBeacons) + { + LLColor4 light_blue(0.5f, 0.5f, 1.f, 0.5f); + gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", light_blue, LLColor4(1.f, 1.f, 1.f, 0.5f), gSavedSettings.getS32("DebugBeaconLineWidth")); + } + + if (gPipeline.sRenderHighlight) + { + S32 face_id; + S32 count = drawablep->getNumFaces(); + for (face_id = 0; face_id < count; face_id++) + { + gPipeline.mHighlightFaces.push_back(drawablep->getFace(face_id) ); + } + } + } +} + +void renderSoundHighlights(LLDrawable* drawablep) +{ + // Look for attachments, objects, etc. + LLViewerObject *vobj = drawablep->getVObj(); + if (vobj && vobj->isAudioSource()) + { + if (gPipeline.sRenderHighlight) + { + S32 face_id; + S32 count = drawablep->getNumFaces(); + for (face_id = 0; face_id < count; face_id++) + { + gPipeline.mHighlightFaces.push_back(drawablep->getFace(face_id) ); + } + } + } +} + +void LLPipeline::postSort(LLCamera& camera) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_POST_SORT); + LLFastTimer ftm(FTM_STATESORT_POSTSORT); + + assertInitialized(); + + llpushcallstacks ; + //rebuild drawable geometry + for (LLCullResult::sg_list_t::iterator i = sCull->beginDrawableGroups(); i != sCull->endDrawableGroups(); ++i) + { + LLSpatialGroup* group = *i; + if (!sUseOcclusion || + !group->isOcclusionState(LLSpatialGroup::OCCLUDED)) + { + group->rebuildGeom(); + } + } + llpushcallstacks ; + //rebuild groups + sCull->assertDrawMapsEmpty(); + + /*LLSpatialGroup::sNoDelete = FALSE; + for (LLCullResult::sg_list_t::iterator i = sCull->beginVisibleGroups(); i != sCull->endVisibleGroups(); ++i) + { + LLSpatialGroup* group = *i; + if (sUseOcclusion && + group->isState(LLSpatialGroup::OCCLUDED)) + { + continue; + } + + group->rebuildGeom(); + } + LLSpatialGroup::sNoDelete = TRUE;*/ + + + rebuildPriorityGroups(); + llpushcallstacks ; + + const S32 bin_count = 1024*8; + + static LLCullResult::drawinfo_list_t alpha_bins[bin_count]; + static U32 bin_size[bin_count]; + + //clear one bin per frame to avoid memory bloat + static S32 clear_idx = 0; + clear_idx = (1+clear_idx)%bin_count; + alpha_bins[clear_idx].clear(); + + for (U32 j = 0; j < bin_count; j++) + { + bin_size[j] = 0; + } + + //build render map + for (LLCullResult::sg_list_t::iterator i = sCull->beginVisibleGroups(); i != sCull->endVisibleGroups(); ++i) + { + LLSpatialGroup* group = *i; + if (sUseOcclusion && + group->isOcclusionState(LLSpatialGroup::OCCLUDED)) + { + continue; + } + + if (group->isState(LLSpatialGroup::NEW_DRAWINFO) && group->isState(LLSpatialGroup::GEOM_DIRTY)) + { //no way this group is going to be drawable without a rebuild + group->rebuildGeom(); + } + + for (LLSpatialGroup::draw_map_t::iterator j = group->mDrawMap.begin(); j != group->mDrawMap.end(); ++j) + { + LLSpatialGroup::drawmap_elem_t& src_vec = j->second; + if (!hasRenderType(j->first)) + { + continue; + } + + for (LLSpatialGroup::drawmap_elem_t::iterator k = src_vec.begin(); k != src_vec.end(); ++k) + { + if (sMinRenderSize > 0.f) + { + LLVector3 bounds = (*k)->mExtents[1]-(*k)->mExtents[0]; + if (llmax(llmax(bounds.mV[0], bounds.mV[1]), bounds.mV[2]) > sMinRenderSize) + { + sCull->pushDrawInfo(j->first, *k); + } + } + else + { + sCull->pushDrawInfo(j->first, *k); + } + } + } + + if (hasRenderType(LLPipeline::RENDER_TYPE_PASS_ALPHA)) + { + LLSpatialGroup::draw_map_t::iterator alpha = group->mDrawMap.find(LLRenderPass::PASS_ALPHA); + + if (alpha != group->mDrawMap.end()) + { //store alpha groups for sorting + LLSpatialBridge* bridge = group->mSpatialPartition->asBridge(); + if (LLViewerCamera::sCurCameraID == LLViewerCamera::CAMERA_WORLD) + { + if (bridge) + { + LLCamera trans_camera = bridge->transformCamera(camera); + group->updateDistance(trans_camera); + } + else + { + group->updateDistance(camera); + } + } + + if (hasRenderType(LLDrawPool::POOL_ALPHA)) + { + sCull->pushAlphaGroup(group); + } + } + } + } + + if (!sShadowRender) + { + //sort by texture or bump map + for (U32 i = 0; i < LLRenderPass::NUM_RENDER_TYPES; ++i) + { + if (i == LLRenderPass::PASS_BUMP) + { + std::sort(sCull->beginRenderMap(i), sCull->endRenderMap(i), LLDrawInfo::CompareBump()); + } + else + { + std::sort(sCull->beginRenderMap(i), sCull->endRenderMap(i), LLDrawInfo::CompareTexturePtrMatrix()); + } + } + + std::sort(sCull->beginAlphaGroups(), sCull->endAlphaGroups(), LLSpatialGroup::CompareDepthGreater()); + } + llpushcallstacks ; + // only render if the flag is set. The flag is only set if we are in edit mode or the toggle is set in the menus + if (LLFloaterReg::instanceVisible("beacons") && !sShadowRender) + { + if (sRenderScriptedTouchBeacons) + { + // Only show the beacon on the root object. + forAllVisibleDrawables(renderScriptedTouchBeacons); + } + else + if (sRenderScriptedBeacons) + { + // Only show the beacon on the root object. + forAllVisibleDrawables(renderScriptedBeacons); + } + + if (sRenderPhysicalBeacons) + { + // Only show the beacon on the root object. + forAllVisibleDrawables(renderPhysicalBeacons); + } + + if (sRenderParticleBeacons) + { + forAllVisibleDrawables(renderParticleBeacons); + } + + // If god mode, also show audio cues + if (sRenderSoundBeacons && gAudiop) + { + // Walk all sound sources and render out beacons for them. Note, this isn't done in the ForAllVisibleDrawables function, because some are not visible. + LLAudioEngine::source_map::iterator iter; + for (iter = gAudiop->mAllSources.begin(); iter != gAudiop->mAllSources.end(); ++iter) + { + LLAudioSource *sourcep = iter->second; + + LLVector3d pos_global = sourcep->getPositionGlobal(); + LLVector3 pos = gAgent.getPosAgentFromGlobal(pos_global); + if (gPipeline.sRenderBeacons) + { + //pos += LLVector3(0.f, 0.f, 0.2f); + gObjectList.addDebugBeacon(pos, "", LLColor4(1.f, 1.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f), gSavedSettings.getS32("DebugBeaconLineWidth")); + } + } + // now deal with highlights for all those seeable sound sources + forAllVisibleDrawables(renderSoundHighlights); + } + } + llpushcallstacks ; + // If managing your telehub, draw beacons at telehub and currently selected spawnpoint. + if (LLFloaterTelehub::renderBeacons()) + { + LLFloaterTelehub::addBeacons(); + } + + if (!sShadowRender) + { + mSelectedFaces.clear(); + + // Draw face highlights for selected faces. + if (LLSelectMgr::getInstance()->getTEMode()) + { + struct f : public LLSelectedTEFunctor + { + virtual bool apply(LLViewerObject* object, S32 te) + { + if (object->mDrawable) + { + gPipeline.mSelectedFaces.push_back(object->mDrawable->getFace(te)); + } + return true; + } + } func; + LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func); + } + } + + //LLSpatialGroup::sNoDelete = FALSE; + llpushcallstacks ; +} + + +void render_hud_elements() +{ + LLMemType mt_rhe(LLMemType::MTYPE_PIPELINE_RENDER_HUD_ELS); + LLFastTimer t(FTM_RENDER_UI); + gPipeline.disableLights(); + + LLGLDisable fog(GL_FOG); + LLGLSUIDefault gls_ui; + + LLGLEnable stencil(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 255, 0xFFFFFFFF); + glStencilMask(0xFFFFFFFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + + gGL.color4f(1,1,1,1); + if (!LLPipeline::sReflectionRender && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + LLGLEnable multisample(GL_MULTISAMPLE_ARB); + gViewerWindow->renderSelections(FALSE, FALSE, FALSE); // For HUD version in render_ui_3d() + + // Draw the tracking overlays + LLTracker::render3D(); + + // Show the property lines + LLWorld::getInstance()->renderPropertyLines(); + LLViewerParcelMgr::getInstance()->render(); + LLViewerParcelMgr::getInstance()->renderParcelCollision(); + + // Render name tags. + LLHUDObject::renderAll(); + } + else if (gForceRenderLandFence) + { + // This is only set when not rendering the UI, for parcel snapshots + LLViewerParcelMgr::getInstance()->render(); + } + else if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD)) + { + LLHUDText::renderAllHUD(); + } + gGL.flush(); +} + +void LLPipeline::renderHighlights() +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_RENDER_HL); + + assertInitialized(); + + // Draw 3D UI elements here (before we clear the Z buffer in POOL_HUD) + // Render highlighted faces. + LLGLSPipelineAlpha gls_pipeline_alpha; + LLColor4 color(1.f, 1.f, 1.f, 0.5f); + LLGLEnable color_mat(GL_COLOR_MATERIAL); + disableLights(); + + if (!hasRenderType(LLPipeline::RENDER_TYPE_HUD) && !mHighlightSet.empty()) + { //draw blurry highlight image over screen + LLGLEnable blend(GL_BLEND); + LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); + LLGLDisable test(GL_ALPHA_TEST); + + LLGLEnable stencil(GL_STENCIL_TEST); + gGL.flush(); + glStencilMask(0xFFFFFFFF); + glClearStencil(1); + glClear(GL_STENCIL_BUFFER_BIT); + + glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFFF); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + + gGL.setColorMask(false, false); + for (std::set::iterator iter = mHighlightSet.begin(); iter != mHighlightSet.end(); ++iter) + { + renderHighlight(iter->mItem->getVObj(), 1.f); + } + gGL.setColorMask(true, false); + + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_NOTEQUAL, 0, 0xFFFFFFFF); + + //gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); + + gGL.pushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + gGL.pushMatrix(); + glLoadIdentity(); + + gGL.getTexUnit(0)->bind(&mHighlight); + + LLVector2 tc1; + LLVector2 tc2; + + tc1.setVec(0,0); + tc2.setVec(2,2); + + gGL.begin(LLRender::TRIANGLES); + + F32 scale = gSavedSettings.getF32("RenderHighlightBrightness"); + LLColor4 color = gSavedSettings.getColor4("RenderHighlightColor"); + F32 thickness = gSavedSettings.getF32("RenderHighlightThickness"); + + for (S32 pass = 0; pass < 2; ++pass) + { + if (pass == 0) + { + gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); + } + else + { + gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + + for (S32 i = 0; i < 8; ++i) + { + for (S32 j = 0; j < 8; ++j) + { + LLVector2 tc(i-4+0.5f, j-4+0.5f); + + F32 dist = 1.f-(tc.length()/sqrtf(32.f)); + dist *= scale/64.f; + + tc *= thickness; + tc.mV[0] = (tc.mV[0])/mHighlight.getWidth(); + tc.mV[1] = (tc.mV[1])/mHighlight.getHeight(); + + gGL.color4f(color.mV[0], + color.mV[1], + color.mV[2], + color.mV[3]*dist); + + gGL.texCoord2f(tc.mV[0]+tc1.mV[0], tc.mV[1]+tc2.mV[1]); + gGL.vertex2f(-1,3); + + gGL.texCoord2f(tc.mV[0]+tc1.mV[0], tc.mV[1]+tc1.mV[1]); + gGL.vertex2f(-1,-1); + + gGL.texCoord2f(tc.mV[0]+tc2.mV[0], tc.mV[1]+tc1.mV[1]); + gGL.vertex2f(3,-1); + } + } + } + + gGL.end(); + + gGL.popMatrix(); + glMatrixMode(GL_MODELVIEW); + gGL.popMatrix(); + + //gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + + if ((LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_INTERFACE) > 0)) + { + gHighlightProgram.bind(); + gHighlightProgram.vertexAttrib4f(LLViewerShaderMgr::MATERIAL_COLOR,1,1,1,0.5f); + } + + if (hasRenderDebugFeatureMask(RENDER_DEBUG_FEATURE_SELECTED)) + { + // Make sure the selection image gets downloaded and decoded + if (!mFaceSelectImagep) + { + mFaceSelectImagep = LLViewerTextureManager::getFetchedTexture(IMG_FACE_SELECT); + } + mFaceSelectImagep->addTextureStats((F32)MAX_IMAGE_AREA); + + U32 count = mSelectedFaces.size(); + for (U32 i = 0; i < count; i++) + { + LLFace *facep = mSelectedFaces[i]; + if (!facep || facep->getDrawable()->isDead()) + { + llerrs << "Bad face on selection" << llendl; + return; + } + + facep->renderSelected(mFaceSelectImagep, color); + } + } + + if (hasRenderDebugFeatureMask(RENDER_DEBUG_FEATURE_SELECTED)) + { + // Paint 'em red! + color.setVec(1.f, 0.f, 0.f, 0.5f); + if ((LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_INTERFACE) > 0)) + { + gHighlightProgram.vertexAttrib4f(LLViewerShaderMgr::MATERIAL_COLOR,1,0,0,0.5f); + } + int count = mHighlightFaces.size(); + for (S32 i = 0; i < count; i++) + { + LLFace* facep = mHighlightFaces[i]; + facep->renderSelected(LLViewerTexture::sNullImagep, color); + } + } + + // Contains a list of the faces of objects that are physical or + // have touch-handlers. + mHighlightFaces.clear(); + + if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_INTERFACE) > 0) + { + gHighlightProgram.unbind(); + } +} + +//debug use +U32 LLPipeline::sCurRenderPoolType = 0 ; + +void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_RENDER_GEOM); + LLFastTimer t(FTM_RENDER_GEOMETRY); + + assertInitialized(); + + F64 saved_modelview[16]; + F64 saved_projection[16]; + + //HACK: preserve/restore matrices around HUD render + if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD)) + { + for (U32 i = 0; i < 16; i++) + { + saved_modelview[i] = gGLModelView[i]; + saved_projection[i] = gGLProjection[i]; + } + } + + /////////////////////////////////////////// + // + // Sync and verify GL state + // + // + + stop_glerror(); + + LLVertexBuffer::unbind(); + + // Do verification of GL state + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + LLGLState::checkClientArrays(); + if (mRenderDebugMask & RENDER_DEBUG_VERIFY) + { + if (!verify()) + { + llerrs << "Pipeline verification failed!" << llendl; + } + } + + LLAppViewer::instance()->pingMainloopTimeout("Pipeline:ForceVBO"); + + // Initialize lots of GL state to "safe" values + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + + LLGLSPipeline gls_pipeline; + LLGLEnable multisample(GL_MULTISAMPLE_ARB); + + LLGLState gls_color_material(GL_COLOR_MATERIAL, mLightingDetail < 2); + + // Toggle backface culling for debugging + LLGLEnable cull_face(mBackfaceCull ? GL_CULL_FACE : 0); + // Set fog + BOOL use_fog = hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FOG); + LLGLEnable fog_enable(use_fog && + !gPipeline.canUseWindLightShadersOnObjects() ? GL_FOG : 0); + gSky.updateFog(camera.getFar()); + if (!use_fog) + { + sUnderWaterRender = FALSE; + } + + gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sDefaultImagep); + LLViewerFetchedTexture::sDefaultImagep->setAddressMode(LLTexUnit::TAM_WRAP); + + ////////////////////////////////////////////// + // + // Actually render all of the geometry + // + // + stop_glerror(); + + LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderDrawPools"); + + for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) + { + LLDrawPool *poolp = *iter; + if (hasRenderType(poolp->getType())) + { + poolp->prerender(); + } + } + + { + LLFastTimer t(FTM_POOLS); + + // HACK: don't calculate local lights if we're rendering the HUD! + // Removing this check will cause bad flickering when there are + // HUD elements being rendered AND the user is in flycam mode -nyx + if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD)) + { + calcNearbyLights(camera); + setupHWLights(NULL); + } + + BOOL occlude = sUseOcclusion > 1; + U32 cur_type = 0; + + pool_set_t::iterator iter1 = mPools.begin(); + while ( iter1 != mPools.end() ) + { + LLDrawPool *poolp = *iter1; + + cur_type = poolp->getType(); + + //debug use + sCurRenderPoolType = cur_type ; + + if (occlude && cur_type >= LLDrawPool::POOL_GRASS) + { + occlude = FALSE; + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + doOcclusion(camera); + } + + pool_set_t::iterator iter2 = iter1; + if (hasRenderType(poolp->getType()) && poolp->getNumPasses() > 0) + { + LLFastTimer t(FTM_POOLRENDER); + + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + + for( S32 i = 0; i < poolp->getNumPasses(); i++ ) + { + LLVertexBuffer::unbind(); + poolp->beginRenderPass(i); + for (iter2 = iter1; iter2 != mPools.end(); iter2++) + { + LLDrawPool *p = *iter2; + if (p->getType() != cur_type) + { + break; + } + + p->render(i); + } + poolp->endRenderPass(i); + LLVertexBuffer::unbind(); + if (gDebugGL || gDebugPipeline) + { + GLint depth; + glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &depth); + if (depth > 3) + { + if (gDebugSession) + { + ll_fail("GL matrix stack corrupted."); + } + llerrs << "GL matrix stack corrupted!" << llendl; + } + std::string msg = llformat("%s pass %d", gPoolNames[cur_type].c_str(), i); + LLGLState::checkStates(msg); + LLGLState::checkTextureChannels(msg); + LLGLState::checkClientArrays(msg); + } + } + } + else + { + // Skip all pools of this type + for (iter2 = iter1; iter2 != mPools.end(); iter2++) + { + LLDrawPool *p = *iter2; + if (p->getType() != cur_type) + { + break; + } + } + } + iter1 = iter2; + stop_glerror(); + } + + LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderDrawPoolsEnd"); + + LLVertexBuffer::unbind(); + + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + + if (occlude) + { + occlude = FALSE; + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + doOcclusion(camera); + } + } + + LLVertexBuffer::unbind(); + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + LLGLState::checkClientArrays(); + + + + stop_glerror(); + + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + LLGLState::checkClientArrays(); + + LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderHighlights"); + + if (!sReflectionRender) + { + renderHighlights(); + } + + // Contains a list of the faces of objects that are physical or + // have touch-handlers. + mHighlightFaces.clear(); + + LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderDebug"); + + renderDebug(); + + LLVertexBuffer::unbind(); + + if (!LLPipeline::sReflectionRender && !LLPipeline::sRenderDeferred) + { + if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + // Render debugging beacons. + gObjectList.renderObjectBeacons(); + gObjectList.resetObjectBeacons(); + } + else + { + // Make sure particle effects disappear + LLHUDObject::renderAllForTimer(); + } + } + else + { + // Make sure particle effects disappear + LLHUDObject::renderAllForTimer(); + } + + LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderGeomEnd"); + + //HACK: preserve/restore matrices around HUD render + if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD)) + { + for (U32 i = 0; i < 16; i++) + { + gGLModelView[i] = saved_modelview[i]; + gGLProjection[i] = saved_projection[i]; + } + } + + LLVertexBuffer::unbind(); + + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + LLGLState::checkClientArrays(); +} + +void LLPipeline::renderGeomDeferred(LLCamera& camera) +{ + LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderGeomDeferred"); + + LLMemType mt_rgd(LLMemType::MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED); + LLFastTimer t(FTM_RENDER_GEOMETRY); + + LLFastTimer t2(FTM_POOLS); + + LLGLEnable cull(GL_CULL_FACE); + + LLGLEnable stencil(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF); + stop_glerror(); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + stop_glerror(); + + for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) + { + LLDrawPool *poolp = *iter; + if (hasRenderType(poolp->getType())) + { + poolp->prerender(); + } + } + + LLGLEnable multisample(GL_MULTISAMPLE_ARB); + + LLVertexBuffer::unbind(); + + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + LLGLState::checkClientArrays(); + + U32 cur_type = 0; + + gGL.setColorMask(true, true); + + pool_set_t::iterator iter1 = mPools.begin(); + + while ( iter1 != mPools.end() ) + { + LLDrawPool *poolp = *iter1; + + cur_type = poolp->getType(); + + pool_set_t::iterator iter2 = iter1; + if (hasRenderType(poolp->getType()) && poolp->getNumDeferredPasses() > 0) + { + LLFastTimer t(FTM_POOLRENDER); + + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + + for( S32 i = 0; i < poolp->getNumDeferredPasses(); i++ ) + { + LLVertexBuffer::unbind(); + poolp->beginDeferredPass(i); + for (iter2 = iter1; iter2 != mPools.end(); iter2++) + { + LLDrawPool *p = *iter2; + if (p->getType() != cur_type) + { + break; + } + + p->renderDeferred(i); + } + poolp->endDeferredPass(i); + LLVertexBuffer::unbind(); + + if (gDebugGL || gDebugPipeline) + { + GLint depth; + glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &depth); + if (depth > 3) + { + llerrs << "GL matrix stack corrupted!" << llendl; + } + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + LLGLState::checkClientArrays(); + } + } + } + else + { + // Skip all pools of this type + for (iter2 = iter1; iter2 != mPools.end(); iter2++) + { + LLDrawPool *p = *iter2; + if (p->getType() != cur_type) + { + break; + } + } + } + iter1 = iter2; + stop_glerror(); + } + + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + + gGL.setColorMask(true, false); +} + +void LLPipeline::renderGeomPostDeferred(LLCamera& camera) +{ + LLMemType mt_rgpd(LLMemType::MTYPE_PIPELINE_RENDER_GEOM_POST_DEF); + LLFastTimer t(FTM_POOLS); + U32 cur_type = 0; + + LLGLEnable cull(GL_CULL_FACE); + + LLGLEnable multisample(GL_MULTISAMPLE_ARB); + + calcNearbyLights(camera); + setupHWLights(NULL); + + gGL.setColorMask(true, false); + + pool_set_t::iterator iter1 = mPools.begin(); + BOOL occlude = LLPipeline::sUseOcclusion > 1; + + while ( iter1 != mPools.end() ) + { + LLDrawPool *poolp = *iter1; + + cur_type = poolp->getType(); + + if (occlude && cur_type >= LLDrawPool::POOL_GRASS) + { + occlude = FALSE; + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + doOcclusion(camera); + gGL.setColorMask(true, false); + } + + pool_set_t::iterator iter2 = iter1; + if (hasRenderType(poolp->getType()) && poolp->getNumPostDeferredPasses() > 0) + { + LLFastTimer t(FTM_POOLRENDER); + + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + + for( S32 i = 0; i < poolp->getNumPostDeferredPasses(); i++ ) + { + LLVertexBuffer::unbind(); + poolp->beginPostDeferredPass(i); + for (iter2 = iter1; iter2 != mPools.end(); iter2++) + { + LLDrawPool *p = *iter2; + if (p->getType() != cur_type) + { + break; + } + + p->renderPostDeferred(i); + } + poolp->endPostDeferredPass(i); + LLVertexBuffer::unbind(); + + if (gDebugGL || gDebugPipeline) + { + GLint depth; + glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &depth); + if (depth > 3) + { + llerrs << "GL matrix stack corrupted!" << llendl; + } + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + LLGLState::checkClientArrays(); + } + } + } + else + { + // Skip all pools of this type + for (iter2 = iter1; iter2 != mPools.end(); iter2++) + { + LLDrawPool *p = *iter2; + if (p->getType() != cur_type) + { + break; + } + } + } + iter1 = iter2; + stop_glerror(); + } + + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + + if (occlude) + { + occlude = FALSE; + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + doOcclusion(camera); + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + } +} + +void LLPipeline::renderGeomShadow(LLCamera& camera) +{ + LLMemType mt_rgs(LLMemType::MTYPE_PIPELINE_RENDER_GEOM_SHADOW); + U32 cur_type = 0; + + LLGLEnable cull(GL_CULL_FACE); + + LLVertexBuffer::unbind(); + + pool_set_t::iterator iter1 = mPools.begin(); + + while ( iter1 != mPools.end() ) + { + LLDrawPool *poolp = *iter1; + + cur_type = poolp->getType(); + + pool_set_t::iterator iter2 = iter1; + if (hasRenderType(poolp->getType()) && poolp->getNumShadowPasses() > 0) + { + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + + for( S32 i = 0; i < poolp->getNumShadowPasses(); i++ ) + { + LLVertexBuffer::unbind(); + poolp->beginShadowPass(i); + for (iter2 = iter1; iter2 != mPools.end(); iter2++) + { + LLDrawPool *p = *iter2; + if (p->getType() != cur_type) + { + break; + } + + p->renderShadow(i); + } + poolp->endShadowPass(i); + LLVertexBuffer::unbind(); + + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + LLGLState::checkClientArrays(); + } + } + else + { + // Skip all pools of this type + for (iter2 = iter1; iter2 != mPools.end(); iter2++) + { + LLDrawPool *p = *iter2; + if (p->getType() != cur_type) + { + break; + } + } + } + iter1 = iter2; + stop_glerror(); + } + + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); +} + + +void LLPipeline::addTrianglesDrawn(S32 index_count, U32 render_type) +{ + assertInitialized(); + S32 count = 0; + if (render_type == LLRender::TRIANGLE_STRIP) + { + count = index_count-2; + } + else + { + count = index_count/3; + } + + mTrianglesDrawn += count; + mBatchCount++; + mMaxBatchSize = llmax(mMaxBatchSize, count); + mMinBatchSize = llmin(mMinBatchSize, count); + + if (LLPipeline::sRenderFrameTest) + { + gViewerWindow->getWindow()->swapBuffers(); + ms_sleep(16); + } +} + +void LLPipeline::renderDebug() +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE); + + assertInitialized(); + + gGL.color4f(1,1,1,1); + + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + gGL.setColorMask(true, false); + + // Debug stuff. + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) + { + LLSpatialPartition* part = region->getSpatialPartition(i); + if (part) + { + if (hasRenderType(part->mDrawableType)) + { + part->renderDebug(); + } + } + } + } + + for (LLCullResult::bridge_list_t::const_iterator i = sCull->beginVisibleBridge(); i != sCull->endVisibleBridge(); ++i) + { + LLSpatialBridge* bridge = *i; + if (!bridge->isDead() && hasRenderType(bridge->mDrawableType)) + { + glPushMatrix(); + glMultMatrixf((F32*)bridge->mDrawable->getRenderMatrix().mMatrix); + bridge->renderDebug(); + glPopMatrix(); + } + } + + if (hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA)) + { + LLGLEnable blend(GL_BLEND); + LLGLDepthTest depth(TRUE, FALSE); + LLGLDisable cull(GL_CULL_FACE); + + gGL.color4f(1,1,1,1); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + F32 a = 0.1f; + + F32 col[] = + { + 1,0,0,a, + 0,1,0,a, + 0,0,1,a, + 1,0,1,a, + + 1,1,0,a, + 0,1,1,a, + 1,1,1,a, + 1,0,1,a, + }; + + for (U32 i = 0; i < 8; i++) + { + LLVector3* frust = mShadowCamera[i].mAgentFrustum; + + if (i > 3) + { //render shadow frusta as volumes + if (mShadowFrustPoints[i-4].empty()) + { + continue; + } + + gGL.color4fv(col+(i-4)*4); + + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.vertex3fv(frust[0].mV); gGL.vertex3fv(frust[4].mV); + gGL.vertex3fv(frust[1].mV); gGL.vertex3fv(frust[5].mV); + gGL.vertex3fv(frust[2].mV); gGL.vertex3fv(frust[6].mV); + gGL.vertex3fv(frust[3].mV); gGL.vertex3fv(frust[7].mV); + gGL.vertex3fv(frust[0].mV); gGL.vertex3fv(frust[4].mV); + gGL.end(); + + + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.vertex3fv(frust[0].mV); + gGL.vertex3fv(frust[1].mV); + gGL.vertex3fv(frust[3].mV); + gGL.vertex3fv(frust[2].mV); + gGL.end(); + + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.vertex3fv(frust[4].mV); + gGL.vertex3fv(frust[5].mV); + gGL.vertex3fv(frust[7].mV); + gGL.vertex3fv(frust[6].mV); + gGL.end(); + } + + + if (i < 4) + { + + if (i == 0 || !mShadowFrustPoints[i].empty()) + { + //render visible point cloud + gGL.flush(); + glPointSize(8.f); + gGL.begin(LLRender::POINTS); + + F32* c = col+i*4; + gGL.color3fv(c); + + for (U32 j = 0; j < mShadowFrustPoints[i].size(); ++j) + { + gGL.vertex3fv(mShadowFrustPoints[i][j].mV); + + } + gGL.end(); + + gGL.flush(); + glPointSize(1.f); + + LLVector3* ext = mShadowExtents[i]; + LLVector3 pos = (ext[0]+ext[1])*0.5f; + LLVector3 size = (ext[1]-ext[0])*0.5f; + drawBoxOutline(pos, size); + + //render camera frustum splits as outlines + gGL.begin(LLRender::LINES); + gGL.vertex3fv(frust[0].mV); gGL.vertex3fv(frust[1].mV); + gGL.vertex3fv(frust[1].mV); gGL.vertex3fv(frust[2].mV); + gGL.vertex3fv(frust[2].mV); gGL.vertex3fv(frust[3].mV); + gGL.vertex3fv(frust[3].mV); gGL.vertex3fv(frust[0].mV); + gGL.vertex3fv(frust[4].mV); gGL.vertex3fv(frust[5].mV); + gGL.vertex3fv(frust[5].mV); gGL.vertex3fv(frust[6].mV); + gGL.vertex3fv(frust[6].mV); gGL.vertex3fv(frust[7].mV); + gGL.vertex3fv(frust[7].mV); gGL.vertex3fv(frust[4].mV); + gGL.vertex3fv(frust[0].mV); gGL.vertex3fv(frust[4].mV); + gGL.vertex3fv(frust[1].mV); gGL.vertex3fv(frust[5].mV); + gGL.vertex3fv(frust[2].mV); gGL.vertex3fv(frust[6].mV); + gGL.vertex3fv(frust[3].mV); gGL.vertex3fv(frust[7].mV); + gGL.end(); + } + + } + + /*for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + for (U32 j = 0; j < LLViewerRegion::NUM_PARTITIONS; j++) + { + LLSpatialPartition* part = region->getSpatialPartition(j); + if (part) + { + if (hasRenderType(part->mDrawableType)) + { + part->renderIntersectingBBoxes(&mShadowCamera[i]); + } + } + } + }*/ + } + } + + if (mRenderDebugMask & RENDER_DEBUG_COMPOSITION) + { + // Debug composition layers + F32 x, y; + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + if (gAgent.getRegion()) + { + gGL.begin(LLRender::POINTS); + // Draw the composition layer for the region that I'm in. + for (x = 0; x <= 260; x++) + { + for (y = 0; y <= 260; y++) + { + if ((x > 255) || (y > 255)) + { + gGL.color4f(1.f, 0.f, 0.f, 1.f); + } + else + { + gGL.color4f(0.f, 0.f, 1.f, 1.f); + } + F32 z = gAgent.getRegion()->getCompositionXY((S32)x, (S32)y); + z *= 5.f; + z += 50.f; + gGL.vertex3f(x, y, z); + } + } + gGL.end(); + } + } + + if (mRenderDebugMask & LLPipeline::RENDER_DEBUG_BUILD_QUEUE) + { + U32 count = 0; + U32 size = mBuildQ2.size(); + LLColor4 col; + + LLGLEnable blend(GL_BLEND); + LLGLDepthTest depth(GL_TRUE, GL_FALSE); + gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep); + + for (LLSpatialGroup::sg_vector_t::iterator iter = mGroupQ2.begin(); iter != mGroupQ2.end(); ++iter) + { + LLSpatialGroup* group = *iter; + if (group->isDead()) + { + continue; + } + + LLSpatialBridge* bridge = group->mSpatialPartition->asBridge(); + + if (bridge && (!bridge->mDrawable || bridge->mDrawable->isDead())) + { + continue; + } + + if (bridge) + { + gGL.pushMatrix(); + glMultMatrixf((F32*)bridge->mDrawable->getRenderMatrix().mMatrix); + } + + F32 alpha = (F32) (size-count)/size; + + + LLVector2 c(1.f-alpha, alpha); + c.normVec(); + + + ++count; + col.set(c.mV[0], c.mV[1], 0, alpha*0.5f+0.1f); + group->drawObjectBox(col); + + if (bridge) + { + gGL.popMatrix(); + } + } + } + + gGL.flush(); +} + +void LLPipeline::rebuildPools() +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_REBUILD_POOLS); + + assertInitialized(); + + S32 max_count = mPools.size(); + pool_set_t::iterator iter1 = mPools.upper_bound(mLastRebuildPool); + while(max_count > 0 && mPools.size() > 0) // && num_rebuilds < MAX_REBUILDS) + { + if (iter1 == mPools.end()) + { + iter1 = mPools.begin(); + } + LLDrawPool* poolp = *iter1; + + if (poolp->isDead()) + { + mPools.erase(iter1++); + removeFromQuickLookup( poolp ); + if (poolp == mLastRebuildPool) + { + mLastRebuildPool = NULL; + } + delete poolp; + } + else + { + mLastRebuildPool = poolp; + iter1++; + } + max_count--; + } + + if (isAgentAvatarValid()) + { + gAgentAvatarp->rebuildHUD(); + } +} + +void LLPipeline::addToQuickLookup( LLDrawPool* new_poolp ) +{ + LLMemType mt(LLMemType::MTYPE_PIPELINE_QUICK_LOOKUP); + + assertInitialized(); + + switch( new_poolp->getType() ) + { + case LLDrawPool::POOL_SIMPLE: + if (mSimplePool) + { + llassert(0); + llwarns << "Ignoring duplicate simple pool." << llendl; + } + else + { + mSimplePool = (LLRenderPass*) new_poolp; + } + break; + + case LLDrawPool::POOL_GRASS: + if (mGrassPool) + { + llassert(0); + llwarns << "Ignoring duplicate grass pool." << llendl; + } + else + { + mGrassPool = (LLRenderPass*) new_poolp; + } + break; + + case LLDrawPool::POOL_FULLBRIGHT: + if (mFullbrightPool) + { + llassert(0); + llwarns << "Ignoring duplicate simple pool." << llendl; + } + else + { + mFullbrightPool = (LLRenderPass*) new_poolp; + } + break; + + case LLDrawPool::POOL_INVISIBLE: + if (mInvisiblePool) + { + llassert(0); + llwarns << "Ignoring duplicate simple pool." << llendl; + } + else + { + mInvisiblePool = (LLRenderPass*) new_poolp; + } + break; + + case LLDrawPool::POOL_GLOW: + if (mGlowPool) + { + llassert(0); + llwarns << "Ignoring duplicate glow pool." << llendl; + } + else + { + mGlowPool = (LLRenderPass*) new_poolp; + } + break; + + case LLDrawPool::POOL_TREE: + mTreePools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ; + break; + + case LLDrawPool::POOL_TERRAIN: + mTerrainPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ; + break; + + case LLDrawPool::POOL_BUMP: + if (mBumpPool) + { + llassert(0); + llwarns << "Ignoring duplicate bump pool." << llendl; + } + else + { + mBumpPool = new_poolp; + } + break; + + case LLDrawPool::POOL_ALPHA: + if( mAlphaPool ) + { + llassert(0); + llwarns << "LLPipeline::addPool(): Ignoring duplicate Alpha pool" << llendl; + } + else + { + mAlphaPool = new_poolp; + } + break; + + case LLDrawPool::POOL_AVATAR: + break; // Do nothing + + case LLDrawPool::POOL_SKY: + if( mSkyPool ) + { + llassert(0); + llwarns << "LLPipeline::addPool(): Ignoring duplicate Sky pool" << llendl; + } + else + { + mSkyPool = new_poolp; + } + break; + + case LLDrawPool::POOL_WATER: + if( mWaterPool ) + { + llassert(0); + llwarns << "LLPipeline::addPool(): Ignoring duplicate Water pool" << llendl; + } + else + { + mWaterPool = new_poolp; + } + break; + + case LLDrawPool::POOL_GROUND: + if( mGroundPool ) + { + llassert(0); + llwarns << "LLPipeline::addPool(): Ignoring duplicate Ground Pool" << llendl; + } + else + { + mGroundPool = new_poolp; + } + break; + + case LLDrawPool::POOL_WL_SKY: + if( mWLSkyPool ) + { + llassert(0); + llwarns << "LLPipeline::addPool(): Ignoring duplicate WLSky Pool" << llendl; + } + else + { + mWLSkyPool = new_poolp; + } + break; + + default: + llassert(0); + llwarns << "Invalid Pool Type in LLPipeline::addPool()" << llendl; + break; + } +} + +void LLPipeline::removePool( LLDrawPool* poolp ) +{ + assertInitialized(); + removeFromQuickLookup(poolp); + mPools.erase(poolp); + delete poolp; +} + +void LLPipeline::removeFromQuickLookup( LLDrawPool* poolp ) +{ + assertInitialized(); + LLMemType mt(LLMemType::MTYPE_PIPELINE); + switch( poolp->getType() ) + { + case LLDrawPool::POOL_SIMPLE: + llassert(mSimplePool == poolp); + mSimplePool = NULL; + break; + + case LLDrawPool::POOL_GRASS: + llassert(mGrassPool == poolp); + mGrassPool = NULL; + break; + + case LLDrawPool::POOL_FULLBRIGHT: + llassert(mFullbrightPool == poolp); + mFullbrightPool = NULL; + break; + + case LLDrawPool::POOL_INVISIBLE: + llassert(mInvisiblePool == poolp); + mInvisiblePool = NULL; + break; + + case LLDrawPool::POOL_WL_SKY: + llassert(mWLSkyPool == poolp); + mWLSkyPool = NULL; + break; + + case LLDrawPool::POOL_GLOW: + llassert(mGlowPool == poolp); + mGlowPool = NULL; + break; + + case LLDrawPool::POOL_TREE: + #ifdef _DEBUG + { + BOOL found = mTreePools.erase( (uintptr_t)poolp->getTexture() ); + llassert( found ); + } + #else + mTreePools.erase( (uintptr_t)poolp->getTexture() ); + #endif + break; + + case LLDrawPool::POOL_TERRAIN: + #ifdef _DEBUG + { + BOOL found = mTerrainPools.erase( (uintptr_t)poolp->getTexture() ); + llassert( found ); + } + #else + mTerrainPools.erase( (uintptr_t)poolp->getTexture() ); + #endif + break; + + case LLDrawPool::POOL_BUMP: + llassert( poolp == mBumpPool ); + mBumpPool = NULL; + break; + + case LLDrawPool::POOL_ALPHA: + llassert( poolp == mAlphaPool ); + mAlphaPool = NULL; + break; + + case LLDrawPool::POOL_AVATAR: + break; // Do nothing + + case LLDrawPool::POOL_SKY: + llassert( poolp == mSkyPool ); + mSkyPool = NULL; + break; + + case LLDrawPool::POOL_WATER: + llassert( poolp == mWaterPool ); + mWaterPool = NULL; + break; + + case LLDrawPool::POOL_GROUND: + llassert( poolp == mGroundPool ); + mGroundPool = NULL; + break; + + default: + llassert(0); + llwarns << "Invalid Pool Type in LLPipeline::removeFromQuickLookup() type=" << poolp->getType() << llendl; + break; + } +} + +void LLPipeline::resetDrawOrders() +{ + assertInitialized(); + // Iterate through all of the draw pools and rebuild them. + for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) + { + LLDrawPool *poolp = *iter; + poolp->resetDrawOrders(); + } +} + +//============================================================================ +// Once-per-frame setup of hardware lights, +// including sun/moon, avatar backlight, and up to 6 local lights + +void LLPipeline::setupAvatarLights(BOOL for_edit) +{ + assertInitialized(); + + if (for_edit) + { + LLColor4 diffuse(1.f, 1.f, 1.f, 0.f); + LLVector4 light_pos_cam(-8.f, 0.25f, 10.f, 0.f); // w==0 => directional light + LLMatrix4 camera_mat = LLViewerCamera::getInstance()->getModelview(); + LLMatrix4 camera_rot(camera_mat.getMat3()); + camera_rot.invert(); + LLVector4 light_pos = light_pos_cam * camera_rot; + + light_pos.normalize(); + + mHWLightColors[1] = diffuse; + glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse.mV); + glLightfv(GL_LIGHT1, GL_AMBIENT, LLColor4::black.mV); + glLightfv(GL_LIGHT1, GL_SPECULAR, LLColor4::black.mV); + glLightfv(GL_LIGHT1, GL_POSITION, light_pos.mV); + glLightf (GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.0f); + glLightf (GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.0f); + glLightf (GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0f); + glLightf (GL_LIGHT1, GL_SPOT_EXPONENT, 0.0f); + glLightf (GL_LIGHT1, GL_SPOT_CUTOFF, 180.0f); + } + else if (gAvatarBacklight) // Always true (unless overridden in a devs .ini) + { + LLVector3 opposite_pos = -1.f * mSunDir; + LLVector3 orthog_light_pos = mSunDir % LLVector3::z_axis; + LLVector4 backlight_pos = LLVector4(lerp(opposite_pos, orthog_light_pos, 0.3f), 0.0f); + backlight_pos.normalize(); + + LLColor4 light_diffuse = mSunDiffuse; + LLColor4 backlight_diffuse(1.f - light_diffuse.mV[VRED], 1.f - light_diffuse.mV[VGREEN], 1.f - light_diffuse.mV[VBLUE], 1.f); + F32 max_component = 0.001f; + for (S32 i = 0; i < 3; i++) + { + if (backlight_diffuse.mV[i] > max_component) + { + max_component = backlight_diffuse.mV[i]; + } + } + F32 backlight_mag; + if (gSky.getSunDirection().mV[2] >= LLSky::NIGHTTIME_ELEVATION_COS) + { + backlight_mag = BACKLIGHT_DAY_MAGNITUDE_OBJECT; + } + else + { + backlight_mag = BACKLIGHT_NIGHT_MAGNITUDE_OBJECT; + } + backlight_diffuse *= backlight_mag / max_component; + + mHWLightColors[1] = backlight_diffuse; + glLightfv(GL_LIGHT1, GL_POSITION, backlight_pos.mV); // this is just sun/moon direction + glLightfv(GL_LIGHT1, GL_DIFFUSE, backlight_diffuse.mV); + glLightfv(GL_LIGHT1, GL_AMBIENT, LLColor4::black.mV); + glLightfv(GL_LIGHT1, GL_SPECULAR, LLColor4::black.mV); + glLightf (GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.0f); + glLightf (GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.0f); + glLightf (GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0f); + glLightf (GL_LIGHT1, GL_SPOT_EXPONENT, 0.0f); + glLightf (GL_LIGHT1, GL_SPOT_CUTOFF, 180.0f); + } + else + { + mHWLightColors[1] = LLColor4::black; + glLightfv(GL_LIGHT1, GL_DIFFUSE, LLColor4::black.mV); + glLightfv(GL_LIGHT1, GL_AMBIENT, LLColor4::black.mV); + glLightfv(GL_LIGHT1, GL_SPECULAR, LLColor4::black.mV); + } +} + +static F32 calc_light_dist(LLVOVolume* light, const LLVector3& cam_pos, F32 max_dist) +{ + F32 inten = light->getLightIntensity(); + if (inten < .001f) + { + return max_dist; + } + F32 radius = light->getLightRadius(); + BOOL selected = light->isSelected(); + LLVector3 dpos = light->getRenderPosition() - cam_pos; + F32 dist2 = dpos.lengthSquared(); + if (!selected && dist2 > (max_dist + radius)*(max_dist + radius)) + { + return max_dist; + } + F32 dist = fsqrtf(dist2); + dist *= 1.f / inten; + dist -= radius; + if (selected) + { + dist -= 10000.f; // selected lights get highest priority + } + if (light->mDrawable.notNull() && light->mDrawable->isState(LLDrawable::ACTIVE)) + { + // moving lights get a little higher priority (too much causes artifacts) + dist -= light->getLightRadius()*0.25f; + } + return dist; +} + +void LLPipeline::calcNearbyLights(LLCamera& camera) +{ + assertInitialized(); + + if (LLPipeline::sReflectionRender) + { + return; + } + + if (mLightingDetail >= 1) + { + // mNearbyLight (and all light_set_t's) are sorted such that + // begin() == the closest light and rbegin() == the farthest light + const S32 MAX_LOCAL_LIGHTS = 6; +// LLVector3 cam_pos = gAgentCamera.getCameraPositionAgent(); + LLVector3 cam_pos = LLViewerJoystick::getInstance()->getOverrideCamera() ? + camera.getOrigin() : + gAgent.getPositionAgent(); + + F32 max_dist = LIGHT_MAX_RADIUS * 4.f; // ignore enitrely lights > 4 * max light rad + + // UPDATE THE EXISTING NEARBY LIGHTS + light_set_t cur_nearby_lights; + for (light_set_t::iterator iter = mNearbyLights.begin(); + iter != mNearbyLights.end(); iter++) + { + const Light* light = &(*iter); + LLDrawable* drawable = light->drawable; + LLVOVolume* volight = drawable->getVOVolume(); + if (!volight || !drawable->isState(LLDrawable::LIGHT)) + { + drawable->clearState(LLDrawable::NEARBY_LIGHT); + continue; + } + if (light->fade <= -LIGHT_FADE_TIME) + { + drawable->clearState(LLDrawable::NEARBY_LIGHT); + continue; + } + if (!sRenderAttachedLights && volight && volight->isAttachment()) + { + drawable->clearState(LLDrawable::NEARBY_LIGHT); + continue; + } + + F32 dist = calc_light_dist(volight, cam_pos, max_dist); + cur_nearby_lights.insert(Light(drawable, dist, light->fade)); + } + mNearbyLights = cur_nearby_lights; + + // FIND NEW LIGHTS THAT ARE IN RANGE + light_set_t new_nearby_lights; + for (LLDrawable::drawable_set_t::iterator iter = mLights.begin(); + iter != mLights.end(); ++iter) + { + LLDrawable* drawable = *iter; + LLVOVolume* light = drawable->getVOVolume(); + if (!light || drawable->isState(LLDrawable::NEARBY_LIGHT)) + { + continue; + } + if (light->isHUDAttachment()) + { + continue; // no lighting from HUD objects + } + F32 dist = calc_light_dist(light, cam_pos, max_dist); + if (dist >= max_dist) + { + continue; + } + if (!sRenderAttachedLights && light && light->isAttachment()) + { + continue; + } + new_nearby_lights.insert(Light(drawable, dist, 0.f)); + if (new_nearby_lights.size() > (U32)MAX_LOCAL_LIGHTS) + { + new_nearby_lights.erase(--new_nearby_lights.end()); + const Light& last = *new_nearby_lights.rbegin(); + max_dist = last.dist; + } + } + + // INSERT ANY NEW LIGHTS + for (light_set_t::iterator iter = new_nearby_lights.begin(); + iter != new_nearby_lights.end(); iter++) + { + const Light* light = &(*iter); + if (mNearbyLights.size() < (U32)MAX_LOCAL_LIGHTS) + { + mNearbyLights.insert(*light); + ((LLDrawable*) light->drawable)->setState(LLDrawable::NEARBY_LIGHT); + } + else + { + // crazy cast so that we can overwrite the fade value + // even though gcc enforces sets as const + // (fade value doesn't affect sort so this is safe) + Light* farthest_light = ((Light*) (&(*(mNearbyLights.rbegin())))); + if (light->dist < farthest_light->dist) + { + if (farthest_light->fade >= 0.f) + { + farthest_light->fade = -gFrameIntervalSeconds; + } + } + else + { + break; // none of the other lights are closer + } + } + } + + } +} + +void LLPipeline::setupHWLights(LLDrawPool* pool) +{ + assertInitialized(); + + // Ambient + LLColor4 ambient = gSky.getTotalAmbientColor(); + glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambient.mV); + + // Light 0 = Sun or Moon (All objects) + { + if (gSky.getSunDirection().mV[2] >= LLSky::NIGHTTIME_ELEVATION_COS) + { + mSunDir.setVec(gSky.getSunDirection()); + mSunDiffuse.setVec(gSky.getSunDiffuseColor()); + } + else + { + mSunDir.setVec(gSky.getMoonDirection()); + mSunDiffuse.setVec(gSky.getMoonDiffuseColor()); + } + + F32 max_color = llmax(mSunDiffuse.mV[0], mSunDiffuse.mV[1], mSunDiffuse.mV[2]); + if (max_color > 1.f) + { + mSunDiffuse *= 1.f/max_color; + } + mSunDiffuse.clamp(); + + LLVector4 light_pos(mSunDir, 0.0f); + LLColor4 light_diffuse = mSunDiffuse; + mHWLightColors[0] = light_diffuse; + glLightfv(GL_LIGHT0, GL_POSITION, light_pos.mV); // this is just sun/moon direction + glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse.mV); + glLightfv(GL_LIGHT0, GL_AMBIENT, LLColor4::black.mV); + glLightfv(GL_LIGHT0, GL_SPECULAR, LLColor4::black.mV); + glLightf (GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0f); + glLightf (GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0f); + glLightf (GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.0f); + glLightf (GL_LIGHT0, GL_SPOT_EXPONENT, 0.0f); + glLightf (GL_LIGHT0, GL_SPOT_CUTOFF, 180.0f); + } + + // Light 1 = Backlight (for avatars) + // (set by enableLightsAvatar) + + S32 cur_light = 2; + + // Nearby lights = LIGHT 2-7 + + mLightMovingMask = 0; + + if (mLightingDetail >= 1) + { + for (light_set_t::iterator iter = mNearbyLights.begin(); + iter != mNearbyLights.end(); ++iter) + { + LLDrawable* drawable = iter->drawable; + LLVOVolume* light = drawable->getVOVolume(); + if (!light) + { + continue; + } + if (drawable->isState(LLDrawable::ACTIVE)) + { + mLightMovingMask |= (1<getLightColor(); + light_color.mV[3] = 0.0f; + + F32 fade = iter->fade; + if (fade < LIGHT_FADE_TIME) + { + // fade in/out light + if (fade >= 0.f) + { + fade = fade / LIGHT_FADE_TIME; + ((Light*) (&(*iter)))->fade += gFrameIntervalSeconds; + } + else + { + fade = 1.f + fade / LIGHT_FADE_TIME; + ((Light*) (&(*iter)))->fade -= gFrameIntervalSeconds; + } + fade = llclamp(fade,0.f,1.f); + light_color *= fade; + } + + LLVector3 light_pos(light->getRenderPosition()); + LLVector4 light_pos_gl(light_pos, 1.0f); + + F32 light_radius = llmax(light->getLightRadius(), 0.001f); + + F32 x = (3.f * (1.f + light->getLightFalloff())); // why this magic? probably trying to match a historic behavior. + float linatten = x / (light_radius); // % of brightness at radius + + mHWLightColors[cur_light] = light_color; + S32 gllight = GL_LIGHT0+cur_light; + glLightfv(gllight, GL_POSITION, light_pos_gl.mV); + glLightfv(gllight, GL_DIFFUSE, light_color.mV); + glLightfv(gllight, GL_AMBIENT, LLColor4::black.mV); + glLightf (gllight, GL_CONSTANT_ATTENUATION, 0.0f); + glLightf (gllight, GL_LINEAR_ATTENUATION, linatten); + glLightf (gllight, GL_QUADRATIC_ATTENUATION, 0.0f); + if (light->isLightSpotlight() // directional (spot-)light + && (LLPipeline::sRenderDeferred || gSavedSettings.getBOOL("RenderSpotLightsInNondeferred"))) // these are only rendered as GL spotlights if we're in deferred rendering mode *or* the setting forces them on + { + LLVector3 spotparams = light->getSpotLightParams(); + LLQuaternion quat = light->getRenderRotation(); + LLVector3 at_axis(0,0,-1); // this matches deferred rendering's object light direction + at_axis *= quat; + //llinfos << "SPOT!!!!!!! fov: " << spotparams.mV[0] << " focus: " << spotparams.mV[1] << " dir: " << at_axis << llendl; + glLightfv(gllight, GL_SPOT_DIRECTION, at_axis.mV); + glLightf (gllight, GL_SPOT_EXPONENT, 2.0f); // 2.0 = good old dot product ^ 2 + glLightf (gllight, GL_SPOT_CUTOFF, 90.0f); // hemisphere + const float specular[] = {0.f, 0.f, 0.f, 0.f}; + glLightfv(gllight, GL_SPECULAR, specular); + } + else // omnidirectional (point) light + { + glLightf (gllight, GL_SPOT_EXPONENT, 0.0f); + glLightf (gllight, GL_SPOT_CUTOFF, 180.0f); + + // we use specular.w = 1.0 as a cheap hack for the shaders to know that this is omnidirectional rather than a spotlight + const float specular[] = {0.f, 0.f, 0.f, 1.f}; + glLightfv(gllight, GL_SPECULAR, specular); + //llinfos << "boring light" << llendl; + } + cur_light++; + if (cur_light >= 8) + { + break; // safety + } + } + } + for ( ; cur_light < 8 ; cur_light++) + { + mHWLightColors[cur_light] = LLColor4::black; + S32 gllight = GL_LIGHT0+cur_light; + glLightfv(gllight, GL_DIFFUSE, LLColor4::black.mV); + glLightfv(gllight, GL_AMBIENT, LLColor4::black.mV); + glLightfv(gllight, GL_SPECULAR, LLColor4::black.mV); + } + + if (isAgentAvatarValid() && + gAgentAvatarp->mSpecialRenderMode == 3) + { + LLColor4 light_color = LLColor4::white; + light_color.mV[3] = 0.0f; + + LLVector3 light_pos(LLViewerCamera::getInstance()->getOrigin()); + LLVector4 light_pos_gl(light_pos, 1.0f); + + F32 light_radius = 16.f; + + F32 x = 3.f; + float linatten = x / (light_radius); // % of brightness at radius + + mHWLightColors[2] = light_color; + S32 gllight = GL_LIGHT2; + glLightfv(gllight, GL_POSITION, light_pos_gl.mV); + glLightfv(gllight, GL_DIFFUSE, light_color.mV); + glLightfv(gllight, GL_AMBIENT, LLColor4::black.mV); + glLightfv(gllight, GL_SPECULAR, LLColor4::black.mV); + glLightf (gllight, GL_CONSTANT_ATTENUATION, 0.0f); + glLightf (gllight, GL_LINEAR_ATTENUATION, linatten); + glLightf (gllight, GL_QUADRATIC_ATTENUATION, 0.0f); + glLightf (gllight, GL_SPOT_EXPONENT, 0.0f); + glLightf (gllight, GL_SPOT_CUTOFF, 180.0f); + } + + // Init GL state + glDisable(GL_LIGHTING); + for (S32 gllight=GL_LIGHT0; gllight<=GL_LIGHT7; gllight++) + { + glDisable(gllight); + } + mLightMask = 0; +} + +void LLPipeline::enableLights(U32 mask) +{ + assertInitialized(); + + if (mLightingDetail == 0) + { + mask &= 0xf003; // sun and backlight only (and fullbright bit) + } + if (mLightMask != mask) + { + stop_glerror(); + if (!mLightMask) + { + glEnable(GL_LIGHTING); + } + if (mask) + { + stop_glerror(); + for (S32 i=0; i<8; i++) + { + if (mask & (1<= 2) + { + mask |= mLightMovingMask; // Hardware moving lights + glColor4f(0.f, 0.f, 0.f, 1.0f); // no local lighting by default + } + else + { + mask |= 0xff & (~2); // Hardware local lights + } + enableLights(mask); +} + +void LLPipeline::enableLightsDynamic() +{ + assertInitialized(); + U32 mask = 0xff & (~2); // Local lights + enableLights(mask); + if (mLightingDetail >= 2) + { + glColor4f(0.f, 0.f, 0.f, 1.f); // no local lighting by default + } + + if (isAgentAvatarValid() && getLightingDetail() <= 0) + { + if (gAgentAvatarp->mSpecialRenderMode == 0) // normal + { + gPipeline.enableLightsAvatar(); + } + else if (gAgentAvatarp->mSpecialRenderMode >= 1) // anim preview + { + gPipeline.enableLightsAvatarEdit(LLColor4(0.7f, 0.6f, 0.3f, 1.f)); + } + } +} + +void LLPipeline::enableLightsAvatar() +{ + U32 mask = 0xff; // All lights + setupAvatarLights(FALSE); + enableLights(mask); +} + +void LLPipeline::enableLightsAvatarEdit(const LLColor4& color) +{ + U32 mask = 0x2002; // Avatar backlight only, set ambient + setupAvatarLights(TRUE); + enableLights(mask); + + glLightModelfv(GL_LIGHT_MODEL_AMBIENT,color.mV); +} + +void LLPipeline::enableLightsFullbright(const LLColor4& color) +{ + assertInitialized(); + U32 mask = 0x1000; // Non-0 mask, set ambient + enableLights(mask); + + glLightModelfv(GL_LIGHT_MODEL_AMBIENT,color.mV); + /*if (mLightingDetail >= 2) + { + glColor4f(0.f, 0.f, 0.f, 1.f); // no local lighting by default + }*/ +} + +void LLPipeline::disableLights() +{ + enableLights(0); // no lighting (full bright) + glColor4f(1.f, 1.f, 1.f, 1.f); // lighting color = white by default +} + +//============================================================================ + +class LLMenuItemGL; +class LLInvFVBridge; +struct cat_folder_pair; +class LLVOBranch; +class LLVOLeaf; + +void LLPipeline::findReferences(LLDrawable *drawablep) +{ + assertInitialized(); + if (mLights.find(drawablep) != mLights.end()) + { + llinfos << "In mLights" << llendl; + } + if (std::find(mMovedList.begin(), mMovedList.end(), drawablep) != mMovedList.end()) + { + llinfos << "In mMovedList" << llendl; + } + if (std::find(mShiftList.begin(), mShiftList.end(), drawablep) != mShiftList.end()) + { + llinfos << "In mShiftList" << llendl; + } + if (mRetexturedList.find(drawablep) != mRetexturedList.end()) + { + llinfos << "In mRetexturedList" << llendl; + } + + if (std::find(mBuildQ1.begin(), mBuildQ1.end(), drawablep) != mBuildQ1.end()) + { + llinfos << "In mBuildQ1" << llendl; + } + if (std::find(mBuildQ2.begin(), mBuildQ2.end(), drawablep) != mBuildQ2.end()) + { + llinfos << "In mBuildQ2" << llendl; + } + + S32 count; + + count = gObjectList.findReferences(drawablep); + if (count) + { + llinfos << "In other drawables: " << count << " references" << llendl; + } +} + +BOOL LLPipeline::verify() +{ + BOOL ok = assertInitialized(); + if (ok) + { + for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter) + { + LLDrawPool *poolp = *iter; + if (!poolp->verify()) + { + ok = FALSE; + } + } + } + + if (!ok) + { + llwarns << "Pipeline verify failed!" << llendl; + } + return ok; +} + +////////////////////////////// +// +// Collision detection +// +// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A method to compute a ray-AABB intersection. + * Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 + * Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) + * Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) + * + * Hence this version is faster as well as more robust than the original one. + * + * Should work provided: + * 1) the integer representation of 0.0f is 0x00000000 + * 2) the sign bit of the float is the most significant one + * + * Report bugs: p.terdiman@codercorner.com + * + * \param aabb [in] the axis-aligned bounding box + * \param origin [in] ray origin + * \param dir [in] ray direction + * \param coord [out] impact coordinates + * \return true if ray intersects AABB + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//#define RAYAABB_EPSILON 0.00001f +#define IR(x) ((U32&)x) + +bool LLRayAABB(const LLVector3 ¢er, const LLVector3 &size, const LLVector3& origin, const LLVector3& dir, LLVector3 &coord, F32 epsilon) +{ + BOOL Inside = TRUE; + LLVector3 MinB = center - size; + LLVector3 MaxB = center + size; + LLVector3 MaxT; + MaxT.mV[VX]=MaxT.mV[VY]=MaxT.mV[VZ]=-1.0f; + + // Find candidate planes. + for(U32 i=0;i<3;i++) + { + if(origin.mV[i] < MinB.mV[i]) + { + coord.mV[i] = MinB.mV[i]; + Inside = FALSE; + + // Calculate T distances to candidate planes + if(IR(dir.mV[i])) MaxT.mV[i] = (MinB.mV[i] - origin.mV[i]) / dir.mV[i]; + } + else if(origin.mV[i] > MaxB.mV[i]) + { + coord.mV[i] = MaxB.mV[i]; + Inside = FALSE; + + // Calculate T distances to candidate planes + if(IR(dir.mV[i])) MaxT.mV[i] = (MaxB.mV[i] - origin.mV[i]) / dir.mV[i]; + } + } + + // Ray origin inside bounding box + if(Inside) + { + coord = origin; + return true; + } + + // Get largest of the maxT's for final choice of intersection + U32 WhichPlane = 0; + if(MaxT.mV[1] > MaxT.mV[WhichPlane]) WhichPlane = 1; + if(MaxT.mV[2] > MaxT.mV[WhichPlane]) WhichPlane = 2; + + // Check final candidate actually inside box + if(IR(MaxT.mV[WhichPlane])&0x80000000) return false; + + for(U32 i=0;i<3;i++) + { + if(i!=WhichPlane) + { + coord.mV[i] = origin.mV[i] + MaxT.mV[WhichPlane] * dir.mV[i]; + if (epsilon > 0) + { + if(coord.mV[i] < MinB.mV[i] - epsilon || coord.mV[i] > MaxB.mV[i] + epsilon) return false; + } + else + { + if(coord.mV[i] < MinB.mV[i] || coord.mV[i] > MaxB.mV[i]) return false; + } + } + } + return true; // ray hits box +} + +////////////////////////////// +// +// Macros, functions, and inline methods from other classes +// +// + +void LLPipeline::setLight(LLDrawable *drawablep, BOOL is_light) +{ + if (drawablep && assertInitialized()) + { + if (is_light) + { + mLights.insert(drawablep); + drawablep->setState(LLDrawable::LIGHT); + } + else + { + drawablep->clearState(LLDrawable::LIGHT); + mLights.erase(drawablep); + } + } +} + +//static +void LLPipeline::toggleRenderType(U32 type) +{ + gPipeline.mRenderTypeEnabled[type] = !gPipeline.mRenderTypeEnabled[type]; + if (type == LLPipeline::RENDER_TYPE_WATER) + { + gPipeline.mRenderTypeEnabled[LLPipeline::RENDER_TYPE_VOIDWATER] = !gPipeline.mRenderTypeEnabled[LLPipeline::RENDER_TYPE_VOIDWATER]; + } +} + +//static +void LLPipeline::toggleRenderTypeControl(void* data) +{ + U32 type = (U32)(intptr_t)data; + U32 bit = (1<inBuildMode() ? FALSE : TRUE; + + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + + for (U32 j = 0; j < LLViewerRegion::NUM_PARTITIONS; j++) + { + if ((j == LLViewerRegion::PARTITION_VOLUME) || + (j == LLViewerRegion::PARTITION_BRIDGE) || + (j == LLViewerRegion::PARTITION_TERRAIN) || + (j == LLViewerRegion::PARTITION_TREE) || + (j == LLViewerRegion::PARTITION_GRASS)) // only check these partitions for now + { + LLSpatialPartition* part = region->getSpatialPartition(j); + if (part && hasRenderType(part->mDrawableType)) + { + LLDrawable* hit = part->lineSegmentIntersect(start, local_end, pick_transparent, face_hit, &position, tex_coord, normal, bi_normal); + if (hit) + { + drawable = hit; + local_end = position; + } + } + } + } + } + + if (!sPickAvatar) + { + //save hit info in case we need to restore + //due to attachment override + LLVector3 local_normal; + LLVector3 local_binormal; + LLVector2 local_texcoord; + S32 local_face_hit = -1; + + if (face_hit) + { + local_face_hit = *face_hit; + } + if (tex_coord) + { + local_texcoord = *tex_coord; + } + if (bi_normal) + { + local_binormal = *bi_normal; + } + if (normal) + { + local_normal = *normal; + } + + const F32 ATTACHMENT_OVERRIDE_DIST = 0.1f; + + //check against avatars + sPickAvatar = TRUE; + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + + LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_BRIDGE); + if (part && hasRenderType(part->mDrawableType)) + { + LLDrawable* hit = part->lineSegmentIntersect(start, local_end, pick_transparent, face_hit, &position, tex_coord, normal, bi_normal); + if (hit) + { + if (!drawable || + !drawable->getVObj()->isAttachment() || + (position-local_end).magVec() > ATTACHMENT_OVERRIDE_DIST) + { //avatar overrides if previously hit drawable is not an attachment or + //attachment is far enough away from detected intersection + drawable = hit; + local_end = position; + } + else + { //prioritize attachments over avatars + position = local_end; + + if (face_hit) + { + *face_hit = local_face_hit; + } + if (tex_coord) + { + *tex_coord = local_texcoord; + } + if (bi_normal) + { + *bi_normal = local_binormal; + } + if (normal) + { + *normal = local_normal; + } + } + } + } + } + } + + //check all avatar nametags (silly, isn't it?) + for (std::vector< LLCharacter* >::iterator iter = LLCharacter::sInstances.begin(); + iter != LLCharacter::sInstances.end(); + ++iter) + { + LLVOAvatar* av = (LLVOAvatar*) *iter; + if (av->mNameText.notNull() + && av->mNameText->lineSegmentIntersect(start, local_end, position)) + { + drawable = av->mDrawable; + local_end = position; + } + } + + if (intersection) + { + *intersection = position; + } + + return drawable ? drawable->getVObj().get() : NULL; +} + +LLViewerObject* LLPipeline::lineSegmentIntersectInHUD(const LLVector3& start, const LLVector3& end, + BOOL pick_transparent, + S32* face_hit, + LLVector3* intersection, // return the intersection point + LLVector2* tex_coord, // return the texture coordinates of the intersection point + LLVector3* normal, // return the surface normal at the intersection point + LLVector3* bi_normal // return the surface bi-normal at the intersection point + ) +{ + LLDrawable* drawable = NULL; + + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + + BOOL toggle = FALSE; + if (!hasRenderType(LLPipeline::RENDER_TYPE_HUD)) + { + toggleRenderType(LLPipeline::RENDER_TYPE_HUD); + toggle = TRUE; + } + + LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_HUD); + if (part) + { + LLDrawable* hit = part->lineSegmentIntersect(start, end, pick_transparent, face_hit, intersection, tex_coord, normal, bi_normal); + if (hit) + { + drawable = hit; + } + } + + if (toggle) + { + toggleRenderType(LLPipeline::RENDER_TYPE_HUD); + } + } + return drawable ? drawable->getVObj().get() : NULL; +} + +LLSpatialPartition* LLPipeline::getSpatialPartition(LLViewerObject* vobj) +{ + if (vobj) + { + LLViewerRegion* region = vobj->getRegion(); + if (region) + { + return region->getSpatialPartition(vobj->getPartitionType()); + } + } + return NULL; +} + + +void LLPipeline::resetVertexBuffers(LLDrawable* drawable) +{ + if (!drawable || drawable->isDead()) + { + return; + } + + for (S32 i = 0; i < drawable->getNumFaces(); i++) + { + LLFace* facep = drawable->getFace(i); + facep->mVertexBuffer = NULL; + facep->mLastVertexBuffer = NULL; + } +} + +void LLPipeline::resetVertexBuffers() +{ + sRenderBump = gSavedSettings.getBOOL("RenderObjectBump"); + sUseTriStrips = gSavedSettings.getBOOL("RenderUseTriStrips"); + LLVertexBuffer::sUseStreamDraw = gSavedSettings.getBOOL("RenderUseStreamVBO"); + + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + { + LLViewerRegion* region = *iter; + for (U32 i = 0; i < LLViewerRegion::NUM_PARTITIONS; i++) + { + LLSpatialPartition* part = region->getSpatialPartition(i); + if (part) + { + part->resetVertexBuffers(); + } + } + } + + resetDrawOrders(); + + gSky.resetVertexBuffers(); + + if (LLVertexBuffer::sGLCount > 0) + { + LLVertexBuffer::cleanupClass(); + } + + //delete all name pool caches + LLGLNamePool::cleanupPools(); + + if (LLVertexBuffer::sGLCount > 0) + { + llwarns << "VBO wipe failed." << llendl; + } + + if (!LLVertexBuffer::sStreamIBOPool.mNameList.empty() || + !LLVertexBuffer::sStreamVBOPool.mNameList.empty() || + !LLVertexBuffer::sDynamicIBOPool.mNameList.empty() || + !LLVertexBuffer::sDynamicVBOPool.mNameList.empty()) + { + llwarns << "VBO name pool cleanup failed." << llendl; + } + + LLVertexBuffer::unbind(); + + LLPipeline::sTextureBindTest = gSavedSettings.getBOOL("RenderDebugTextureBind"); +} + +void LLPipeline::renderObjects(U32 type, U32 mask, BOOL texture) +{ + LLMemType mt_ro(LLMemType::MTYPE_PIPELINE_RENDER_OBJECTS); + assertInitialized(); + glLoadMatrixd(gGLModelView); + gGLLastMatrix = NULL; + mSimplePool->pushBatches(type, mask); + glLoadMatrixd(gGLModelView); + gGLLastMatrix = NULL; +} + +void LLPipeline::setUseVBO(BOOL use_vbo) +{ + if (use_vbo != LLVertexBuffer::sEnableVBOs) + { + if (use_vbo) + { + llinfos << "Enabling VBO." << llendl; + } + else + { + llinfos << "Disabling VBO." << llendl; + } + + resetVertexBuffers(); + LLVertexBuffer::initClass(use_vbo, gSavedSettings.getBOOL("RenderVBOMappingDisable")); + } +} + +void LLPipeline::setDisableVBOMapping(BOOL no_vbo_mapping) +{ + if (LLVertexBuffer::sEnableVBOs && no_vbo_mapping != LLVertexBuffer::sDisableVBOMapping) + { + if (no_vbo_mapping) + { + llinfos << "Disabling VBO glMapBufferARB." << llendl; + } + else + { + llinfos << "Enabling VBO glMapBufferARB." << llendl; + } + + resetVertexBuffers(); + LLVertexBuffer::initClass(true, no_vbo_mapping); + } +} + +void apply_cube_face_rotation(U32 face) +{ + switch (face) + { + case 0: + glRotatef(90.f, 0, 1, 0); + glRotatef(180.f, 1, 0, 0); + break; + case 2: + glRotatef(-90.f, 1, 0, 0); + break; + case 4: + glRotatef(180.f, 0, 1, 0); + glRotatef(180.f, 0, 0, 1); + break; + case 1: + glRotatef(-90.f, 0, 1, 0); + glRotatef(180.f, 1, 0, 0); + break; + case 3: + glRotatef(90, 1, 0, 0); + break; + case 5: + glRotatef(180, 0, 0, 1); + break; + } +} + +void validate_framebuffer_object() +{ + GLenum status; + status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + switch(status) + { + case GL_FRAMEBUFFER_COMPLETE_EXT: + //framebuffer OK, no error. + break; + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: + // frame buffer not OK: probably means unsupported depth buffer format + llerrs << "Framebuffer Incomplete Dimensions." << llendl; + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: + // frame buffer not OK: probably means unsupported depth buffer format + llerrs << "Framebuffer Incomplete Attachment." << llendl; + break; + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + /* choose different formats */ + llerrs << "Framebuffer unsupported." << llendl; + break; + default: + llerrs << "Unknown framebuffer status." << llendl; + break; + } +} + +void LLPipeline::bindScreenToTexture() +{ + +} + +static LLFastTimer::DeclareTimer FTM_RENDER_BLOOM("Bloom"); + +void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) +{ + LLMemType mt_ru(LLMemType::MTYPE_PIPELINE_RENDER_BLOOM); + if (!(gPipeline.canUseVertexShaders() && + sRenderGlow)) + { + return; + } + + LLVertexBuffer::unbind(); + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + + assertInitialized(); + + if (gUseWireframe) + { + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + + U32 res_mod = gSavedSettings.getU32("RenderResolutionDivisor"); + + LLVector2 tc1(0,0); + LLVector2 tc2((F32) gViewerWindow->getWorldViewWidthRaw()*2, + (F32) gViewerWindow->getWorldViewHeightRaw()*2); + + if (res_mod > 1) + { + tc2 /= (F32) res_mod; + } + + gGL.setColorMask(true, true); + + LLFastTimer ftm(FTM_RENDER_BLOOM); + gGL.color4f(1,1,1,1); + LLGLDepthTest depth(GL_FALSE); + LLGLDisable blend(GL_BLEND); + LLGLDisable cull(GL_CULL_FACE); + + enableLightsFullbright(LLColor4(1,1,1,1)); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + LLGLDisable test(GL_ALPHA_TEST); + + gGL.setColorMask(true, true); + glClearColor(0,0,0,0); + + /*if (for_snapshot) + { + gGL.getTexUnit(0)->bind(&mGlow[1]); + { + //LLGLEnable stencil(GL_STENCIL_TEST); + //glStencilFunc(GL_NOTEQUAL, 255, 0xFFFFFFFF); + //glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + //LLGLDisable blend(GL_BLEND); + + // If the snapshot is constructed from tiles, calculate which + // tile we're in. + + //from LLViewerCamera::setPerpsective + if (zoom_factor > 1.f) + { + int pos_y = subfield / llceil(zoom_factor); + int pos_x = subfield - (pos_y*llceil(zoom_factor)); + F32 size = 1.f/zoom_factor; + + tc1.set(pos_x*size, pos_y*size); + tc2 = tc1 + LLVector2(size,size); + } + else + { + tc2.set(1,1); + } + + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_ADD); + + + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.color4f(1,1,1,1); + gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); + gGL.vertex2f(-1,-1); + + gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); + gGL.vertex2f(-1,1); + + gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); + gGL.vertex2f(1,-1); + + gGL.texCoord2f(tc2.mV[0], tc2.mV[1]); + gGL.vertex2f(1,1); + + gGL.end(); + + gGL.flush(); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + + gGL.flush(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + return; + }*/ + + { + { + LLFastTimer ftm(FTM_RENDER_BLOOM_FBO); + mGlow[2].bindTarget(); + mGlow[2].clear(); + } + + gGlowExtractProgram.bind(); + F32 minLum = llmax(gSavedSettings.getF32("RenderGlowMinLuminance"), 0.0f); + F32 maxAlpha = gSavedSettings.getF32("RenderGlowMaxExtractAlpha"); + F32 warmthAmount = gSavedSettings.getF32("RenderGlowWarmthAmount"); + LLVector3 lumWeights = gSavedSettings.getVector3("RenderGlowLumWeights"); + LLVector3 warmthWeights = gSavedSettings.getVector3("RenderGlowWarmthWeights"); + gGlowExtractProgram.uniform1f("minLuminance", minLum); + gGlowExtractProgram.uniform1f("maxExtractAlpha", maxAlpha); + gGlowExtractProgram.uniform3f("lumWeights", lumWeights.mV[0], lumWeights.mV[1], lumWeights.mV[2]); + gGlowExtractProgram.uniform3f("warmthWeights", warmthWeights.mV[0], warmthWeights.mV[1], warmthWeights.mV[2]); + gGlowExtractProgram.uniform1f("warmthAmount", warmthAmount); + LLGLEnable blend_on(GL_BLEND); + LLGLEnable test(GL_ALPHA_TEST); + gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); + gGL.setSceneBlendType(LLRender::BT_ADD_WITH_ALPHA); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(0)->disable(); + gGL.getTexUnit(0)->enable(LLTexUnit::TT_RECT_TEXTURE); + gGL.getTexUnit(0)->bind(&mScreen); + + gGL.color4f(1,1,1,1); + gPipeline.enableLightsFullbright(LLColor4(1,1,1,1)); + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); + gGL.vertex2f(-1,-1); + + gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); + gGL.vertex2f(-1,3); + + gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); + gGL.vertex2f(3,-1); + + gGL.end(); + + gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); + + mGlow[2].flush(); + } + + tc1.setVec(0,0); + tc2.setVec(2,2); + + // power of two between 1 and 1024 + U32 glowResPow = gSavedSettings.getS32("RenderGlowResolutionPow"); + const U32 glow_res = llmax(1, + llmin(1024, 1 << glowResPow)); + + S32 kernel = gSavedSettings.getS32("RenderGlowIterations")*2; + F32 delta = gSavedSettings.getF32("RenderGlowWidth") / glow_res; + // Use half the glow width if we have the res set to less than 9 so that it looks + // almost the same in either case. + if (glowResPow < 9) + { + delta *= 0.5f; + } + F32 strength = gSavedSettings.getF32("RenderGlowStrength"); + + gGlowProgram.bind(); + gGlowProgram.uniform1f("glowStrength", strength); + + for (S32 i = 0; i < kernel; i++) + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + { + LLFastTimer ftm(FTM_RENDER_BLOOM_FBO); + mGlow[i%2].bindTarget(); + mGlow[i%2].clear(); + } + + if (i == 0) + { + gGL.getTexUnit(0)->bind(&mGlow[2]); + } + else + { + gGL.getTexUnit(0)->bind(&mGlow[(i-1)%2]); + } + + if (i%2 == 0) + { + gGlowProgram.uniform2f("glowDelta", delta, 0); + } + else + { + gGlowProgram.uniform2f("glowDelta", 0, delta); + } + + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); + gGL.vertex2f(-1,-1); + + gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); + gGL.vertex2f(-1,3); + + gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); + gGL.vertex2f(3,-1); + + gGL.end(); + + mGlow[i%2].flush(); + } + + gGlowProgram.unbind(); + + if (LLRenderTarget::sUseFBO) + { + LLFastTimer ftm(FTM_RENDER_BLOOM_FBO); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + } + + gGLViewport[0] = gViewerWindow->getWorldViewRectRaw().mLeft; + gGLViewport[1] = gViewerWindow->getWorldViewRectRaw().mBottom; + gGLViewport[2] = gViewerWindow->getWorldViewRectRaw().getWidth(); + gGLViewport[3] = gViewerWindow->getWorldViewRectRaw().getHeight(); + glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); + + tc2.setVec((F32) gViewerWindow->getWorldViewWidthRaw(), + (F32) gViewerWindow->getWorldViewHeightRaw()); + + gGL.flush(); + + LLVertexBuffer::unbind(); + + if (LLPipeline::sRenderDeferred && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 2) + { + LLGLDisable blend(GL_BLEND); + bindDeferredShader(gDeferredGIFinalProgram); + + S32 channel = gDeferredGIFinalProgram.enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE); + if (channel > -1) + { + mScreen.bindTexture(0, channel); + } + + gGL.begin(LLRender::TRIANGLE_STRIP); + gGL.texCoord2f(tc1.mV[0], tc1.mV[1]); + gGL.vertex2f(-1,-1); + + gGL.texCoord2f(tc1.mV[0], tc2.mV[1]); + gGL.vertex2f(-1,3); + + gGL.texCoord2f(tc2.mV[0], tc1.mV[1]); + gGL.vertex2f(3,-1); + + gGL.end(); + + unbindDeferredShader(gDeferredGIFinalProgram); + } + else + { + + if (res_mod > 1) + { + tc2 /= (F32) res_mod; + } + + U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_TEXCOORD1; + LLPointer buff = new LLVertexBuffer(mask, 0); + buff->allocateBuffer(3,0,TRUE); + + LLStrider v; + LLStrider uv1; + LLStrider uv2; + + buff->getVertexStrider(v); + buff->getTexCoord0Strider(uv1); + buff->getTexCoord1Strider(uv2); + + uv1[0] = LLVector2(0, 0); + uv1[1] = LLVector2(0, 2); + uv1[2] = LLVector2(2, 0); + + uv2[0] = LLVector2(0, 0); + uv2[1] = LLVector2(0, tc2.mV[1]*2.f); + uv2[2] = LLVector2(tc2.mV[0]*2.f, 0); + + v[0] = LLVector3(-1,-1,0); + v[1] = LLVector3(-1,3,0); + v[2] = LLVector3(3,-1,0); + + buff->setBuffer(0); + + LLGLDisable blend(GL_BLEND); + + //tex unit 0 + gGL.getTexUnit(0)->setTextureColorBlend(LLTexUnit::TBO_REPLACE, LLTexUnit::TBS_TEX_COLOR); + + gGL.getTexUnit(0)->bind(&mGlow[1]); + gGL.getTexUnit(1)->activate(); + gGL.getTexUnit(1)->enable(LLTexUnit::TT_RECT_TEXTURE); + + + //tex unit 1 + gGL.getTexUnit(1)->setTextureColorBlend(LLTexUnit::TBO_ADD, LLTexUnit::TBS_TEX_COLOR, LLTexUnit::TBS_PREV_COLOR); + + gGL.getTexUnit(1)->bind(&mScreen); + gGL.getTexUnit(1)->activate(); + + LLGLEnable multisample(GL_MULTISAMPLE_ARB); + + buff->setBuffer(mask); + buff->drawArrays(LLRender::TRIANGLE_STRIP, 0, 3); + + gGL.getTexUnit(1)->disable(); + gGL.getTexUnit(1)->setTextureBlendType(LLTexUnit::TB_MULT); + + gGL.getTexUnit(0)->activate(); + gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); + + if (LLRenderTarget::sUseFBO) + { //copy depth buffer from mScreen to framebuffer + LLRenderTarget::copyContentsToFramebuffer(mScreen, 0, 0, mScreen.getWidth(), mScreen.getHeight(), + 0, 0, mScreen.getWidth(), mScreen.getHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + } + + + gGL.setSceneBlendType(LLRender::BT_ALPHA); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + LLVertexBuffer::unbind(); + + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + +} + +static LLFastTimer::DeclareTimer FTM_BIND_DEFERRED("Bind Deferred"); + +void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, LLRenderTarget* gi_source, LLRenderTarget* last_gi_post, U32 noise_map) +{ + LLFastTimer t(FTM_BIND_DEFERRED); + + if (noise_map == 0xFFFFFFFF) + { + noise_map = mNoiseMap; + } + + LLGLState::checkTextureChannels(); + + shader.bind(); + S32 channel = 0; + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE); + if (channel > -1) + { + mDeferredScreen.bindTexture(0,channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_SPECULAR, LLTexUnit::TT_RECT_TEXTURE); + if (channel > -1) + { + mDeferredScreen.bindTexture(1, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL, LLTexUnit::TT_RECT_TEXTURE); + if (channel > -1) + { + mDeferredScreen.bindTexture(2, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + } + + if (gi_source) + { + BOOL has_gi = FALSE; + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_DIFFUSE); + if (channel > -1) + { + has_gi = TRUE; + gi_source->bindTexture(0, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_SPECULAR); + if (channel > -1) + { + has_gi = TRUE; + gi_source->bindTexture(1, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_NORMAL); + if (channel > -1) + { + has_gi = TRUE; + gi_source->bindTexture(2, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_MIN_POS); + if (channel > -1) + { + has_gi = TRUE; + gi_source->bindTexture(1, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_MAX_POS); + if (channel > -1) + { + has_gi = TRUE; + gi_source->bindTexture(3, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_DIFFUSE); + if (channel > -1) + { + has_gi = TRUE; + last_gi_post->bindTexture(0, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_NORMAL); + if (channel > -1) + { + has_gi = TRUE; + last_gi_post->bindTexture(2, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_MAX_POS); + if (channel > -1) + { + has_gi = TRUE; + last_gi_post->bindTexture(1, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_MIN_POS); + if (channel > -1) + { + has_gi = TRUE; + last_gi_post->bindTexture(3, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_DEPTH); + if (channel > -1) + { + has_gi = TRUE; + gGL.getTexUnit(channel)->bind(gi_source, TRUE); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + stop_glerror(); + + glTexParameteri(LLTexUnit::getInternalType(mGIMap.getUsage()), GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); + glTexParameteri(LLTexUnit::getInternalType(mGIMap.getUsage()), GL_DEPTH_TEXTURE_MODE_ARB, GL_ALPHA); + + stop_glerror(); + } + + if (has_gi) + { + F32 range_x = llmin(mGIRange.mV[0], 1.f); + F32 range_y = llmin(mGIRange.mV[1], 1.f); + + LLVector2 scale(range_x,range_y); + + LLVector2 kern[25]; + + for (S32 i = 0; i < 5; ++i) + { + for (S32 j = 0; j < 5; ++j) + { + S32 idx = i*5+j; + kern[idx].mV[0] = (i-2)*0.5f; + kern[idx].mV[1] = (j-2)*0.5f; + kern[idx].scaleVec(scale); + } + } + + shader.uniform2fv("gi_kern", 25, (F32*) kern); + shader.uniformMatrix4fv("gi_mat", 1, FALSE, mGIMatrix.m); + shader.uniformMatrix4fv("gi_mat_proj", 1, FALSE, mGIMatrixProj.m); + shader.uniformMatrix4fv("gi_inv_proj", 1, FALSE, mGIInvProj.m); + shader.uniformMatrix4fv("gi_norm_mat", 1, FALSE, mGINormalMatrix.m); + } + } + + /*channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_POSITION, LLTexUnit::TT_RECT_TEXTURE); + if (channel > -1) + { + mDeferredScreen.bindTexture(3, channel); + }*/ + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, LLTexUnit::TT_RECT_TEXTURE); + if (channel > -1) + { + gGL.getTexUnit(channel)->bind(&mDeferredDepth, TRUE); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + stop_glerror(); + + glTexParameteri(LLTexUnit::getInternalType(mDeferredDepth.getUsage()), GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); + glTexParameteri(LLTexUnit::getInternalType(mDeferredDepth.getUsage()), GL_DEPTH_TEXTURE_MODE_ARB, GL_ALPHA); + + stop_glerror(); + + glh::matrix4f projection = glh_get_current_projection(); + glh::matrix4f inv_proj = projection.inverse(); + + shader.uniformMatrix4fv("inv_proj", 1, FALSE, inv_proj.m); + shader.uniform4f("viewport", (F32) gGLViewport[0], + (F32) gGLViewport[1], + (F32) gGLViewport[2], + (F32) gGLViewport[3]); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_NOISE); + if (channel > -1) + { + gGL.getTexUnit(channel)->bindManual(LLTexUnit::TT_TEXTURE, noise_map); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_LIGHTFUNC); + if (channel > -1) + { + gGL.getTexUnit(channel)->bindManual(LLTexUnit::TT_TEXTURE, mLightFunc); + } + + stop_glerror(); + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_LIGHT, LLTexUnit::TT_RECT_TEXTURE); + if (channel > -1) + { + mDeferredLight[light_index].bindTexture(0, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_LUMINANCE); + if (channel > -1) + { + gGL.getTexUnit(channel)->bindManual(LLTexUnit::TT_TEXTURE, mLuminanceMap.getTexture(), true); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_TRILINEAR); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_BLOOM); + if (channel > -1) + { + mGlow[1].bindTexture(0, channel); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_GI_LIGHT, LLTexUnit::TT_RECT_TEXTURE); + if (channel > -1) + { + gi_source->bindTexture(0, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_EDGE, LLTexUnit::TT_RECT_TEXTURE); + if (channel > -1) + { + mEdgeMap.bindTexture(0, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_SUN_LIGHT, LLTexUnit::TT_RECT_TEXTURE); + if (channel > -1) + { + mDeferredLight[1].bindTexture(0, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + } + + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_LOCAL_LIGHT, LLTexUnit::TT_RECT_TEXTURE); + if (channel > -1) + { + mDeferredLight[2].bindTexture(0, channel); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + } + + + stop_glerror(); + + for (U32 i = 0; i < 4; i++) + { + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_RECT_TEXTURE); + stop_glerror(); + if (channel > -1) + { + stop_glerror(); + gGL.getTexUnit(channel)->bind(&mShadow[i], TRUE); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); + stop_glerror(); + + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); + stop_glerror(); + } + } + + for (U32 i = 4; i < 6; i++) + { + channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_SHADOW0+i); + stop_glerror(); + if (channel > -1) + { + stop_glerror(); + gGL.getTexUnit(channel)->bind(&mShadow[i], TRUE); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_BILINEAR); + gGL.getTexUnit(channel)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); + stop_glerror(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL); + stop_glerror(); + } + } + + stop_glerror(); + + F32 mat[16*6]; + for (U32 i = 0; i < 16; i++) + { + mat[i] = mSunShadowMatrix[0].m[i]; + mat[i+16] = mSunShadowMatrix[1].m[i]; + mat[i+32] = mSunShadowMatrix[2].m[i]; + mat[i+48] = mSunShadowMatrix[3].m[i]; + mat[i+64] = mSunShadowMatrix[4].m[i]; + mat[i+80] = mSunShadowMatrix[5].m[i]; + } + + shader.uniformMatrix4fv("shadow_matrix[0]", 6, FALSE, mat); + shader.uniformMatrix4fv("shadow_matrix", 6, FALSE, mat); + + stop_glerror(); + + channel = shader.enableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP); + if (channel > -1) + { + LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL; + if (cube_map) + { + cube_map->enable(channel); + cube_map->bind(); + F64* m = gGLModelView; + + + F32 mat[] = { m[0], m[1], m[2], + m[4], m[5], m[6], + m[8], m[9], m[10] }; + + shader.uniform3fv("env_mat[0]", 3, mat); + shader.uniform3fv("env_mat", 3, mat); + } + } + + shader.uniform4fv("shadow_clip", 1, mSunClipPlanes.mV); + shader.uniform1f("sun_wash", gSavedSettings.getF32("RenderDeferredSunWash")); + shader.uniform1f("shadow_noise", gSavedSettings.getF32("RenderShadowNoise")); + shader.uniform1f("blur_size", gSavedSettings.getF32("RenderShadowBlurSize")); + + shader.uniform1f("ssao_radius", gSavedSettings.getF32("RenderSSAOScale")); + shader.uniform1f("ssao_max_radius", gSavedSettings.getU32("RenderSSAOMaxScale")); + + F32 ssao_factor = gSavedSettings.getF32("RenderSSAOFactor"); + shader.uniform1f("ssao_factor", ssao_factor); + shader.uniform1f("ssao_factor_inv", 1.0/ssao_factor); + + LLVector3 ssao_effect = gSavedSettings.getVector3("RenderSSAOEffect"); + F32 matrix_diag = (ssao_effect[0] + 2.0*ssao_effect[1])/3.0; + F32 matrix_nondiag = (ssao_effect[0] - ssao_effect[1])/3.0; + // This matrix scales (proj of color onto <1/rt(3),1/rt(3),1/rt(3)>) by + // value factor, and scales remainder by saturation factor + F32 ssao_effect_mat[] = { matrix_diag, matrix_nondiag, matrix_nondiag, + matrix_nondiag, matrix_diag, matrix_nondiag, + matrix_nondiag, matrix_nondiag, matrix_diag}; + shader.uniformMatrix3fv("ssao_effect_mat", 1, GL_FALSE, ssao_effect_mat); + + F32 shadow_offset_error = 1.f + gSavedSettings.getF32("RenderShadowOffsetError") * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2]); + F32 shadow_bias_error = 1.f + gSavedSettings.getF32("RenderShadowBiasError") * fabsf(LLViewerCamera::getInstance()->getOrigin().mV[2]); + + shader.uniform2f("screen_res", mDeferredScreen.getWidth(), mDeferredScreen.getHeight()); + shader.uniform1f("near_clip", LLViewerCamera::getInstance()->getNear()*2.f); + shader.uniform1f ("shadow_offset", gSavedSettings.getF32("RenderShadowOffset")*shadow_offset_error); + shader.uniform1f("shadow_bias", gSavedSettings.getF32("RenderShadowBias")*shadow_bias_error); + shader.uniform1f ("spot_shadow_offset", gSavedSettings.getF32("RenderSpotShadowOffset")); + shader.uniform1f("spot_shadow_bias", gSavedSettings.getF32("RenderSpotShadowBias")); + + shader.uniform1f("lum_scale", gSavedSettings.getF32("RenderLuminanceScale")); + shader.uniform1f("sun_lum_scale", gSavedSettings.getF32("RenderSunLuminanceScale")); + shader.uniform1f("sun_lum_offset", gSavedSettings.getF32("RenderSunLuminanceOffset")); + shader.uniform1f("lum_lod", gSavedSettings.getF32("RenderLuminanceDetail")); + shader.uniform1f("gi_range", gSavedSettings.getF32("RenderGIRange")); + shader.uniform1f("gi_brightness", gSavedSettings.getF32("RenderGIBrightness")); + shader.uniform1f("gi_luminance", gSavedSettings.getF32("RenderGILuminance")); + shader.uniform1f("gi_edge_weight", gSavedSettings.getF32("RenderGIBlurEdgeWeight")); + shader.uniform1f("gi_blur_brightness", gSavedSettings.getF32("RenderGIBlurBrightness")); + shader.uniform1f("gi_sample_width", mGILightRadius); + shader.uniform1f("gi_noise", gSavedSettings.getF32("RenderGINoise")); + shader.uniform1f("gi_attenuation", gSavedSettings.getF32("RenderGIAttenuation")); + shader.uniform1f("gi_ambiance", gSavedSettings.getF32("RenderGIAmbiance")); + shader.uniform2f("shadow_res", mShadow[0].getWidth(), mShadow[0].getHeight()); + shader.uniform2f("proj_shadow_res", mShadow[4].getWidth(), mShadow[4].getHeight()); + shader.uniform1f("depth_cutoff", gSavedSettings.getF32("RenderEdgeDepthCutoff")); + shader.uniform1f("norm_cutoff", gSavedSettings.getF32("RenderEdgeNormCutoff")); + + if (shader.getUniformLocation("norm_mat") >= 0) + { + glh::matrix4f norm_mat = glh_get_current_modelview().inverse().transpose(); + shader.uniformMatrix4fv("norm_mat", 1, FALSE, norm_mat.m); + } +} + +static LLFastTimer::DeclareTimer FTM_GI_TRACE("Trace"); +static LLFastTimer::DeclareTimer FTM_GI_GATHER("Gather"); +static LLFastTimer::DeclareTimer FTM_SUN_SHADOW("Shadow Map"); +static LLFastTimer::DeclareTimer FTM_SOFTEN_SHADOW("Shadow Soften"); +static LLFastTimer::DeclareTimer FTM_EDGE_DETECTION("Find Edges"); +static LLFastTimer::DeclareTimer FTM_LOCAL_LIGHTS("Local Lights"); +static LLFastTimer::DeclareTimer FTM_ATMOSPHERICS("Atmospherics"); +static LLFastTimer::DeclareTimer FTM_FULLSCREEN_LIGHTS("Fullscreen Lights"); +static LLFastTimer::DeclareTimer FTM_PROJECTORS("Projectors"); +static LLFastTimer::DeclareTimer FTM_POST("Post"); + + +void LLPipeline::renderDeferredLighting() +{ + if (!sCull) + { + return; + } + + { + LLFastTimer ftm(FTM_RENDER_DEFERRED); + + LLViewerCamera* camera = LLViewerCamera::getInstance(); + { + LLGLDepthTest depth(GL_TRUE); + mDeferredDepth.copyContents(mDeferredScreen, 0, 0, mDeferredScreen.getWidth(), mDeferredScreen.getHeight(), + 0, 0, mDeferredDepth.getWidth(), mDeferredDepth.getHeight(), GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + + LLGLEnable multisample(GL_MULTISAMPLE_ARB); + + if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD)) + { + gPipeline.toggleRenderType(LLPipeline::RENDER_TYPE_HUD); + } + + //ati doesn't seem to love actually using the stencil buffer on FBO's + LLGLEnable stencil(GL_STENCIL_TEST); + glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + gGL.setColorMask(true, true); + + //draw a cube around every light + LLVertexBuffer::unbind(); + + LLGLEnable cull(GL_CULL_FACE); + LLGLEnable blend(GL_BLEND); + + glh::matrix4f mat = glh_copy_matrix(gGLModelView); + + F32 vert[] = + { + -1,1, + -1,-3, + 3,1, + }; + glVertexPointer(2, GL_FLOAT, 0, vert); + glColor3f(1,1,1); + + { + setupHWLights(NULL); //to set mSunDir; + LLVector4 dir(mSunDir, 0.f); + glh::vec4f tc(dir.mV); + mat.mult_matrix_vec(tc); + glTexCoord4f(tc.v[0], tc.v[1], tc.v[2], 0); + } + + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + mDeferredLight[0].bindTarget(); + + if (gSavedSettings.getBOOL("RenderDeferredSSAO") || gSavedSettings.getS32("RenderShadowDetail") > 0) + { + { //paint shadow/SSAO light map (direct lighting lightmap) + LLFastTimer ftm(FTM_SUN_SHADOW); + bindDeferredShader(gDeferredSunProgram, 0); + + glClearColor(1,1,1,1); + mDeferredLight[0].clear(GL_COLOR_BUFFER_BIT); + glClearColor(0,0,0,0); + + glh::matrix4f inv_trans = glh_get_current_modelview().inverse().transpose(); + + const U32 slice = 32; + F32 offset[slice*3]; + for (U32 i = 0; i < 4; i++) + { + for (U32 j = 0; j < 8; j++) + { + glh::vec3f v; + v.set_value(sinf(6.284f/8*j), cosf(6.284f/8*j), -(F32) i); + v.normalize(); + inv_trans.mult_matrix_vec(v); + v.normalize(); + offset[(i*8+j)*3+0] = v.v[0]; + offset[(i*8+j)*3+1] = v.v[2]; + offset[(i*8+j)*3+2] = v.v[1]; + } + } + + gDeferredSunProgram.uniform3fv("offset", slice, offset); + gDeferredSunProgram.uniform2f("screenRes", mDeferredLight[0].getWidth(), mDeferredLight[0].getHeight()); + + { + LLGLDisable blend(GL_BLEND); + LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); + stop_glerror(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + stop_glerror(); + } + + unbindDeferredShader(gDeferredSunProgram); + } + } + else + { + glClearColor(1,1,1,1); + mDeferredLight[0].clear(GL_COLOR_BUFFER_BIT); + glClearColor(0,0,0,0); + } + + mDeferredLight[0].flush(); + + { //global illumination specific block (still experimental) + if (gSavedSettings.getBOOL("RenderDeferredBlurLight") && + gSavedSettings.getBOOL("RenderDeferredGI")) + { + LLFastTimer ftm(FTM_EDGE_DETECTION); + //generate edge map + LLGLDisable blend(GL_BLEND); + LLGLDisable test(GL_ALPHA_TEST); + LLGLDepthTest depth(GL_FALSE); + LLGLDisable stencil(GL_STENCIL_TEST); + + { + gDeferredEdgeProgram.bind(); + mEdgeMap.bindTarget(); + bindDeferredShader(gDeferredEdgeProgram); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + unbindDeferredShader(gDeferredEdgeProgram); + mEdgeMap.flush(); + } + } + + if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 2) + { + { //get luminance map from previous frame's light map + LLGLEnable blend(GL_BLEND); + LLGLDisable test(GL_ALPHA_TEST); + LLGLDepthTest depth(GL_FALSE); + LLGLDisable stencil(GL_STENCIL_TEST); + + //static F32 fade = 1.f; + + { + gGL.setSceneBlendType(LLRender::BT_ALPHA); + gLuminanceGatherProgram.bind(); + gLuminanceGatherProgram.uniform2f("screen_res", mDeferredLight[0].getWidth(), mDeferredLight[0].getHeight()); + mLuminanceMap.bindTarget(); + bindDeferredShader(gLuminanceGatherProgram); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + unbindDeferredShader(gLuminanceGatherProgram); + mLuminanceMap.flush(); + gGL.getTexUnit(0)->bindManual(LLTexUnit::TT_TEXTURE, mLuminanceMap.getTexture(), true); + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_TRILINEAR); + glGenerateMipmapEXT(GL_TEXTURE_2D); + } + } + + { //paint noisy GI map (bounce lighting lightmap) + LLFastTimer ftm(FTM_GI_TRACE); + LLGLDisable blend(GL_BLEND); + LLGLDepthTest depth(GL_FALSE); + LLGLDisable test(GL_ALPHA_TEST); + + mGIMapPost[0].bindTarget(); + + bindDeferredShader(gDeferredGIProgram, 0, &mGIMap, 0, mTrueNoiseMap); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + unbindDeferredShader(gDeferredGIProgram); + mGIMapPost[0].flush(); + } + + U32 pass_count = 0; + if (gSavedSettings.getBOOL("RenderDeferredBlurLight")) + { + pass_count = llclamp(gSavedSettings.getU32("RenderGIBlurPasses"), (U32) 1, (U32) 128); + } + + for (U32 i = 0; i < pass_count; ++i) + { //gather/soften indirect lighting map + LLFastTimer ftm(FTM_GI_GATHER); + bindDeferredShader(gDeferredPostGIProgram, 0, &mGIMapPost[0], NULL, mTrueNoiseMap); + F32 blur_size = gSavedSettings.getF32("RenderGIBlurSize")/((F32) i * gSavedSettings.getF32("RenderGIBlurIncrement")+1.f); + gDeferredPostGIProgram.uniform2f("delta", 1.f, 0.f); + gDeferredPostGIProgram.uniform1f("kern_scale", blur_size); + gDeferredPostGIProgram.uniform1f("gi_blur_brightness", gSavedSettings.getF32("RenderGIBlurBrightness")); + + mGIMapPost[1].bindTarget(); + { + LLGLDisable blend(GL_BLEND); + LLGLDepthTest depth(GL_FALSE); + stop_glerror(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + stop_glerror(); + } + + mGIMapPost[1].flush(); + unbindDeferredShader(gDeferredPostGIProgram); + bindDeferredShader(gDeferredPostGIProgram, 0, &mGIMapPost[1], NULL, mTrueNoiseMap); + mGIMapPost[0].bindTarget(); + + gDeferredPostGIProgram.uniform2f("delta", 0.f, 1.f); + + { + LLGLDisable blend(GL_BLEND); + LLGLDepthTest depth(GL_FALSE); + stop_glerror(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + stop_glerror(); + } + mGIMapPost[0].flush(); + unbindDeferredShader(gDeferredPostGIProgram); + } + } + } + + if (gSavedSettings.getBOOL("RenderDeferredSSAO")) + { //soften direct lighting lightmap + LLFastTimer ftm(FTM_SOFTEN_SHADOW); + //blur lightmap + mDeferredLight[1].bindTarget(); + + glClearColor(1,1,1,1); + mDeferredLight[1].clear(GL_COLOR_BUFFER_BIT); + glClearColor(0,0,0,0); + + bindDeferredShader(gDeferredBlurLightProgram); + + LLVector3 go = gSavedSettings.getVector3("RenderShadowGaussian"); + const U32 kern_length = 4; + F32 blur_size = gSavedSettings.getF32("RenderShadowBlurSize"); + F32 dist_factor = gSavedSettings.getF32("RenderShadowBlurDistFactor"); + + // sample symmetrically with the middle sample falling exactly on 0.0 + F32 x = 0.f; + + LLVector3 gauss[32]; // xweight, yweight, offset + + for (U32 i = 0; i < kern_length; i++) + { + gauss[i].mV[0] = llgaussian(x, go.mV[0]); + gauss[i].mV[1] = llgaussian(x, go.mV[1]); + gauss[i].mV[2] = x; + x += 1.f; + } + + gDeferredBlurLightProgram.uniform2f("delta", 1.f, 0.f); + gDeferredBlurLightProgram.uniform1f("dist_factor", dist_factor); + gDeferredBlurLightProgram.uniform3fv("kern[0]", kern_length, gauss[0].mV); + gDeferredBlurLightProgram.uniform3fv("kern", kern_length, gauss[0].mV); + gDeferredBlurLightProgram.uniform1f("kern_scale", blur_size * (kern_length/2.f - 0.5f)); + + { + LLGLDisable blend(GL_BLEND); + LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); + stop_glerror(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + stop_glerror(); + } + + mDeferredLight[1].flush(); + unbindDeferredShader(gDeferredBlurLightProgram); + + bindDeferredShader(gDeferredBlurLightProgram, 1); + mDeferredLight[0].bindTarget(); + + gDeferredBlurLightProgram.uniform2f("delta", 0.f, 1.f); + + { + LLGLDisable blend(GL_BLEND); + LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); + stop_glerror(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + stop_glerror(); + } + mDeferredLight[0].flush(); + unbindDeferredShader(gDeferredBlurLightProgram); + } + + stop_glerror(); + glPopMatrix(); + stop_glerror(); + glMatrixMode(GL_MODELVIEW); + stop_glerror(); + glPopMatrix(); + stop_glerror(); + + //copy depth and stencil from deferred screen + //mScreen.copyContents(mDeferredScreen, 0, 0, mDeferredScreen.getWidth(), mDeferredScreen.getHeight(), + // 0, 0, mScreen.getWidth(), mScreen.getHeight(), GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); + + if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 2) + { + mDeferredLight[1].bindTarget(); + // clear color buffer here (GI) - zeroing alpha (glow) is important or it will accumulate against sky + glClearColor(0,0,0,0); + mScreen.clear(GL_COLOR_BUFFER_BIT); + } + else + { + mScreen.bindTarget(); + // clear color buffer here - zeroing alpha (glow) is important or it will accumulate against sky + glClearColor(0,0,0,0); + mScreen.clear(GL_COLOR_BUFFER_BIT); + } + + if (gSavedSettings.getBOOL("RenderDeferredAtmospheric")) + { //apply sunlight contribution + LLFastTimer ftm(FTM_ATMOSPHERICS); + bindDeferredShader(gDeferredSoftenProgram, 0, &mGIMapPost[0]); + { + LLGLDepthTest depth(GL_FALSE); + LLGLDisable blend(GL_BLEND); + LLGLDisable test(GL_ALPHA_TEST); + + //full screen blit + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + glVertexPointer(2, GL_FLOAT, 0, vert); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + } + + unbindDeferredShader(gDeferredSoftenProgram); + } + + { //render sky + LLGLDisable blend(GL_BLEND); + LLGLDisable stencil(GL_STENCIL_TEST); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + gPipeline.pushRenderTypeMask(); + + gPipeline.andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY, + LLPipeline::RENDER_TYPE_CLOUDS, + LLPipeline::RENDER_TYPE_WL_SKY, + LLPipeline::END_RENDER_TYPES); + + + renderGeomPostDeferred(*LLViewerCamera::getInstance()); + gPipeline.popRenderTypeMask(); + } + + BOOL render_local = gSavedSettings.getBOOL("RenderDeferredLocalLights"); + BOOL render_fullscreen = gSavedSettings.getBOOL("RenderDeferredFullscreenLights"); + + + if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 2) + { + mDeferredLight[1].flush(); + mDeferredLight[2].bindTarget(); + mDeferredLight[2].clear(GL_COLOR_BUFFER_BIT); + } + + if (render_local || render_fullscreen) + { + gGL.setSceneBlendType(LLRender::BT_ADD); + std::list fullscreen_lights; + LLDrawable::drawable_list_t spot_lights; + LLDrawable::drawable_list_t fullscreen_spot_lights; + + for (U32 i = 0; i < 2; i++) + { + mTargetShadowSpotLight[i] = NULL; + } + + std::list light_colors; + + F32 v[24]; + glVertexPointer(3, GL_FLOAT, 0, v); + BOOL render_local = gSavedSettings.getBOOL("RenderDeferredLocalLights"); + + { + bindDeferredShader(gDeferredLightProgram); + LLGLDepthTest depth(GL_TRUE, GL_FALSE); + for (LLDrawable::drawable_set_t::iterator iter = mLights.begin(); iter != mLights.end(); ++iter) + { + LLDrawable* drawablep = *iter; + + LLVOVolume* volume = drawablep->getVOVolume(); + if (!volume) + { + continue; + } + + if (volume->isAttachment()) + { + if (!sRenderAttachedLights) + { + continue; + } + } + + + LLVector3 center = drawablep->getPositionAgent(); + F32* c = center.mV; + F32 s = volume->getLightRadius()*1.5f; + + LLColor3 col = volume->getLightColor(); + col *= volume->getLightIntensity(); + + if (col.magVecSquared() < 0.001f) + { + continue; + } + + if (s <= 0.001f) + { + continue; + } + + if (camera->AABBInFrustumNoFarClip(center, LLVector3(s,s,s)) == 0) + { + continue; + } + + sVisibleLightCount++; + + glh::vec3f tc(c); + mat.mult_matrix_vec(tc); + + //vertex positions are encoded so the 3 bits of their vertex index + //correspond to their axis facing, with bit position 3,2,1 matching + //axis facing x,y,z, bit set meaning positive facing, bit clear + //meaning negative facing + v[0] = c[0]-s; v[1] = c[1]-s; v[2] = c[2]-s; // 0 - 0000 + v[3] = c[0]-s; v[4] = c[1]-s; v[5] = c[2]+s; // 1 - 0001 + v[6] = c[0]-s; v[7] = c[1]+s; v[8] = c[2]-s; // 2 - 0010 + v[9] = c[0]-s; v[10] = c[1]+s; v[11] = c[2]+s; // 3 - 0011 + + v[12] = c[0]+s; v[13] = c[1]-s; v[14] = c[2]-s; // 4 - 0100 + v[15] = c[0]+s; v[16] = c[1]-s; v[17] = c[2]+s; // 5 - 0101 + v[18] = c[0]+s; v[19] = c[1]+s; v[20] = c[2]-s; // 6 - 0110 + v[21] = c[0]+s; v[22] = c[1]+s; v[23] = c[2]+s; // 7 - 0111 + + if (camera->getOrigin().mV[0] > c[0] + s + 0.2f || + camera->getOrigin().mV[0] < c[0] - s - 0.2f || + camera->getOrigin().mV[1] > c[1] + s + 0.2f || + camera->getOrigin().mV[1] < c[1] - s - 0.2f || + camera->getOrigin().mV[2] > c[2] + s + 0.2f || + camera->getOrigin().mV[2] < c[2] - s - 0.2f) + { //draw box if camera is outside box + if (render_local) + { + if (volume->isLightSpotlight()) + { + drawablep->getVOVolume()->updateSpotLightPriority(); + spot_lights.push_back(drawablep); + continue; + } + + LLFastTimer ftm(FTM_LOCAL_LIGHTS); + glTexCoord4f(tc.v[0], tc.v[1], tc.v[2], s*s); + glColor4f(col.mV[0], col.mV[1], col.mV[2], volume->getLightFalloff()*0.5f); + glDrawRangeElements(GL_TRIANGLE_FAN, 0, 7, 8, + GL_UNSIGNED_BYTE, get_box_fan_indices(camera, center)); + stop_glerror(); + } + } + else if (render_fullscreen) + { + if (volume->isLightSpotlight()) + { + drawablep->getVOVolume()->updateSpotLightPriority(); + fullscreen_spot_lights.push_back(drawablep); + continue; + } + + fullscreen_lights.push_back(LLVector4(tc.v[0], tc.v[1], tc.v[2], s*s)); + light_colors.push_back(LLVector4(col.mV[0], col.mV[1], col.mV[2], volume->getLightFalloff()*0.5f)); + } + } + unbindDeferredShader(gDeferredLightProgram); + } + + if (!spot_lights.empty()) + { + LLGLDepthTest depth(GL_TRUE, GL_FALSE); + bindDeferredShader(gDeferredSpotLightProgram); + + gDeferredSpotLightProgram.enableTexture(LLViewerShaderMgr::DEFERRED_PROJECTION); + + for (LLDrawable::drawable_list_t::iterator iter = spot_lights.begin(); iter != spot_lights.end(); ++iter) + { + LLFastTimer ftm(FTM_PROJECTORS); + LLDrawable* drawablep = *iter; + + LLVOVolume* volume = drawablep->getVOVolume(); + + LLVector3 center = drawablep->getPositionAgent(); + F32* c = center.mV; + F32 s = volume->getLightRadius()*1.5f; + + sVisibleLightCount++; + + glh::vec3f tc(c); + mat.mult_matrix_vec(tc); + + setupSpotLight(gDeferredSpotLightProgram, drawablep); + + LLColor3 col = volume->getLightColor(); + col *= volume->getLightIntensity(); + + //vertex positions are encoded so the 3 bits of their vertex index + //correspond to their axis facing, with bit position 3,2,1 matching + //axis facing x,y,z, bit set meaning positive facing, bit clear + //meaning negative facing + v[0] = c[0]-s; v[1] = c[1]-s; v[2] = c[2]-s; // 0 - 0000 + v[3] = c[0]-s; v[4] = c[1]-s; v[5] = c[2]+s; // 1 - 0001 + v[6] = c[0]-s; v[7] = c[1]+s; v[8] = c[2]-s; // 2 - 0010 + v[9] = c[0]-s; v[10] = c[1]+s; v[11] = c[2]+s; // 3 - 0011 + + v[12] = c[0]+s; v[13] = c[1]-s; v[14] = c[2]-s; // 4 - 0100 + v[15] = c[0]+s; v[16] = c[1]-s; v[17] = c[2]+s; // 5 - 0101 + v[18] = c[0]+s; v[19] = c[1]+s; v[20] = c[2]-s; // 6 - 0110 + v[21] = c[0]+s; v[22] = c[1]+s; v[23] = c[2]+s; // 7 - 0111 + + glTexCoord4f(tc.v[0], tc.v[1], tc.v[2], s*s); + glColor4f(col.mV[0], col.mV[1], col.mV[2], volume->getLightFalloff()*0.5f); + glDrawRangeElements(GL_TRIANGLE_FAN, 0, 7, 8, + GL_UNSIGNED_BYTE, get_box_fan_indices(camera, center)); + } + gDeferredSpotLightProgram.disableTexture(LLViewerShaderMgr::DEFERRED_PROJECTION); + unbindDeferredShader(gDeferredSpotLightProgram); + } + + { + bindDeferredShader(gDeferredMultiLightProgram); + + LLGLDepthTest depth(GL_FALSE); + + //full screen blit + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + U32 count = 0; + + const U32 max_count = 8; + LLVector4 light[max_count]; + LLVector4 col[max_count]; + + glVertexPointer(2, GL_FLOAT, 0, vert); + + F32 far_z = 0.f; + + while (!fullscreen_lights.empty()) + { + LLFastTimer ftm(FTM_FULLSCREEN_LIGHTS); + light[count] = fullscreen_lights.front(); + fullscreen_lights.pop_front(); + col[count] = light_colors.front(); + light_colors.pop_front(); + + far_z = llmin(light[count].mV[2]-sqrtf(light[count].mV[3]), far_z); + + count++; + if (count == max_count || fullscreen_lights.empty()) + { + gDeferredMultiLightProgram.uniform1i("light_count", count); + gDeferredMultiLightProgram.uniform4fv("light[0]", count, (GLfloat*) light); + gDeferredMultiLightProgram.uniform4fv("light", count, (GLfloat*) light); + gDeferredMultiLightProgram.uniform4fv("light_col[0]", count, (GLfloat*) col); + gDeferredMultiLightProgram.uniform4fv("light_col", count, (GLfloat*) col); + gDeferredMultiLightProgram.uniform1f("far_z", far_z); + far_z = 0.f; + count = 0; + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + } + } + + unbindDeferredShader(gDeferredMultiLightProgram); + + bindDeferredShader(gDeferredMultiSpotLightProgram); + + gDeferredMultiSpotLightProgram.enableTexture(LLViewerShaderMgr::DEFERRED_PROJECTION); + + for (LLDrawable::drawable_list_t::iterator iter = fullscreen_spot_lights.begin(); iter != fullscreen_spot_lights.end(); ++iter) + { + LLFastTimer ftm(FTM_PROJECTORS); + LLDrawable* drawablep = *iter; + + LLVOVolume* volume = drawablep->getVOVolume(); + + LLVector3 center = drawablep->getPositionAgent(); + F32* c = center.mV; + F32 s = volume->getLightRadius()*1.5f; + + sVisibleLightCount++; + + glh::vec3f tc(c); + mat.mult_matrix_vec(tc); + + setupSpotLight(gDeferredMultiSpotLightProgram, drawablep); + + LLColor3 col = volume->getLightColor(); + col *= volume->getLightIntensity(); + + glTexCoord4f(tc.v[0], tc.v[1], tc.v[2], s*s); + glColor4f(col.mV[0], col.mV[1], col.mV[2], volume->getLightFalloff()*0.5f); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + } + + gDeferredMultiSpotLightProgram.disableTexture(LLViewerShaderMgr::DEFERRED_PROJECTION); + unbindDeferredShader(gDeferredMultiSpotLightProgram); + + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + } + } + + gGL.setColorMask(true, true); + + if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 2) + { + mDeferredLight[2].flush(); + + mScreen.bindTarget(); + mScreen.clear(GL_COLOR_BUFFER_BIT); + + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + { //mix various light maps (local, sun, gi) + LLFastTimer ftm(FTM_POST); + LLGLDisable blend(GL_BLEND); + LLGLDisable test(GL_ALPHA_TEST); + LLGLDepthTest depth(GL_FALSE); + LLGLDisable stencil(GL_STENCIL_TEST); + + bindDeferredShader(gDeferredPostProgram, 0, &mGIMapPost[0]); + + gDeferredPostProgram.bind(); + + LLVertexBuffer::unbind(); + + glVertexPointer(2, GL_FLOAT, 0, vert); + glColor3f(1,1,1); + + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + glDrawArrays(GL_TRIANGLES, 0, 3); + + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + unbindDeferredShader(gDeferredPostProgram); + } + } + } + + { //render non-deferred geometry (alpha, fullbright, glow) + LLGLDisable blend(GL_BLEND); + LLGLDisable stencil(GL_STENCIL_TEST); + + pushRenderTypeMask(); + andRenderTypeMask(LLPipeline::RENDER_TYPE_ALPHA, + LLPipeline::RENDER_TYPE_FULLBRIGHT, + LLPipeline::RENDER_TYPE_VOLUME, + LLPipeline::RENDER_TYPE_GLOW, + LLPipeline::RENDER_TYPE_BUMP, + LLPipeline::RENDER_TYPE_PASS_SIMPLE, + LLPipeline::RENDER_TYPE_PASS_ALPHA, + LLPipeline::RENDER_TYPE_PASS_ALPHA_MASK, + LLPipeline::RENDER_TYPE_PASS_BUMP, + LLPipeline::RENDER_TYPE_PASS_POST_BUMP, + LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT, + LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_ALPHA_MASK, + LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_SHINY, + LLPipeline::RENDER_TYPE_PASS_GLOW, + LLPipeline::RENDER_TYPE_PASS_GRASS, + LLPipeline::RENDER_TYPE_PASS_SHINY, + LLPipeline::RENDER_TYPE_PASS_INVISIBLE, + LLPipeline::RENDER_TYPE_PASS_INVISI_SHINY, + LLPipeline::RENDER_TYPE_AVATAR, + END_RENDER_TYPES); + + renderGeomPostDeferred(*LLViewerCamera::getInstance()); + popRenderTypeMask(); + } + + { + //render highlights, etc. + renderHighlights(); + mHighlightFaces.clear(); + + renderDebug(); + + LLVertexBuffer::unbind(); + + if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + // Render debugging beacons. + gObjectList.renderObjectBeacons(); + gObjectList.resetObjectBeacons(); + } + } + + mScreen.flush(); + +} + +void LLPipeline::setupSpotLight(LLGLSLShader& shader, LLDrawable* drawablep) +{ + //construct frustum + LLVOVolume* volume = drawablep->getVOVolume(); + LLVector3 params = volume->getSpotLightParams(); + + F32 fov = params.mV[0]; + F32 focus = params.mV[1]; + + LLVector3 pos = drawablep->getPositionAgent(); + LLQuaternion quat = volume->getRenderRotation(); + LLVector3 scale = volume->getScale(); + + //get near clip plane + LLVector3 at_axis(0,0,-scale.mV[2]*0.5f); + at_axis *= quat; + + LLVector3 np = pos+at_axis; + at_axis.normVec(); + + //get origin that has given fov for plane np, at_axis, and given scale + F32 dist = (scale.mV[1]*0.5f)/tanf(fov*0.5f); + + LLVector3 origin = np - at_axis*dist; + + //matrix from volume space to agent space + LLMatrix4 light_mat(quat, LLVector4(origin,1.f)); + + glh::matrix4f light_to_agent((F32*) light_mat.mMatrix); + glh::matrix4f light_to_screen = glh_get_current_modelview() * light_to_agent; + + glh::matrix4f screen_to_light = light_to_screen.inverse(); + + F32 s = volume->getLightRadius()*1.5f; + F32 near_clip = dist; + F32 width = scale.mV[VX]; + F32 height = scale.mV[VY]; + F32 far_clip = s+dist-scale.mV[VZ]; + + F32 fovy = fov * RAD_TO_DEG; + F32 aspect = width/height; + + glh::matrix4f trans(0.5f, 0.f, 0.f, 0.5f, + 0.f, 0.5f, 0.f, 0.5f, + 0.f, 0.f, 0.5f, 0.5f, + 0.f, 0.f, 0.f, 1.f); + + glh::vec3f p1(0, 0, -(near_clip+0.01f)); + glh::vec3f p2(0, 0, -(near_clip+1.f)); + + glh::vec3f screen_origin(0, 0, 0); + + light_to_screen.mult_matrix_vec(p1); + light_to_screen.mult_matrix_vec(p2); + light_to_screen.mult_matrix_vec(screen_origin); + + glh::vec3f n = p2-p1; + n.normalize(); + + F32 proj_range = far_clip - near_clip; + glh::matrix4f light_proj = gl_perspective(fovy, aspect, near_clip, far_clip); + screen_to_light = trans * light_proj * screen_to_light; + shader.uniformMatrix4fv("proj_mat", 1, FALSE, screen_to_light.m); + shader.uniform1f("proj_near", near_clip); + shader.uniform3fv("proj_p", 1, p1.v); + shader.uniform3fv("proj_n", 1, n.v); + shader.uniform3fv("proj_origin", 1, screen_origin.v); + shader.uniform1f("proj_range", proj_range); + shader.uniform1f("proj_ambiance", params.mV[2]); + S32 s_idx = -1; + + for (U32 i = 0; i < 2; i++) + { + if (mShadowSpotLight[i] == drawablep) + { + s_idx = i; + } + } + + shader.uniform1i("proj_shadow_idx", s_idx); + + if (s_idx >= 0) + { + shader.uniform1f("shadow_fade", 1.f-mSpotLightFade[s_idx]); + } + else + { + shader.uniform1f("shadow_fade", 1.f); + } + + { + LLDrawable* potential = drawablep; + //determine if this is a good light for casting shadows + F32 m_pri = volume->getSpotLightPriority(); + + for (U32 i = 0; i < 2; i++) + { + F32 pri = 0.f; + + if (mTargetShadowSpotLight[i].notNull()) + { + pri = mTargetShadowSpotLight[i]->getVOVolume()->getSpotLightPriority(); + } + + if (m_pri > pri) + { + LLDrawable* temp = mTargetShadowSpotLight[i]; + mTargetShadowSpotLight[i] = potential; + potential = temp; + m_pri = pri; + } + } + } + + LLViewerTexture* img = volume->getLightTexture(); + + S32 channel = shader.enableTexture(LLViewerShaderMgr::DEFERRED_PROJECTION); + + if (channel > -1 && img) + { + gGL.getTexUnit(channel)->bind(img); + + F32 lod_range = logf(img->getWidth())/logf(2.f); + + shader.uniform1f("proj_focus", focus); + shader.uniform1f("proj_lod", lod_range); + shader.uniform1f("proj_ambient_lod", llclamp((proj_range-focus)/proj_range*lod_range, 0.f, 1.f)); + } +} + +void LLPipeline::unbindDeferredShader(LLGLSLShader &shader) +{ + stop_glerror(); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_POSITION, LLTexUnit::TT_RECT_TEXTURE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_NORMAL, LLTexUnit::TT_RECT_TEXTURE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_DIFFUSE, LLTexUnit::TT_RECT_TEXTURE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_SPECULAR, LLTexUnit::TT_RECT_TEXTURE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_DEPTH, LLTexUnit::TT_RECT_TEXTURE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_LIGHT, LLTexUnit::TT_RECT_TEXTURE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_LIGHT, LLTexUnit::TT_RECT_TEXTURE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_EDGE, LLTexUnit::TT_RECT_TEXTURE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_SUN_LIGHT, LLTexUnit::TT_RECT_TEXTURE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_LOCAL_LIGHT, LLTexUnit::TT_RECT_TEXTURE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_LUMINANCE); + shader.disableTexture(LLViewerShaderMgr::DIFFUSE_MAP); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_MIP); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_BLOOM); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_NORMAL); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_DIFFUSE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_SPECULAR); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_DEPTH); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_MIN_POS); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_MAX_POS); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_NORMAL); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_DIFFUSE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_MIN_POS); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_GI_LAST_MAX_POS); + + for (U32 i = 0; i < 4; i++) + { + if (shader.disableTexture(LLViewerShaderMgr::DEFERRED_SHADOW0+i, LLTexUnit::TT_RECT_TEXTURE) > -1) + { + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); + } + } + + for (U32 i = 4; i < 6; i++) + { + if (shader.disableTexture(LLViewerShaderMgr::DEFERRED_SHADOW0+i) > -1) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE); + } + } + + shader.disableTexture(LLViewerShaderMgr::DEFERRED_NOISE); + shader.disableTexture(LLViewerShaderMgr::DEFERRED_LIGHTFUNC); + + S32 channel = shader.disableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP); + if (channel > -1) + { + LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL; + if (cube_map) + { + cube_map->disable(); + } + } + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(0)->activate(); + shader.unbind(); + + LLGLState::checkTextureChannels(); +} + +inline float sgn(float a) +{ + if (a > 0.0F) return (1.0F); + if (a < 0.0F) return (-1.0F); + return (0.0F); +} + +void LLPipeline::generateWaterReflection(LLCamera& camera_in) +{ + if (LLPipeline::sWaterReflections && assertInitialized() && LLDrawPoolWater::sNeedsReflectionUpdate) + { + BOOL skip_avatar_update = FALSE; + if (!isAgentAvatarValid() || gAgentCamera.getCameraAnimating() || gAgentCamera.getCameraMode() != CAMERA_MODE_MOUSELOOK) + { + skip_avatar_update = TRUE; + } + + if (!skip_avatar_update) + { + gAgentAvatarp->updateAttachmentVisibility(CAMERA_MODE_THIRD_PERSON); + } + LLVertexBuffer::unbind(); + + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + LLGLState::checkClientArrays(); + + LLCamera camera = camera_in; + camera.setFar(camera.getFar()*0.87654321f); + LLPipeline::sReflectionRender = TRUE; + S32 occlusion = LLPipeline::sUseOcclusion; + + LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_WORLD; + + LLPipeline::sUseOcclusion = llmin(occlusion, 1); + + gPipeline.pushRenderTypeMask(); + + glh::matrix4f projection = glh_get_current_projection(); + glh::matrix4f mat; + + stop_glerror(); + LLPlane plane; + + F32 height = gAgent.getRegion()->getWaterHeight(); + F32 to_clip = fabsf(camera.getOrigin().mV[2]-height); + F32 pad = -to_clip*0.05f; //amount to "pad" clip plane by + + //plane params + LLVector3 pnorm; + F32 pd; + + S32 water_clip = 0; + if (!LLViewerCamera::getInstance()->cameraUnderWater()) + { //camera is above water, clip plane points up + pnorm.setVec(0,0,1); + pd = -height; + plane.setVec(pnorm, pd); + water_clip = -1; + } + else + { //camera is below water, clip plane points down + pnorm = LLVector3(0,0,-1); + pd = height; + plane.setVec(pnorm, pd); + water_clip = 1; + } + + if (!LLViewerCamera::getInstance()->cameraUnderWater()) + { //generate planar reflection map + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + glClearColor(0,0,0,0); + mWaterRef.bindTarget(); + gGL.setColorMask(true, true); + mWaterRef.clear(); + gGL.setColorMask(true, false); + + mWaterRef.getViewport(gGLViewport); + + stop_glerror(); + + glPushMatrix(); + + mat.set_scale(glh::vec3f(1,1,-1)); + mat.set_translate(glh::vec3f(0,0,height*2.f)); + + glh::matrix4f current = glh_get_current_modelview(); + + mat = current * mat; + + glh_set_current_modelview(mat); + glLoadMatrixf(mat.m); + + LLViewerCamera::updateFrustumPlanes(camera, FALSE, TRUE); + + glh::matrix4f inv_mat = mat.inverse(); + + glh::vec3f origin(0,0,0); + inv_mat.mult_matrix_vec(origin); + + camera.setOrigin(origin.v); + + glCullFace(GL_FRONT); + + static LLCullResult ref_result; + + if (LLDrawPoolWater::sNeedsDistortionUpdate) + { + //initial sky pass (no user clip plane) + { //mask out everything but the sky + gPipeline.pushRenderTypeMask(); + gPipeline.andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY, + LLPipeline::RENDER_TYPE_WL_SKY, + LLPipeline::END_RENDER_TYPES); + static LLCullResult result; + updateCull(camera, result); + stateSort(camera, result); + andRenderTypeMask(LLPipeline::RENDER_TYPE_SKY, + LLPipeline::RENDER_TYPE_CLOUDS, + LLPipeline::RENDER_TYPE_WL_SKY, + LLPipeline::END_RENDER_TYPES); + + renderGeom(camera, TRUE); + gPipeline.popRenderTypeMask(); + } + + gPipeline.pushRenderTypeMask(); + + clearRenderTypeMask(LLPipeline::RENDER_TYPE_WATER, + LLPipeline::RENDER_TYPE_VOIDWATER, + LLPipeline::RENDER_TYPE_GROUND, + LLPipeline::RENDER_TYPE_SKY, + LLPipeline::RENDER_TYPE_CLOUDS, + LLPipeline::END_RENDER_TYPES); + + S32 detail = gSavedSettings.getS32("RenderReflectionDetail"); + if (detail > 0) + { //mask out selected geometry based on reflection detail + if (detail < 4) + { + clearRenderTypeMask(LLPipeline::RENDER_TYPE_PARTICLES, END_RENDER_TYPES); + if (detail < 3) + { + clearRenderTypeMask(LLPipeline::RENDER_TYPE_AVATAR, END_RENDER_TYPES); + if (detail < 2) + { + clearRenderTypeMask(LLPipeline::RENDER_TYPE_VOLUME, END_RENDER_TYPES); + } + } + } + + LLGLUserClipPlane clip_plane(plane, mat, projection); + LLGLDisable cull(GL_CULL_FACE); + updateCull(camera, ref_result, 1); + stateSort(camera, ref_result); + } + + if (LLDrawPoolWater::sNeedsDistortionUpdate) + { + if (gSavedSettings.getS32("RenderReflectionDetail") > 0) + { + gPipeline.grabReferences(ref_result); + LLGLUserClipPlane clip_plane(plane, mat, projection); + renderGeom(camera); + } + } + + gPipeline.popRenderTypeMask(); + } + glCullFace(GL_BACK); + glPopMatrix(); + mWaterRef.flush(); + glh_set_current_modelview(current); + } + + camera.setOrigin(camera_in.getOrigin()); + //render distortion map + static BOOL last_update = TRUE; + if (last_update) + { + camera.setFar(camera_in.getFar()); + clearRenderTypeMask(LLPipeline::RENDER_TYPE_WATER, + LLPipeline::RENDER_TYPE_VOIDWATER, + LLPipeline::RENDER_TYPE_GROUND, + END_RENDER_TYPES); + stop_glerror(); + + LLPipeline::sUnderWaterRender = LLViewerCamera::getInstance()->cameraUnderWater() ? FALSE : TRUE; + + if (LLPipeline::sUnderWaterRender) + { + clearRenderTypeMask(LLPipeline::RENDER_TYPE_GROUND, + LLPipeline::RENDER_TYPE_SKY, + LLPipeline::RENDER_TYPE_CLOUDS, + LLPipeline::RENDER_TYPE_WL_SKY, + END_RENDER_TYPES); + } + LLViewerCamera::updateFrustumPlanes(camera); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + LLColor4& col = LLDrawPoolWater::sWaterFogColor; + glClearColor(col.mV[0], col.mV[1], col.mV[2], 0.f); + mWaterDis.bindTarget(); + mWaterDis.getViewport(gGLViewport); + + if (!LLPipeline::sUnderWaterRender || LLDrawPoolWater::sNeedsReflectionUpdate) + { + //clip out geometry on the same side of water as the camera + mat = glh_get_current_modelview(); + LLGLUserClipPlane clip_plane(LLPlane(-pnorm, -(pd+pad)), mat, projection); + static LLCullResult result; + updateCull(camera, result, water_clip); + stateSort(camera, result); + + gGL.setColorMask(true, true); + mWaterDis.clear(); + gGL.setColorMask(true, false); + + renderGeom(camera); + } + + LLPipeline::sUnderWaterRender = FALSE; + mWaterDis.flush(); + } + last_update = LLDrawPoolWater::sNeedsReflectionUpdate && LLDrawPoolWater::sNeedsDistortionUpdate; + + LLRenderTarget::unbindTarget(); + + LLPipeline::sReflectionRender = FALSE; + + if (!LLRenderTarget::sUseFBO) + { + glClear(GL_DEPTH_BUFFER_BIT); + } + glClearColor(0.f, 0.f, 0.f, 0.f); + gViewerWindow->setup3DViewport(); + gPipeline.popRenderTypeMask(); + LLDrawPoolWater::sNeedsReflectionUpdate = FALSE; + LLDrawPoolWater::sNeedsDistortionUpdate = FALSE; + LLViewerCamera::getInstance()->setUserClipPlane(LLPlane(-pnorm, -pd)); + LLPipeline::sUseOcclusion = occlusion; + + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + LLGLState::checkClientArrays(); + + if (!skip_avatar_update) + { + gAgentAvatarp->updateAttachmentVisibility(gAgentCamera.getCameraMode()); + } + } +} + +glh::matrix4f look(const LLVector3 pos, const LLVector3 dir, const LLVector3 up) +{ + glh::matrix4f ret; + + LLVector3 dirN; + LLVector3 upN; + LLVector3 lftN; + + lftN = dir % up; + lftN.normVec(); + + upN = lftN % dir; + upN.normVec(); + + dirN = dir; + dirN.normVec(); + + ret.m[ 0] = lftN[0]; + ret.m[ 1] = upN[0]; + ret.m[ 2] = -dirN[0]; + ret.m[ 3] = 0.f; + + ret.m[ 4] = lftN[1]; + ret.m[ 5] = upN[1]; + ret.m[ 6] = -dirN[1]; + ret.m[ 7] = 0.f; + + ret.m[ 8] = lftN[2]; + ret.m[ 9] = upN[2]; + ret.m[10] = -dirN[2]; + ret.m[11] = 0.f; + + ret.m[12] = -(lftN*pos); + ret.m[13] = -(upN*pos); + ret.m[14] = dirN*pos; + ret.m[15] = 1.f; + + return ret; +} + +glh::matrix4f scale_translate_to_fit(const LLVector3 min, const LLVector3 max) +{ + glh::matrix4f ret; + ret.m[ 0] = 2/(max[0]-min[0]); + ret.m[ 4] = 0; + ret.m[ 8] = 0; + ret.m[12] = -(max[0]+min[0])/(max[0]-min[0]); + + ret.m[ 1] = 0; + ret.m[ 5] = 2/(max[1]-min[1]); + ret.m[ 9] = 0; + ret.m[13] = -(max[1]+min[1])/(max[1]-min[1]); + + ret.m[ 2] = 0; + ret.m[ 6] = 0; + ret.m[10] = 2/(max[2]-min[2]); + ret.m[14] = -(max[2]+min[2])/(max[2]-min[2]); + + ret.m[ 3] = 0; + ret.m[ 7] = 0; + ret.m[11] = 0; + ret.m[15] = 1; + + return ret; +} + +static LLFastTimer::DeclareTimer FTM_SHADOW_RENDER("Render Shadows"); +static LLFastTimer::DeclareTimer FTM_SHADOW_ALPHA("Alpha Shadow"); +static LLFastTimer::DeclareTimer FTM_SHADOW_SIMPLE("Simple Shadow"); + +void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera& shadow_cam, LLCullResult &result, BOOL use_shader, BOOL use_occlusion) +{ + LLFastTimer t(FTM_SHADOW_RENDER); + + //clip out geometry on the same side of water as the camera + S32 occlude = LLPipeline::sUseOcclusion; + if (!use_occlusion) + { + LLPipeline::sUseOcclusion = 0; + } + LLPipeline::sShadowRender = TRUE; + + U32 types[] = { LLRenderPass::PASS_SIMPLE, LLRenderPass::PASS_FULLBRIGHT, LLRenderPass::PASS_SHINY, LLRenderPass::PASS_BUMP, LLRenderPass::PASS_FULLBRIGHT_SHINY }; + LLGLEnable cull(GL_CULL_FACE); + + if (use_shader) + { + gDeferredShadowProgram.bind(); + } + + updateCull(shadow_cam, result); + stateSort(shadow_cam, result); + + //generate shadow map + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(proj.m); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixf(view.m); + + stop_glerror(); + gGLLastMatrix = NULL; + + { + LLGLDepthTest depth(GL_TRUE); + glClear(GL_DEPTH_BUFFER_BIT); + } + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + glColor4f(1,1,1,1); + + stop_glerror(); + + gGL.setColorMask(false, false); + + //glCullFace(GL_FRONT); + + { + LLFastTimer ftm(FTM_SHADOW_SIMPLE); + LLGLDisable test(GL_ALPHA_TEST); + gGL.getTexUnit(0)->disable(); + for (U32 i = 0; i < sizeof(types)/sizeof(U32); ++i) + { + renderObjects(types[i], LLVertexBuffer::MAP_VERTEX, FALSE); + } + gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); + } + + if (use_shader) + { + gDeferredShadowProgram.unbind(); + renderGeomShadow(shadow_cam); + gDeferredShadowProgram.bind(); + } + else + { + renderGeomShadow(shadow_cam); + } + + { + LLFastTimer ftm(FTM_SHADOW_ALPHA); + LLGLEnable test(GL_ALPHA_TEST); + gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.6f); + renderObjects(LLRenderPass::PASS_ALPHA_SHADOW, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_COLOR, TRUE); + glColor4f(1,1,1,1); + renderObjects(LLRenderPass::PASS_GRASS, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0, TRUE); + gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); + } + + //glCullFace(GL_BACK); + + gGLLastMatrix = NULL; + glLoadMatrixd(gGLModelView); + doOcclusion(shadow_cam); + + if (use_shader) + { + gDeferredShadowProgram.unbind(); + } + + gGL.setColorMask(true, true); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + gGLLastMatrix = NULL; + + LLPipeline::sUseOcclusion = occlude; + LLPipeline::sShadowRender = FALSE; +} + +static LLFastTimer::DeclareTimer FTM_VISIBLE_CLOUD("Visible Cloud"); +BOOL LLPipeline::getVisiblePointCloud(LLCamera& camera, LLVector3& min, LLVector3& max, std::vector& fp, LLVector3 light_dir) +{ + LLFastTimer t(FTM_VISIBLE_CLOUD); + //get point cloud of intersection of frust and min, max + + if (getVisibleExtents(camera, min, max)) + { + return FALSE; + } + + //get set of planes on bounding box + std::vector bp; + + bp.push_back(LLPlane(min, LLVector3(-1,0,0))); + bp.push_back(LLPlane(min, LLVector3(0,-1,0))); + bp.push_back(LLPlane(min, LLVector3(0,0,-1))); + bp.push_back(LLPlane(max, LLVector3(1,0,0))); + bp.push_back(LLPlane(max, LLVector3(0,1,0))); + bp.push_back(LLPlane(max, LLVector3(0,0,1))); + + //potential points + std::vector pp; + + //add corners of AABB + pp.push_back(LLVector3(min.mV[0], min.mV[1], min.mV[2])); + pp.push_back(LLVector3(max.mV[0], min.mV[1], min.mV[2])); + pp.push_back(LLVector3(min.mV[0], max.mV[1], min.mV[2])); + pp.push_back(LLVector3(max.mV[0], max.mV[1], min.mV[2])); + pp.push_back(LLVector3(min.mV[0], min.mV[1], max.mV[2])); + pp.push_back(LLVector3(max.mV[0], min.mV[1], max.mV[2])); + pp.push_back(LLVector3(min.mV[0], max.mV[1], max.mV[2])); + pp.push_back(LLVector3(max.mV[0], max.mV[1], max.mV[2])); + + //add corners of camera frustum + for (U32 i = 0; i < 8; i++) + { + pp.push_back(camera.mAgentFrustum[i]); + } + + + //bounding box line segments + U32 bs[] = + { + 0,1, + 1,3, + 3,2, + 2,0, + + 4,5, + 5,7, + 7,6, + 6,4, + + 0,4, + 1,5, + 3,7, + 2,6 + }; + + for (U32 i = 0; i < 12; i++) + { //for each line segment in bounding box + for (U32 j = 0; j < 6; j++) + { //for each plane in camera frustum + const LLPlane& cp = camera.getAgentPlane(j); + const LLVector3& v1 = pp[bs[i*2+0]]; + const LLVector3& v2 = pp[bs[i*2+1]]; + const LLVector3 n(cp.mV); + + LLVector3 line = v1-v2; + + F32 d1 = line*n; + F32 d2 = -cp.dist(v2); + + F32 t = d2/d1; + + if (t > 0.f && t < 1.f) + { + LLVector3 intersect = v2+line*t; + pp.push_back(intersect); + } + } + } + + //camera frustum line segments + const U32 fs[] = + { + 0,1, + 1,2, + 2,3, + 3,1, + + 4,5, + 5,6, + 6,7, + 7,4, + + 0,4, + 1,5, + 2,6, + 3,7 + }; + + LLVector3 center = (max+min)*0.5f; + LLVector3 size = (max-min)*0.5f; + + for (U32 i = 0; i < 12; i++) + { + for (U32 j = 0; j < 6; ++j) + { + const LLVector3& v1 = pp[fs[i*2+0]+8]; + const LLVector3& v2 = pp[fs[i*2+1]+8]; + const LLPlane& cp = bp[j]; + const LLVector3 n(cp.mV); + + LLVector3 line = v1-v2; + + F32 d1 = line*n; + F32 d2 = -cp.dist(v2); + + F32 t = d2/d1; + + if (t > 0.f && t < 1.f) + { + LLVector3 intersect = v2+line*t; + pp.push_back(intersect); + } + } + } + + LLVector3 ext[] = { min-LLVector3(0.05f,0.05f,0.05f), + max+LLVector3(0.05f,0.05f,0.05f) }; + + for (U32 i = 0; i < pp.size(); ++i) + { + bool found = true; + + const F32* p = pp[i].mV; + + for (U32 j = 0; j < 3; ++j) + { + if (p[j] < ext[0].mV[j] || + p[j] > ext[1].mV[j]) + { + found = false; + break; + } + } + + for (U32 j = 0; j < 6; ++j) + { + const LLPlane& cp = camera.getAgentPlane(j); + F32 dist = cp.dist(pp[i]); + if (dist > 0.05f) //point is above some plane, not contained + { + found = false; + break; + } + } + + if (found) + { + fp.push_back(pp[i]); + } + } + + if (fp.empty()) + { + return FALSE; + } + + return TRUE; +} + +void LLPipeline::generateGI(LLCamera& camera, LLVector3& lightDir, std::vector& vpc) +{ + if (LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) < 3) + { + return; + } + + LLVector3 up; + + //LLGLEnable depth_clamp(GL_DEPTH_CLAMP_NV); + + if (lightDir.mV[2] > 0.5f) + { + up = LLVector3(1,0,0); + } + else + { + up = LLVector3(0, 0, 1); + } + + + F32 gi_range = gSavedSettings.getF32("RenderGIRange"); + + U32 res = mGIMap.getWidth(); + + F32 atten = llmax(gSavedSettings.getF32("RenderGIAttenuation"), 0.001f); + + //set radius to range at which distance attenuation of incoming photons is near 0 + + F32 lrad = sqrtf(1.f/(atten*0.01f)); + + F32 lrange = lrad+gi_range*0.5f; + + LLVector3 pad(lrange,lrange,lrange); + + glh::matrix4f view = look(LLVector3(128.f,128.f,128.f), lightDir, up); + + LLVector3 cp = camera.getOrigin()+camera.getAtAxis()*(gi_range*0.5f); + + glh::vec3f scp(cp.mV); + view.mult_matrix_vec(scp); + cp.setVec(scp.v); + + F32 pix_width = lrange/(res*0.5f); + + //move cp to the nearest pix_width + for (U32 i = 0; i < 3; i++) + { + cp.mV[i] = llround(cp.mV[i], pix_width); + } + + LLVector3 min = cp-pad; + LLVector3 max = cp+pad; + + //set mGIRange to range in tc space[0,1] that covers texture block of intersecting lights around a point + mGIRange.mV[0] = (max.mV[0]-min.mV[0])/res; + mGIRange.mV[1] = (max.mV[1]-min.mV[1])/res; + mGILightRadius = lrad/lrange*0.5f; + + glh::matrix4f proj = gl_ortho(min.mV[0], max.mV[0], + min.mV[1], max.mV[1], + -max.mV[2], -min.mV[2]); + + LLCamera sun_cam = camera; + + glh::matrix4f eye_view = glh_get_current_modelview(); + + //get eye space to camera space matrix + mGIMatrix = view*eye_view.inverse(); + mGINormalMatrix = mGIMatrix.inverse().transpose(); + mGIInvProj = proj.inverse(); + mGIMatrixProj = proj*mGIMatrix; + + //translate and scale to [0,1] + glh::matrix4f trans(.5f, 0.f, 0.f, .5f, + 0.f, 0.5f, 0.f, 0.5f, + 0.f, 0.f, 0.5f, 0.5f, + 0.f, 0.f, 0.f, 1.f); + + mGIMatrixProj = trans*mGIMatrixProj; + + glh_set_current_modelview(view); + glh_set_current_projection(proj); + + LLViewerCamera::updateFrustumPlanes(sun_cam, TRUE, FALSE, TRUE); + + sun_cam.ignoreAgentFrustumPlane(LLCamera::AGENT_PLANE_NEAR); + static LLCullResult result; + + pushRenderTypeMask(); + + andRenderTypeMask(LLPipeline::RENDER_TYPE_SIMPLE, + LLPipeline::RENDER_TYPE_FULLBRIGHT, + LLPipeline::RENDER_TYPE_BUMP, + LLPipeline::RENDER_TYPE_VOLUME, + LLPipeline::RENDER_TYPE_TREE, + LLPipeline::RENDER_TYPE_TERRAIN, + LLPipeline::RENDER_TYPE_WATER, + LLPipeline::RENDER_TYPE_VOIDWATER, + LLPipeline::RENDER_TYPE_PASS_ALPHA_SHADOW, + LLPipeline::RENDER_TYPE_AVATAR, + LLPipeline::RENDER_TYPE_PASS_SIMPLE, + LLPipeline::RENDER_TYPE_PASS_BUMP, + LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT, + LLPipeline::RENDER_TYPE_PASS_SHINY, + END_RENDER_TYPES); + + + + S32 occlude = LLPipeline::sUseOcclusion; + //LLPipeline::sUseOcclusion = 0; + LLPipeline::sShadowRender = TRUE; + + //only render large objects into GI map + sMinRenderSize = gSavedSettings.getF32("RenderGIMinRenderSize"); + + LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_GI_SOURCE; + mGIMap.bindTarget(); + + F64 last_modelview[16]; + F64 last_projection[16]; + for (U32 i = 0; i < 16; i++) + { + last_modelview[i] = gGLLastModelView[i]; + last_projection[i] = gGLLastProjection[i]; + gGLLastModelView[i] = mGIModelview.m[i]; + gGLLastProjection[i] = mGIProjection.m[i]; + } + + sun_cam.setOrigin(0.f, 0.f, 0.f); + updateCull(sun_cam, result); + stateSort(sun_cam, result); + + for (U32 i = 0; i < 16; i++) + { + gGLLastModelView[i] = last_modelview[i]; + gGLLastProjection[i] = last_projection[i]; + } + + mGIProjection = proj; + mGIModelview = view; + + LLGLEnable cull(GL_CULL_FACE); + + //generate GI map + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadMatrixf(proj.m); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadMatrixf(view.m); + + stop_glerror(); + gGLLastMatrix = NULL; + + mGIMap.clear(); + + { + //LLGLEnable enable(GL_DEPTH_CLAMP_NV); + renderGeomDeferred(camera); + } + + mGIMap.flush(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + gGLLastMatrix = NULL; + + LLPipeline::sUseOcclusion = occlude; + LLPipeline::sShadowRender = FALSE; + sMinRenderSize = 0.f; + + popRenderTypeMask(); + +} + +void LLPipeline::renderHighlight(const LLViewerObject* obj, F32 fade) +{ + if (obj && obj->getVolume()) + { + for (LLViewerObject::child_list_t::const_iterator iter = obj->getChildren().begin(); iter != obj->getChildren().end(); ++iter) + { + renderHighlight(*iter, fade); + } + + LLDrawable* drawable = obj->mDrawable; + if (drawable) + { + for (S32 i = 0; i < drawable->getNumFaces(); ++i) + { + LLFace* face = drawable->getFace(i); + if (face) + { + face->renderSelected(LLViewerTexture::sNullImagep, LLColor4(1,1,1,fade)); + } + } + } + } +} + +void LLPipeline::generateHighlight(LLCamera& camera) +{ + //render highlighted object as white into offscreen render target + if (mHighlightObject.notNull()) + { + mHighlightSet.insert(HighlightItem(mHighlightObject)); + } + + if (!mHighlightSet.empty()) + { + F32 transition = gFrameIntervalSeconds/gSavedSettings.getF32("RenderHighlightFadeTime"); + + LLGLDisable test(GL_ALPHA_TEST); + LLGLDepthTest depth(GL_FALSE); + mHighlight.bindTarget(); + disableLights(); + gGL.setColorMask(true, true); + mHighlight.clear(); + + gGL.getTexUnit(0)->bind(LLViewerFetchedTexture::sWhiteImagep); + for (std::set::iterator iter = mHighlightSet.begin(); iter != mHighlightSet.end(); ) + { + std::set::iterator cur_iter = iter++; + + if (cur_iter->mItem.isNull()) + { + mHighlightSet.erase(cur_iter); + continue; + } + + if (cur_iter->mItem == mHighlightObject) + { + cur_iter->incrFade(transition); + } + else + { + cur_iter->incrFade(-transition); + if (cur_iter->mFade <= 0.f) + { + mHighlightSet.erase(cur_iter); + continue; + } + } + + renderHighlight(cur_iter->mItem->getVObj(), cur_iter->mFade); + } + + mHighlight.flush(); + gGL.setColorMask(true, false); + gViewerWindow->setup3DViewport(); + } +} + + +void LLPipeline::generateSunShadow(LLCamera& camera) +{ + if (!sRenderDeferred || gSavedSettings.getS32("RenderShadowDetail") <= 0) + { + return; + } + + F64 last_modelview[16]; + F64 last_projection[16]; + for (U32 i = 0; i < 16; i++) + { //store last_modelview of world camera + last_modelview[i] = gGLLastModelView[i]; + last_projection[i] = gGLLastProjection[i]; + } + + pushRenderTypeMask(); + andRenderTypeMask(LLPipeline::RENDER_TYPE_SIMPLE, + LLPipeline::RENDER_TYPE_ALPHA, + LLPipeline::RENDER_TYPE_GRASS, + LLPipeline::RENDER_TYPE_FULLBRIGHT, + LLPipeline::RENDER_TYPE_BUMP, + LLPipeline::RENDER_TYPE_VOLUME, + LLPipeline::RENDER_TYPE_AVATAR, + LLPipeline::RENDER_TYPE_TREE, + LLPipeline::RENDER_TYPE_TERRAIN, + LLPipeline::RENDER_TYPE_WATER, + LLPipeline::RENDER_TYPE_VOIDWATER, + LLPipeline::RENDER_TYPE_PASS_ALPHA_SHADOW, + LLPipeline::RENDER_TYPE_PASS_SIMPLE, + LLPipeline::RENDER_TYPE_PASS_BUMP, + LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT, + LLPipeline::RENDER_TYPE_PASS_SHINY, + LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_SHINY, + END_RENDER_TYPES); + + gGL.setColorMask(false, false); + + //get sun view matrix + + //store current projection/modelview matrix + glh::matrix4f saved_proj = glh_get_current_projection(); + glh::matrix4f saved_view = glh_get_current_modelview(); + glh::matrix4f inv_view = saved_view.inverse(); + + glh::matrix4f view[6]; + glh::matrix4f proj[6]; + + //clip contains parallel split distances for 3 splits + LLVector3 clip = gSavedSettings.getVector3("RenderShadowClipPlanes"); + + //F32 slope_threshold = gSavedSettings.getF32("RenderShadowSlopeThreshold"); + + //far clip on last split is minimum of camera view distance and 128 + mSunClipPlanes = LLVector4(clip, clip.mV[2] * clip.mV[2]/clip.mV[1]); + + clip = gSavedSettings.getVector3("RenderShadowOrthoClipPlanes"); + mSunOrthoClipPlanes = LLVector4(clip, clip.mV[2]*clip.mV[2]/clip.mV[1]); + + //currently used for amount to extrude frusta corners for constructing shadow frusta + LLVector3 n = gSavedSettings.getVector3("RenderShadowNearDist"); + //F32 nearDist[] = { n.mV[0], n.mV[1], n.mV[2], n.mV[2] }; + + LLVector3 lightDir = -mSunDir; + lightDir.normVec(); + + glh::vec3f light_dir(lightDir.mV); + + //create light space camera matrix + + LLVector3 at = lightDir; + + LLVector3 up = camera.getAtAxis(); + + if (fabsf(up*lightDir) > 0.75f) + { + up = camera.getUpAxis(); + } + + /*LLVector3 left = up%at; + up = at%left;*/ + + up.normVec(); + at.normVec(); + + + LLCamera main_camera = camera; + + F32 near_clip = 0.f; + { + //get visible point cloud + std::vector fp; + + main_camera.calcAgentFrustumPlanes(main_camera.mAgentFrustum); + + LLVector3 min,max; + getVisiblePointCloud(main_camera,min,max,fp); + + if (fp.empty()) + { + if (!hasRenderDebugMask(RENDER_DEBUG_SHADOW_FRUSTA)) + { + mShadowCamera[0] = main_camera; + mShadowExtents[0][0] = min; + mShadowExtents[0][1] = max; + + mShadowFrustPoints[0].clear(); + mShadowFrustPoints[1].clear(); + mShadowFrustPoints[2].clear(); + mShadowFrustPoints[3].clear(); + } + popRenderTypeMask(); + return; + } + + generateGI(camera, lightDir, fp); + + //get good split distances for frustum + for (U32 i = 0; i < fp.size(); ++i) + { + glh::vec3f v(fp[i].mV); + saved_view.mult_matrix_vec(v); + fp[i].setVec(v.v); + } + + min = fp[0]; + max = fp[0]; + + //get camera space bounding box + for (U32 i = 1; i < fp.size(); ++i) + { + update_min_max(min, max, fp[i]); + } + + near_clip = -max.mV[2]; + F32 far_clip = -min.mV[2]*2.f; + + far_clip = llmin(far_clip, 128.f); + far_clip = llmin(far_clip, camera.getFar()); + + F32 range = far_clip-near_clip; + + LLVector3 split_exp = gSavedSettings.getVector3("RenderShadowSplitExponent"); + + F32 da = 1.f-llmax( fabsf(lightDir*up), fabsf(lightDir*camera.getLeftAxis()) ); + + da = powf(da, split_exp.mV[2]); + + + F32 sxp = split_exp.mV[1] + (split_exp.mV[0]-split_exp.mV[1])*da; + + + for (U32 i = 0; i < 4; ++i) + { + F32 x = (F32)(i+1)/4.f; + x = powf(x, sxp); + mSunClipPlanes.mV[i] = near_clip+range*x; + } + } + + // convenience array of 4 near clip plane distances + F32 dist[] = { near_clip, mSunClipPlanes.mV[0], mSunClipPlanes.mV[1], mSunClipPlanes.mV[2], mSunClipPlanes.mV[3] }; + + for (S32 j = 0; j < 4; j++) + { + if (!hasRenderDebugMask(RENDER_DEBUG_SHADOW_FRUSTA)) + { + mShadowFrustPoints[j].clear(); + } + + LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_SHADOW0+j; + + //restore render matrices + glh_set_current_modelview(saved_view); + glh_set_current_projection(saved_proj); + + LLVector3 eye = camera.getOrigin(); + + //camera used for shadow cull/render + LLCamera shadow_cam; + + //create world space camera frustum for this split + shadow_cam = camera; + shadow_cam.setFar(16.f); + + LLViewerCamera::updateFrustumPlanes(shadow_cam, FALSE, FALSE, TRUE); + + LLVector3* frust = shadow_cam.mAgentFrustum; + + LLVector3 pn = shadow_cam.getAtAxis(); + + LLVector3 min, max; + + //construct 8 corners of split frustum section + for (U32 i = 0; i < 4; i++) + { + LLVector3 delta = frust[i+4]-eye; + delta += (frust[i+4]-frust[(i+2)%4+4])*0.05f; + delta.normVec(); + F32 dp = delta*pn; + frust[i] = eye + (delta*dist[j]*0.95f)/dp; + frust[i+4] = eye + (delta*dist[j+1]*1.05f)/dp; + } + + shadow_cam.calcAgentFrustumPlanes(frust); + shadow_cam.mFrustumCornerDist = 0.f; + + if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA)) + { + mShadowCamera[j] = shadow_cam; + } + + std::vector fp; + + if (!gPipeline.getVisiblePointCloud(shadow_cam, min, max, fp, lightDir)) + { + //no possible shadow receivers + if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA)) + { + mShadowExtents[j][0] = LLVector3(); + mShadowExtents[j][1] = LLVector3(); + mShadowCamera[j+4] = shadow_cam; + } + + mShadow[j].bindTarget(); + { + LLGLDepthTest depth(GL_TRUE); + mShadow[j].clear(); + } + mShadow[j].flush(); + + mShadowError.mV[j] = 0.f; + mShadowFOV.mV[j] = 0.f; + + continue; + } + + if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA)) + { + mShadowExtents[j][0] = min; + mShadowExtents[j][1] = max; + mShadowFrustPoints[j] = fp; + } + + + //find a good origin for shadow projection + LLVector3 origin; + + //get a temporary view projection + view[j] = look(camera.getOrigin(), lightDir, -up); + + std::vector wpf; + + for (U32 i = 0; i < fp.size(); i++) + { + glh::vec3f p = glh::vec3f(fp[i].mV); + view[j].mult_matrix_vec(p); + wpf.push_back(LLVector3(p.v)); + } + + min = wpf[0]; + max = wpf[0]; + + for (U32 i = 0; i < fp.size(); ++i) + { //get AABB in camera space + update_min_max(min, max, wpf[i]); + } + + // Construct a perspective transform with perspective along y-axis that contains + // points in wpf + //Known: + // - far clip plane + // - near clip plane + // - points in frustum + //Find: + // - origin + + //get some "interesting" points of reference + LLVector3 center = (min+max)*0.5f; + LLVector3 size = (max-min)*0.5f; + LLVector3 near_center = center; + near_center.mV[1] += size.mV[1]*2.f; + + + //put all points in wpf in quadrant 0, reletive to center of min/max + //get the best fit line using least squares + F32 bfm = 0.f; + F32 bfb = 0.f; + + for (U32 i = 0; i < wpf.size(); ++i) + { + wpf[i] -= center; + wpf[i].mV[0] = fabsf(wpf[i].mV[0]); + wpf[i].mV[2] = fabsf(wpf[i].mV[2]); + } + + if (!wpf.empty()) + { + F32 sx = 0.f; + F32 sx2 = 0.f; + F32 sy = 0.f; + F32 sxy = 0.f; + + for (U32 i = 0; i < wpf.size(); ++i) + { + sx += wpf[i].mV[0]; + sx2 += wpf[i].mV[0]*wpf[i].mV[0]; + sy += wpf[i].mV[1]; + sxy += wpf[i].mV[0]*wpf[i].mV[1]; + } + + bfm = (sy*sx-wpf.size()*sxy)/(sx*sx-wpf.size()*sx2); + bfb = (sx*sxy-sy*sx2)/(sx*sx-bfm*sx2); + } + + { + // best fit line is y=bfm*x+bfb + + //find point that is furthest to the right of line + F32 off_x = -1.f; + LLVector3 lp; + + for (U32 i = 0; i < wpf.size(); ++i) + { + //y = bfm*x+bfb + //x = (y-bfb)/bfm + F32 lx = (wpf[i].mV[1]-bfb)/bfm; + + lx = wpf[i].mV[0]-lx; + + if (off_x < lx) + { + off_x = lx; + lp = wpf[i]; + } + } + + //get line with slope bfm through lp + // bfb = y-bfm*x + bfb = lp.mV[1]-bfm*lp.mV[0]; + + //calculate error + mShadowError.mV[j] = 0.f; + + for (U32 i = 0; i < wpf.size(); ++i) + { + F32 lx = (wpf[i].mV[1]-bfb)/bfm; + mShadowError.mV[j] += fabsf(wpf[i].mV[0]-lx); + } + + mShadowError.mV[j] /= wpf.size(); + mShadowError.mV[j] /= size.mV[0]; + + if (mShadowError.mV[j] > gSavedSettings.getF32("RenderShadowErrorCutoff")) + { //just use ortho projection + mShadowFOV.mV[j] = -1.f; + origin.clearVec(); + proj[j] = gl_ortho(min.mV[0], max.mV[0], + min.mV[1], max.mV[1], + -max.mV[2], -min.mV[2]); + } + else + { + //origin is where line x = 0; + origin.setVec(0,bfb,0); + + F32 fovz = 1.f; + F32 fovx = 1.f; + + LLVector3 zp; + LLVector3 xp; + + for (U32 i = 0; i < wpf.size(); ++i) + { + LLVector3 atz = wpf[i]-origin; + atz.mV[0] = 0.f; + atz.normVec(); + if (fovz > -atz.mV[1]) + { + zp = wpf[i]; + fovz = -atz.mV[1]; + } + + LLVector3 atx = wpf[i]-origin; + atx.mV[2] = 0.f; + atx.normVec(); + if (fovx > -atx.mV[1]) + { + fovx = -atx.mV[1]; + xp = wpf[i]; + } + } + + fovx = acos(fovx); + fovz = acos(fovz); + + F32 cutoff = llmin(gSavedSettings.getF32("RenderShadowFOVCutoff"), 1.4f); + + mShadowFOV.mV[j] = fovx; + + if (fovx < cutoff && fovz > cutoff) + { + //x is a good fit, but z is too big, move away from zp enough so that fovz matches cutoff + F32 d = zp.mV[2]/tan(cutoff); + F32 ny = zp.mV[1] + fabsf(d); + + origin.mV[1] = ny; + + fovz = 1.f; + fovx = 1.f; + + for (U32 i = 0; i < wpf.size(); ++i) + { + LLVector3 atz = wpf[i]-origin; + atz.mV[0] = 0.f; + atz.normVec(); + fovz = llmin(fovz, -atz.mV[1]); + + LLVector3 atx = wpf[i]-origin; + atx.mV[2] = 0.f; + atx.normVec(); + fovx = llmin(fovx, -atx.mV[1]); + } + + fovx = acos(fovx); + fovz = acos(fovz); + + if (fovx > cutoff || llround(fovz, 0.01f) > cutoff) + { + // llerrs << "WTF?" << llendl; + } + + mShadowFOV.mV[j] = cutoff; + } + + + origin += center; + + F32 ynear = -(max.mV[1]-origin.mV[1]); + F32 yfar = -(min.mV[1]-origin.mV[1]); + + if (ynear < 0.1f) //keep a sensible near clip plane + { + F32 diff = 0.1f-ynear; + origin.mV[1] += diff; + ynear += diff; + yfar += diff; + } + + if (fovx > cutoff) + { //just use ortho projection + origin.clearVec(); + mShadowError.mV[j] = -1.f; + proj[j] = gl_ortho(min.mV[0], max.mV[0], + min.mV[1], max.mV[1], + -max.mV[2], -min.mV[2]); + } + else + { + //get perspective projection + view[j] = view[j].inverse(); + + glh::vec3f origin_agent(origin.mV); + + //translate view to origin + view[j].mult_matrix_vec(origin_agent); + + eye = LLVector3(origin_agent.v); + + if (!hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA)) + { + mShadowFrustOrigin[j] = eye; + } + + view[j] = look(LLVector3(origin_agent.v), lightDir, -up); + + F32 fx = 1.f/tanf(fovx); + F32 fz = 1.f/tanf(fovz); + + proj[j] = glh::matrix4f(-fx, 0, 0, 0, + 0, (yfar+ynear)/(ynear-yfar), 0, (2.f*yfar*ynear)/(ynear-yfar), + 0, 0, -fz, 0, + 0, -1.f, 0, 0); + } + } + } + + shadow_cam.setFar(128.f); + shadow_cam.setOriginAndLookAt(eye, up, center); + + shadow_cam.setOrigin(0,0,0); + + glh_set_current_modelview(view[j]); + glh_set_current_projection(proj[j]); + + LLViewerCamera::updateFrustumPlanes(shadow_cam, FALSE, FALSE, TRUE); + + shadow_cam.ignoreAgentFrustumPlane(LLCamera::AGENT_PLANE_NEAR); + + //translate and scale to from [-1, 1] to [0, 1] + glh::matrix4f trans(0.5f, 0.f, 0.f, 0.5f, + 0.f, 0.5f, 0.f, 0.5f, + 0.f, 0.f, 0.5f, 0.5f, + 0.f, 0.f, 0.f, 1.f); + + glh_set_current_modelview(view[j]); + glh_set_current_projection(proj[j]); + + for (U32 i = 0; i < 16; i++) + { + gGLLastModelView[i] = mShadowModelview[j].m[i]; + gGLLastProjection[i] = mShadowProjection[j].m[i]; + } + + mShadowModelview[j] = view[j]; + mShadowProjection[j] = proj[j]; + + + mSunShadowMatrix[j] = trans*proj[j]*view[j]*inv_view; + + stop_glerror(); + + mShadow[j].bindTarget(); + mShadow[j].getViewport(gGLViewport); + + { + static LLCullResult result[4]; + + //LLGLEnable enable(GL_DEPTH_CLAMP_NV); + renderShadow(view[j], proj[j], shadow_cam, result[j], TRUE); + } + + mShadow[j].flush(); + + if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_SHADOW_FRUSTA)) + { + LLViewerCamera::updateFrustumPlanes(shadow_cam, FALSE, FALSE, TRUE); + mShadowCamera[j+4] = shadow_cam; + } + } + + + //hack to disable projector shadows + static bool clear = true; + bool gen_shadow = gSavedSettings.getS32("RenderShadowDetail") > 1; + + if (gen_shadow) + { + clear = true; + F32 fade_amt = gFrameIntervalSeconds * llmax(LLViewerCamera::getInstance()->getVelocityStat()->getCurrentPerSec(), 1.f); + + //update shadow targets + for (U32 i = 0; i < 2; i++) + { //for each current shadow + LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_SHADOW4+i; + + if (mShadowSpotLight[i].notNull() && + (mShadowSpotLight[i] == mTargetShadowSpotLight[0] || + mShadowSpotLight[i] == mTargetShadowSpotLight[1])) + { //keep this spotlight + mSpotLightFade[i] = llmin(mSpotLightFade[i]+fade_amt, 1.f); + } + else + { //fade out this light + mSpotLightFade[i] = llmax(mSpotLightFade[i]-fade_amt, 0.f); + + if (mSpotLightFade[i] == 0.f || mShadowSpotLight[i].isNull()) + { //faded out, grab one of the pending spots (whichever one isn't already taken) + if (mTargetShadowSpotLight[0] != mShadowSpotLight[(i+1)%2]) + { + mShadowSpotLight[i] = mTargetShadowSpotLight[0]; + } + else + { + mShadowSpotLight[i] = mTargetShadowSpotLight[1]; + } + } + } + } + + for (S32 i = 0; i < 2; i++) + { + glh_set_current_modelview(saved_view); + glh_set_current_projection(saved_proj); + + if (mShadowSpotLight[i].isNull()) + { + continue; + } + + LLVOVolume* volume = mShadowSpotLight[i]->getVOVolume(); + + if (!volume) + { + mShadowSpotLight[i] = NULL; + continue; + } + + LLDrawable* drawable = mShadowSpotLight[i]; + + LLVector3 params = volume->getSpotLightParams(); + F32 fov = params.mV[0]; + + //get agent->light space matrix (modelview) + LLVector3 center = drawable->getPositionAgent(); + LLQuaternion quat = volume->getRenderRotation(); + + //get near clip plane + LLVector3 scale = volume->getScale(); + LLVector3 at_axis(0,0,-scale.mV[2]*0.5f); + at_axis *= quat; + + LLVector3 np = center+at_axis; + at_axis.normVec(); + + //get origin that has given fov for plane np, at_axis, and given scale + F32 dist = (scale.mV[1]*0.5f)/tanf(fov*0.5f); + + LLVector3 origin = np - at_axis*dist; + + LLMatrix4 mat(quat, LLVector4(origin, 1.f)); + + view[i+4] = glh::matrix4f((F32*) mat.mMatrix); + + view[i+4] = view[i+4].inverse(); + + //get perspective matrix + F32 near_clip = dist+0.01f; + F32 width = scale.mV[VX]; + F32 height = scale.mV[VY]; + F32 far_clip = dist+volume->getLightRadius()*1.5f; + + F32 fovy = fov * RAD_TO_DEG; + F32 aspect = width/height; + + proj[i+4] = gl_perspective(fovy, aspect, near_clip, far_clip); + + //translate and scale to from [-1, 1] to [0, 1] + glh::matrix4f trans(0.5f, 0.f, 0.f, 0.5f, + 0.f, 0.5f, 0.f, 0.5f, + 0.f, 0.f, 0.5f, 0.5f, + 0.f, 0.f, 0.f, 1.f); + + glh_set_current_modelview(view[i+4]); + glh_set_current_projection(proj[i+4]); + + mSunShadowMatrix[i+4] = trans*proj[i+4]*view[i+4]*inv_view; + + for (U32 j = 0; j < 16; j++) + { + gGLLastModelView[j] = mShadowModelview[i+4].m[j]; + gGLLastProjection[j] = mShadowProjection[i+4].m[j]; + } + + mShadowModelview[i+4] = view[i+4]; + mShadowProjection[i+4] = proj[i+4]; + + LLCamera shadow_cam = camera; + shadow_cam.setFar(far_clip); + shadow_cam.setOrigin(origin); + + LLViewerCamera::updateFrustumPlanes(shadow_cam, FALSE, FALSE, TRUE); + + stop_glerror(); + + mShadow[i+4].bindTarget(); + mShadow[i+4].getViewport(gGLViewport); + + static LLCullResult result[2]; + + LLViewerCamera::sCurCameraID = LLViewerCamera::CAMERA_SHADOW0+i+4; + + renderShadow(view[i+4], proj[i+4], shadow_cam, result[i], FALSE, FALSE); + + mShadow[i+4].flush(); + } + } + else + { + if (clear) + { + clear = false; + for (U32 i = 4; i < 6; i++) + { + mShadow[i].bindTarget(); + mShadow[i].clear(); + mShadow[i].flush(); + } + } + } + + if (!gSavedSettings.getBOOL("CameraOffset")) + { + glh_set_current_modelview(saved_view); + glh_set_current_projection(saved_proj); + } + else + { + glh_set_current_modelview(view[1]); + glh_set_current_projection(proj[1]); + glLoadMatrixf(view[1].m); + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(proj[1].m); + glMatrixMode(GL_MODELVIEW); + } + gGL.setColorMask(true, false); + + for (U32 i = 0; i < 16; i++) + { + gGLLastModelView[i] = last_modelview[i]; + gGLLastProjection[i] = last_projection[i]; + } + + popRenderTypeMask(); +} + +void LLPipeline::renderGroups(LLRenderPass* pass, U32 type, U32 mask, BOOL texture) +{ + for (LLCullResult::sg_list_t::iterator i = sCull->beginVisibleGroups(); i != sCull->endVisibleGroups(); ++i) + { + LLSpatialGroup* group = *i; + if (!group->isDead() && + (!sUseOcclusion || !group->isOcclusionState(LLSpatialGroup::OCCLUDED)) && + gPipeline.hasRenderType(group->mSpatialPartition->mDrawableType) && + group->mDrawMap.find(type) != group->mDrawMap.end()) + { + pass->renderGroup(group,type,mask,texture); + } + } +} + +void LLPipeline::generateImpostor(LLVOAvatar* avatar) +{ + LLMemType mt_gi(LLMemType::MTYPE_PIPELINE_GENERATE_IMPOSTOR); + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + LLGLState::checkClientArrays(); + + static LLCullResult result; + result.clear(); + grabReferences(result); + + if (!avatar || !avatar->mDrawable) + { + return; + } + + assertInitialized(); + + BOOL muted = LLMuteList::getInstance()->isMuted(avatar->getID()); + + pushRenderTypeMask(); + + if (muted) + { + andRenderTypeMask(LLPipeline::RENDER_TYPE_AVATAR, END_RENDER_TYPES); + } + else + { + andRenderTypeMask(LLPipeline::RENDER_TYPE_VOLUME, + LLPipeline::RENDER_TYPE_AVATAR, + LLPipeline::RENDER_TYPE_BUMP, + LLPipeline::RENDER_TYPE_GRASS, + LLPipeline::RENDER_TYPE_SIMPLE, + LLPipeline::RENDER_TYPE_FULLBRIGHT, + LLPipeline::RENDER_TYPE_ALPHA, + LLPipeline::RENDER_TYPE_INVISIBLE, + LLPipeline::RENDER_TYPE_PASS_SIMPLE, + LLPipeline::RENDER_TYPE_PASS_ALPHA, + LLPipeline::RENDER_TYPE_PASS_ALPHA_MASK, + LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT, + LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_ALPHA_MASK, + LLPipeline::RENDER_TYPE_PASS_FULLBRIGHT_SHINY, + LLPipeline::RENDER_TYPE_PASS_SHINY, + LLPipeline::RENDER_TYPE_PASS_INVISIBLE, + LLPipeline::RENDER_TYPE_PASS_INVISI_SHINY, + END_RENDER_TYPES); + } + + S32 occlusion = sUseOcclusion; + sUseOcclusion = 0; + sReflectionRender = sRenderDeferred ? FALSE : TRUE; + sShadowRender = TRUE; + sImpostorRender = TRUE; + + LLViewerCamera* viewer_camera = LLViewerCamera::getInstance(); + markVisible(avatar->mDrawable, *viewer_camera); + LLVOAvatar::sUseImpostors = FALSE; + + LLVOAvatar::attachment_map_t::iterator iter; + for (iter = avatar->mAttachmentPoints.begin(); + iter != avatar->mAttachmentPoints.end(); + ++iter) + { + LLViewerJointAttachment *attachment = iter->second; + for (LLViewerJointAttachment::attachedobjs_vec_t::iterator attachment_iter = attachment->mAttachedObjects.begin(); + attachment_iter != attachment->mAttachedObjects.end(); + ++attachment_iter) + { + if (LLViewerObject* attached_object = (*attachment_iter)) + { + markVisible(attached_object->mDrawable->getSpatialBridge(), *viewer_camera); + } + } + } + + stateSort(*LLViewerCamera::getInstance(), result); + + const LLVector3* ext = avatar->mDrawable->getSpatialExtents(); + LLVector3 pos(avatar->getRenderPosition()+avatar->getImpostorOffset()); + + LLCamera camera = *viewer_camera; + + camera.lookAt(viewer_camera->getOrigin(), pos, viewer_camera->getUpAxis()); + + LLVector2 tdim; + + LLVector3 half_height = (ext[1]-ext[0])*0.5f; + + LLVector3 left = camera.getLeftAxis(); + left *= left; + left.normalize(); + + LLVector3 up = camera.getUpAxis(); + up *= up; + up.normalize(); + + tdim.mV[0] = fabsf(half_height * left); + tdim.mV[1] = fabsf(half_height * up); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + //glh::matrix4f ortho = gl_ortho(-tdim.mV[0], tdim.mV[0], -tdim.mV[1], tdim.mV[1], 1.0, 256.0); + F32 distance = (pos-camera.getOrigin()).length(); + F32 fov = atanf(tdim.mV[1]/distance)*2.f*RAD_TO_DEG; + F32 aspect = tdim.mV[0]/tdim.mV[1]; //128.f/256.f; + glh::matrix4f persp = gl_perspective(fov, aspect, 1.f, 256.f); + glh_set_current_projection(persp); + glLoadMatrixf(persp.m); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glh::matrix4f mat; + camera.getOpenGLTransform(mat.m); + + mat = glh::matrix4f((GLfloat*) OGL_TO_CFR_ROTATION) * mat; + + glLoadMatrixf(mat.m); + glh_set_current_modelview(mat); + + glClearColor(0.0f,0.0f,0.0f,0.0f); + gGL.setColorMask(true, true); + glStencilMask(0xFFFFFFFF); + glClearStencil(0); + + // get the number of pixels per angle + F32 pa = gViewerWindow->getWindowHeightRaw() / (RAD_TO_DEG * viewer_camera->getView()); + + //get resolution based on angle width and height of impostor (double desired resolution to prevent aliasing) + U32 resY = llmin(nhpo2((U32) (fov*pa)), (U32) 512); + U32 resX = llmin(nhpo2((U32) (atanf(tdim.mV[0]/distance)*2.f*RAD_TO_DEG*pa)), (U32) 512); + + if (!avatar->mImpostor.isComplete() || resX != avatar->mImpostor.getWidth() || + resY != avatar->mImpostor.getHeight()) + { + avatar->mImpostor.allocate(resX,resY,GL_RGBA,TRUE,TRUE); + + if (LLPipeline::sRenderDeferred) + { + addDeferredAttachments(avatar->mImpostor); + } + + gGL.getTexUnit(0)->bind(&avatar->mImpostor); + gGL.getTexUnit(0)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + } + + LLGLEnable stencil(GL_STENCIL_TEST); + glStencilMask(0xFFFFFFFF); + glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + + { + LLGLEnable scissor(GL_SCISSOR_TEST); + glScissor(0, 0, resX, resY); + avatar->mImpostor.bindTarget(); + avatar->mImpostor.clear(); + } + + if (LLPipeline::sRenderDeferred) + { + stop_glerror(); + renderGeomDeferred(camera); + renderGeomPostDeferred(camera); + } + else + { + renderGeom(camera); + } + + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, 1, 0xFFFFFF); + + { //create alpha mask based on stencil buffer (grey out if muted) + LLVector3 left = camera.getLeftAxis()*tdim.mV[0]*2.f; + LLVector3 up = camera.getUpAxis()*tdim.mV[1]*2.f; + + if (LLPipeline::sRenderDeferred) + { + GLuint buff = GL_COLOR_ATTACHMENT0_EXT; + glDrawBuffersARB(1, &buff); + } + + LLGLEnable blend(muted ? 0 : GL_BLEND); + + if (muted) + { + gGL.setColorMask(true, true); + } + else + { + gGL.setColorMask(false, true); + } + + gGL.setSceneBlendType(LLRender::BT_ADD); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + LLGLDepthTest depth(GL_FALSE, GL_FALSE); + + gGL.color4f(1,1,1,1); + gGL.color4ub(64,64,64,255); + gGL.begin(LLRender::QUADS); + gGL.vertex3fv((pos+left-up).mV); + gGL.vertex3fv((pos-left-up).mV); + gGL.vertex3fv((pos-left+up).mV); + gGL.vertex3fv((pos+left+up).mV); + gGL.end(); + gGL.flush(); + + gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + + + avatar->mImpostor.flush(); + + avatar->setImpostorDim(tdim); + + LLVOAvatar::sUseImpostors = TRUE; + sUseOcclusion = occlusion; + sReflectionRender = FALSE; + sImpostorRender = FALSE; + sShadowRender = FALSE; + popRenderTypeMask(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + avatar->mNeedsImpostorUpdate = FALSE; + avatar->cacheImpostorValues(); + + LLVertexBuffer::unbind(); + LLGLState::checkStates(); + LLGLState::checkTextureChannels(); + LLGLState::checkClientArrays(); +} + +BOOL LLPipeline::hasRenderBatches(const U32 type) const +{ + return sCull->getRenderMapSize(type) > 0; +} + +LLCullResult::drawinfo_list_t::iterator LLPipeline::beginRenderMap(U32 type) +{ + return sCull->beginRenderMap(type); +} + +LLCullResult::drawinfo_list_t::iterator LLPipeline::endRenderMap(U32 type) +{ + return sCull->endRenderMap(type); +} + +LLCullResult::sg_list_t::iterator LLPipeline::beginAlphaGroups() +{ + return sCull->beginAlphaGroups(); +} + +LLCullResult::sg_list_t::iterator LLPipeline::endAlphaGroups() +{ + return sCull->endAlphaGroups(); +} + +BOOL LLPipeline::hasRenderType(const U32 type) const +{ + // STORM-365 : LLViewerJointAttachment::setAttachmentVisibility() is setting type to 0 to actually mean "do not render" + // We then need to test that value here and return FALSE to prevent attachment to render (in mouselook for instance) + // TODO: reintroduce RENDER_TYPE_NONE in LLRenderTypeMask and initialize its mRenderTypeEnabled[RENDER_TYPE_NONE] to FALSE explicitely + return (type == 0 ? FALSE : mRenderTypeEnabled[type]); +} + +void LLPipeline::setRenderTypeMask(U32 type, ...) +{ + va_list args; + + va_start(args, type); + while (type < END_RENDER_TYPES) + { + mRenderTypeEnabled[type] = TRUE; + type = va_arg(args, U32); + } + va_end(args); + + if (type > END_RENDER_TYPES) + { + llerrs << "Invalid render type." << llendl; + } +} + +BOOL LLPipeline::hasAnyRenderType(U32 type, ...) const +{ + va_list args; + + va_start(args, type); + while (type < END_RENDER_TYPES) + { + if (mRenderTypeEnabled[type]) + { + return TRUE; + } + type = va_arg(args, U32); + } + va_end(args); + + if (type > END_RENDER_TYPES) + { + llerrs << "Invalid render type." << llendl; + } + + return FALSE; +} + +void LLPipeline::pushRenderTypeMask() +{ + std::string cur_mask; + cur_mask.assign((const char*) mRenderTypeEnabled, sizeof(mRenderTypeEnabled)); + mRenderTypeEnableStack.push(cur_mask); +} + +void LLPipeline::popRenderTypeMask() +{ + if (mRenderTypeEnableStack.empty()) + { + llerrs << "Depleted render type stack." << llendl; + } + + memcpy(mRenderTypeEnabled, mRenderTypeEnableStack.top().data(), sizeof(mRenderTypeEnabled)); + mRenderTypeEnableStack.pop(); +} + +void LLPipeline::andRenderTypeMask(U32 type, ...) +{ + va_list args; + + BOOL tmp[NUM_RENDER_TYPES]; + for (U32 i = 0; i < NUM_RENDER_TYPES; ++i) + { + tmp[i] = FALSE; + } + + va_start(args, type); + while (type < END_RENDER_TYPES) + { + if (mRenderTypeEnabled[type]) + { + tmp[type] = TRUE; + } + + type = va_arg(args, U32); + } + va_end(args); + + if (type > END_RENDER_TYPES) + { + llerrs << "Invalid render type." << llendl; + } + + for (U32 i = 0; i < LLPipeline::NUM_RENDER_TYPES; ++i) + { + mRenderTypeEnabled[i] = tmp[i]; + } + +} + +void LLPipeline::clearRenderTypeMask(U32 type, ...) +{ + va_list args; + + va_start(args, type); + while (type < END_RENDER_TYPES) + { + mRenderTypeEnabled[type] = FALSE; + + type = va_arg(args, U32); + } + va_end(args); + + if (type > END_RENDER_TYPES) + { + llerrs << "Invalid render type." << llendl; + } +} + + diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index cef3d87f36..e99b0d71e3 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -111,6 +111,7 @@ public: void resetVertexBuffers(LLDrawable* drawable); void setUseVBO(BOOL use_vbo); + void setDisableVBOMapping(BOOL no_vbo_mapping); void generateImpostor(LLVOAvatar* avatar); void bindScreenToTexture(); void renderBloom(BOOL for_snapshot, F32 zoom_factor = 1.f, int subfield = 0); diff --git a/indra/newview/skins/default/textures/bottomtray/ChatBarHandle.png b/indra/newview/skins/default/textures/bottomtray/ChatBarHandle.png new file mode 100644 index 0000000000..8b58db0cba Binary files /dev/null and b/indra/newview/skins/default/textures/bottomtray/ChatBarHandle.png differ diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 7b3cc7bdfa..cec2942b35 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -115,6 +115,7 @@ with the same filename but different name + diff --git a/indra/newview/skins/default/xui/da/floater_about_land.xml b/indra/newview/skins/default/xui/da/floater_about_land.xml index a096a87928..e78924a1ab 100644 --- a/indra/newview/skins/default/xui/da/floater_about_land.xml +++ b/indra/newview/skins/default/xui/da/floater_about_land.xml @@ -87,15 +87,9 @@ Gå til 'Verden' > 'Om land' eller vælg en anden parcel Ejer: - - Leyla Linden - Gruppe: - - Leyla Linden - + + + þ: [COUNT] + - Mrs. Esbee Linden (esbee.linden) + TestString PleaseIgnore (please.ignore) - Mrs. Erica "Moose" Linden (erica.linden) + TestString PleaseIgnore (please.ignore) - Grumpity's Grumpy Group of Moose + TestString PleaseIgnore + + + + + - Grumpity ProductEngine + TestString PleaseIgnore