diff options
Diffstat (limited to 'indra')
315 files changed, 8001 insertions, 3738 deletions
diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 3be8ebafa8..62ed631e06 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -29,6 +29,9 @@ else() set( USE_AUTOBUILD_3P ON ) endif() +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + include(Variables) include(BuildVersion) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 9017fc2fb4..9d95a23a59 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -43,6 +43,7 @@ set(cmake_SOURCE_FILES Lualibs.cmake Meshoptimizer.cmake NDOF.cmake + NFDE.cmake OPENAL.cmake OpenGL.cmake OpenJPEG.cmake diff --git a/indra/cmake/LLKDU.cmake b/indra/cmake/LLKDU.cmake index 668b07ffab..6d27dcc286 100644 --- a/indra/cmake/LLKDU.cmake +++ b/indra/cmake/LLKDU.cmake @@ -23,4 +23,5 @@ if (USE_KDU) ${AUTOBUILD_INSTALL_DIR}/include/kdu ${LIBS_OPEN_DIR}/llkdu ) + target_compile_definitions(ll::kdu INTERFACE KDU_X86_INTRINSICS=1 KDU_NO_THREADS=1) endif (USE_KDU) diff --git a/indra/cmake/LLWindow.cmake b/indra/cmake/LLWindow.cmake index a5791f1bef..3bdca544ac 100644 --- a/indra/cmake/LLWindow.cmake +++ b/indra/cmake/LLWindow.cmake @@ -9,5 +9,5 @@ include_guard() if (LINUX) # linux uses SDL2 for window and keyboard - target_compile_definitions( ll::SDL2 INTERFACE LL_USE_SDL_KEYBOARD=1 ) + target_compile_definitions( ll::SDL2 INTERFACE LL_USE_SDL_WINDOW=1 LL_USE_SDL_KEYBOARD=1 ) endif (LINUX) diff --git a/indra/cmake/NFDE.cmake b/indra/cmake/NFDE.cmake new file mode 100644 index 0000000000..196ba575b2 --- /dev/null +++ b/indra/cmake/NFDE.cmake @@ -0,0 +1,42 @@ +# -*- cmake -*- +if(LINUX) + set(USE_NFDE ON CACHE BOOL "Use Native File Dialog wrapper library") + set(USE_NFDE_PORTAL ON CACHE BOOL "Use NFDE XDG Portals") +endif() + +include_guard() + +add_library(ll::nfde INTERFACE IMPORTED) +if(USE_NFDE) + include(Prebuilt) + use_prebuilt_binary(nfde) + + target_compile_definitions( ll::nfde INTERFACE LL_NFD=1) + + if (WINDOWS) + target_link_libraries( ll::nfde INTERFACE ${ARCH_PREBUILT_DIRS_RELEASE}/nfd.lib) + elseif (DARWIN) + target_link_libraries( ll::nfde INTERFACE ${ARCH_PREBUILT_DIRS_RELEASE}/libnfd.a) + elseif (LINUX) + if(USE_NFDE_PORTAL) + target_link_libraries( ll::nfde INTERFACE ${ARCH_PREBUILT_DIRS_RELEASE}/libnfd_portal.a) + else() + target_link_libraries( ll::nfde INTERFACE ${ARCH_PREBUILT_DIRS_RELEASE}/libnfd_gtk.a) + endif() + endif () + + if (LINUX) + find_package(PkgConfig REQUIRED) + if(NOT USE_NFDE_PORTAL) + pkg_check_modules(GTK3 REQUIRED gtk+-3.0) + target_link_libraries(ll::nfde INTERFACE ${GTK3_LINK_LIBRARIES}) + else() + pkg_check_modules(DBUS REQUIRED dbus-1) + target_link_libraries(ll::nfde INTERFACE ${DBUS_LINK_LIBRARIES}) + endif() + endif() + + target_include_directories( ll::nfde SYSTEM INTERFACE + ${LIBS_PREBUILT_DIR}/include/nfde + ) +endif() diff --git a/indra/cmake/Python.cmake b/indra/cmake/Python.cmake index da5d2ef22c..39fd21c33f 100644 --- a/indra/cmake/Python.cmake +++ b/indra/cmake/Python.cmake @@ -13,7 +13,7 @@ elseif (WINDOWS) foreach(hive HKEY_CURRENT_USER HKEY_LOCAL_MACHINE) # prefer more recent Python versions to older ones, if multiple versions # are installed - foreach(pyver 3.12 3.11 3.10 3.9 3.8 3.7) + foreach(pyver 3.14 3.13 3.12 3.11 3.10 3.9 3.8 3.7) list(APPEND regpaths "[${hive}\\SOFTWARE\\Python\\PythonCore\\${pyver}\\InstallPath]") endforeach() endforeach() diff --git a/indra/cmake/SDL2.cmake b/indra/cmake/SDL2.cmake index 87195ed108..a464133f3f 100644 --- a/indra/cmake/SDL2.cmake +++ b/indra/cmake/SDL2.cmake @@ -12,11 +12,8 @@ use_prebuilt_binary( SDL2 ) find_library( SDL2_LIBRARY NAMES SDL2 - PATHS "${LIBS_PREBUILT_DIR}/lib/release") -if ( "${SDL2_LIBRARY}" STREQUAL "SDL2_LIBRARY-NOTFOUND" ) - message( FATAL_ERROR "unable to find SDL2_LIBRARY" ) -endif() + PATHS "${LIBS_PREBUILT_DIR}/lib/release" REQUIRED) target_link_libraries( ll::SDL2 INTERFACE "${SDL2_LIBRARY}" ) -target_include_directories( ll::SDL2 SYSTEM INTERFACE "${LIBS_PREBUILT_DIR}/include" ) +target_include_directories( ll::SDL2 SYSTEM INTERFACE "${LIBS_PREBUILT_DIR}/include/SDL2" ) diff --git a/indra/cmake/UI.cmake b/indra/cmake/UI.cmake index ae039b4a47..595f394af4 100644 --- a/indra/cmake/UI.cmake +++ b/indra/cmake/UI.cmake @@ -6,15 +6,22 @@ include(GLIB) add_library( ll::uilibraries INTERFACE IMPORTED ) if (LINUX) - use_prebuilt_binary(fltk) - target_compile_definitions(ll::uilibraries INTERFACE LL_FLTK=1 LL_X11=1 ) + target_compile_definitions(ll::uilibraries INTERFACE LL_X11=1 ) if( USE_CONAN ) return() endif() + include(FindPkgConfig) + pkg_check_modules(WAYLAND_CLIENT wayland-client) + + if( WAYLAND_CLIENT_FOUND ) + target_compile_definitions( ll::uilibraries INTERFACE LL_WAYLAND=1) + else() + message("pkgconfig could not find wayland client, compiling without full wayland support") + endif() + target_link_libraries( ll::uilibraries INTERFACE - fltk Xrender Xcursor Xfixes diff --git a/indra/integration_tests/CMakeLists.txt b/indra/integration_tests/CMakeLists.txt index ced2b3dbcf..1d5f0772b5 100644 --- a/indra/integration_tests/CMakeLists.txt +++ b/indra/integration_tests/CMakeLists.txt @@ -1,8 +1,3 @@ # -*- cmake -*- add_subdirectory(llui_libtest) -IF (LLIMAGE_LIBTEST) - MESSAGE(STATUS "Build llimage_libtest") - add_subdirectory(llimage_libtest) -ELSE (LLIMAGE_LIBTEST) - MESSAGE(STATUS "Skip llimage_libtest") -ENDIF (LLIMAGE_LIBTEST) +add_subdirectory(llimage_libtest) diff --git a/indra/integration_tests/llimage_libtest/CMakeLists.txt b/indra/integration_tests/llimage_libtest/CMakeLists.txt index ee2890778b..e6ff142626 100644 --- a/indra/integration_tests/llimage_libtest/CMakeLists.txt +++ b/indra/integration_tests/llimage_libtest/CMakeLists.txt @@ -1,6 +1,7 @@ # -*- cmake -*- # Integration tests of the llimage library (JPEG2000, PNG, jpeg, etc... images reading and writing) +if (LL_TESTS) project (llimage_libtest) @@ -8,9 +9,7 @@ include(00-Common) include(LLCommon) include(LLImage) include(LLMath) -include(LLImageJ2COJ) include(LLKDU) -include(LLFileSystem) set(llimage_libtest_SOURCE_FILES llimage_libtest.cpp @@ -24,17 +23,9 @@ set(llimage_libtest_HEADER_FILES list(APPEND llimage_libtest_SOURCE_FILES ${llimage_libtest_HEADER_FILES}) add_executable(llimage_libtest - WIN32 - MACOSX_BUNDLE ${llimage_libtest_SOURCE_FILES} ) -set_target_properties(llimage_libtest - PROPERTIES - WIN32_EXECUTABLE - FALSE -) - # Libraries on which this application depends on # Sort by high-level to low-level target_link_libraries(llimage_libtest @@ -42,64 +33,9 @@ target_link_libraries(llimage_libtest llfilesystem llmath llimage - llkdu - llimagej2coj ) - -if (DARWIN) - # Path inside the app bundle where we'll need to copy libraries - set(LLIMAGE_LIBTEST_DESTINATION_DIR - ${CMAKE_CURRENT_BINARY_DIR}/$<IF:$<BOOL:${LL_GENERATOR_IS_MULTI_CONFIG}>,$<CONFIG>,>/llimage_libtest.app/Contents/Resources - ) - # Create the Contents/Resources directory - add_custom_command( - TARGET llimage_libtest POST_BUILD - COMMAND ${CMAKE_COMMAND} - ARGS - -E - make_directory - ${LLIMAGE_LIBTEST_DESTINATION_DIR} - COMMENT "Creating Resources directory in app bundle." - ) -else (DARWIN) - set(LLIMAGE_LIBTEST_DESTINATION_DIR - ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ - ) -endif (DARWIN) - -get_target_property(BUILT_LLCOMMON llcommon LOCATION) -add_custom_command(TARGET llimage_libtest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_LLCOMMON} ${LLIMAGE_LIBTEST_DESTINATION_DIR} - DEPENDS ${BUILT_LLCOMMON} -) - -if (DARWIN) - # Copy the required libraries to the package app - add_custom_command(TARGET llimage_libtest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${AUTOBUILD_INSTALL_DIR}/lib/release/libapr-1.0.dylib ${LLIMAGE_LIBTEST_DESTINATION_DIR} - DEPENDS ${AUTOBUILD_INSTALL_DIR}/lib/release/libapr-1.0.dylib - ) - add_custom_command(TARGET llimage_libtest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${AUTOBUILD_INSTALL_DIR}/lib/release/libaprutil-1.0.dylib ${LLIMAGE_LIBTEST_DESTINATION_DIR} - DEPENDS ${AUTOBUILD_INSTALL_DIR}/lib/release/libaprutil-1.0.dylib - ) - add_custom_command(TARGET llimage_libtest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${AUTOBUILD_INSTALL_DIR}/lib/release/libexception_handler.dylib ${LLIMAGE_LIBTEST_DESTINATION_DIR} - DEPENDS ${AUTOBUILD_INSTALL_DIR}/lib/release/libexception_handler.dylib - ) - foreach(expat ${EXPAT_COPY}) - add_custom_command(TARGET llimage_libtest POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${AUTOBUILD_INSTALL_DIR}/lib/release/${expat} ${LLIMAGE_LIBTEST_DESTINATION_DIR} - DEPENDS ${AUTOBUILD_INSTALL_DIR}/lib/release/${expat} - ) - endforeach(expat) -endif (DARWIN) - -if (WINDOWS) - # Check indra/test_apps/llplugintest/CMakeLists.txt for an example of what to copy over for Windows and how -endif (WINDOWS) # Ensure people working on the viewer don't break this library -# *NOTE: This could be removed, or only built by TeamCity, if the build -# and link times become too long. add_dependencies(viewer llimage_libtest) + +endif(LL_TESTS) diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp index 95102094ae..c45bd6fd01 100644 --- a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp +++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp @@ -43,6 +43,8 @@ #include "v4coloru.h" #include "llsdserialize.h" #include "llcleanup.h" +#include "lltrace.h" +#include "llfasttimer.h" // system libraries #include <iostream> @@ -573,10 +575,10 @@ int main(int argc, char** argv) // Create the logging thread if required - if (LLFastTimer::sMetricLog) + if (LLTrace::BlockTimer::sMetricLog) { - LLFastTimer::sLogLock = new LLMutex(NULL); - fast_timer_log_thread = new LogThread(LLFastTimer::sLogName); + LLTrace::BlockTimer::setLogLock(new LLMutex()); + fast_timer_log_thread = new LogThread(LLTrace::BlockTimer::sLogName); fast_timer_log_thread->start(); } @@ -618,9 +620,9 @@ int main(int argc, char** argv) // Output perf data if requested by user if (analyze_performance) { - std::string baseline_name = LLFastTimer::sLogName + "_baseline.slp"; - std::string current_name = LLFastTimer::sLogName + ".slp"; - std::string report_name = LLFastTimer::sLogName + "_report.csv"; + std::string baseline_name = LLTrace::BlockTimer::sLogName + "_baseline.slp"; + std::string current_name = LLTrace::BlockTimer::sLogName + ".slp"; + std::string report_name = LLTrace::BlockTimer::sLogName + "_report.csv"; std::cout << "Analyzing performance, check report in : " << report_name << std::endl; @@ -628,9 +630,9 @@ int main(int argc, char** argv) } // Stop the perf gathering system if needed - if (LLFastTimer::sMetricLog) + if (LLTrace::BlockTimer::sMetricLog) { - LLMetricPerformanceTesterBasic::deleteTester(LLFastTimer::sLogName); + LLMetricPerformanceTesterBasic::deleteTester(LLTrace::BlockTimer::sLogName); sAllDone = true; } diff --git a/indra/llappearance/lltexlayerparams.h b/indra/llappearance/lltexlayerparams.h index 5e785e4f3e..116eb3bee0 100644 --- a/indra/llappearance/lltexlayerparams.h +++ b/indra/llappearance/lltexlayerparams.h @@ -30,6 +30,7 @@ #include "llpointer.h" #include "v4color.h" #include "llviewervisualparam.h" +#include <atomic> class LLAvatarAppearance; class LLImageRaw; diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 78bfaade55..aa8810f00b 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -70,6 +70,7 @@ set(llcommon_SOURCE_FILES llmetrics.cpp llmortician.cpp llmutex.cpp + llpointer.cpp llpredicate.cpp llprocess.cpp llprocessor.cpp diff --git a/indra/llcommon/coro_scheduler.cpp b/indra/llcommon/coro_scheduler.cpp index 2d8b6e1a97..b6117fa6a1 100644 --- a/indra/llcommon/coro_scheduler.cpp +++ b/indra/llcommon/coro_scheduler.cpp @@ -20,6 +20,7 @@ #include <boost/fiber/operations.hpp> // other Linden headers #include "llcallbacklist.h" +#include "llcoros.h" #include "lldate.h" #include "llerror.h" @@ -56,17 +57,55 @@ void scheduler::awakened( boost::fibers::context* ctx) noexcept boost::fibers::context* scheduler::pick_next() noexcept { + auto now = LLDate::now().secondsSinceEpoch(); // count calls to pick_next() ++mSwitches; // pick_next() is called when the previous fiber has suspended, and we // need to pick another. Did the previous pick_next() call pick the main - // fiber? If so, it's the main fiber that just suspended. - auto now = LLDate::now().secondsSinceEpoch(); - if (mMainRunning) + // fiber? (Or is this the first pick_next() call?) If so, it's the main + // fiber that just suspended. + if ((! mPrevCtx) || mPrevCtx->get_id() == mMainID) { - mMainRunning = false; mMainLast = now; } + else + { + // How long did we spend in the fiber that just suspended? + // Don't bother with long runs of the main fiber, since (a) it happens + // pretty often and (b) it's moderately likely that we've reached here + // from the canonical yield at the top of mainloop, and what we'd want + // to know about is whatever the main fiber was doing in the + // *previous* iteration of mainloop. + F64 elapsed{ now - mResumeTime }; + LLCoros::CoroData& data{ LLCoros::get_CoroData(mPrevCtx->get_id()) }; + // Find iterator to the first mHistogram key greater than elapsed. + auto past = data.mHistogram.upper_bound(elapsed); + // If the smallest key (mHistogram.begin()->first) is greater than + // elapsed, then we need not bother with this timeslice. + if (past != data.mHistogram.begin()) + { + // Here elapsed was greater than at least one key. Back off to the + // previous entry and increment that count. If it's end(), backing + // off gets us the last entry -- assuming mHistogram isn't empty. + llassert(! data.mHistogram.empty()); + ++(--past)->second; + LL::WorkQueue::ptr_t queue{ getWorkQueue() }; + // make sure the queue exists + if (queue) + { + // If it proves difficult to track down *why* the fiber spent so + // much time, consider also binding and reporting + // boost::stacktrace::stacktrace(). + queue->post( + [name=data.getName(), elapsed] + { + LL_WARNS_ONCE("LLCoros.scheduler") + << "Coroutine " << name << " ran for " + << elapsed << " seconds" << LL_ENDL; + }); + } + } + } boost::fibers::context* next; @@ -96,17 +135,9 @@ boost::fibers::context* scheduler::pick_next() noexcept // passage could be skipped. // Record this event for logging, but push it off to a thread pool to - // perform that work. Presumably std::weak_ptr::lock() is cheaper than - // WorkQueue::getInstance(). - LL::WorkQueue::ptr_t queue{ mQueue.lock() }; - // We probably started before the relevant WorkQueue was created. - if (! queue) - { - // Try again to locate the specified WorkQueue. - queue = LL::WorkQueue::getInstance(qname); - mQueue = queue; - } - // Both the lock() call and the getInstance() call might have failed. + // perform that work. + LL::WorkQueue::ptr_t queue{ getWorkQueue() }; + // The work queue we're looking for might not exist right now. if (queue) { // Bind values. Do NOT bind 'this' to avoid cross-thread access! @@ -116,7 +147,6 @@ boost::fibers::context* scheduler::pick_next() noexcept // so we have no access. queue->post( [switches=mSwitches, start=mStart, elapsed, now] - () { U32 runtime(U32(now) - U32(start)); U32 minutes(runtime / 60u); @@ -150,12 +180,29 @@ boost::fibers::context* scheduler::pick_next() noexcept { // we're about to resume the main fiber: it's no longer "ready" mMainCtx = nullptr; - // instead, it's "running" - mMainRunning = true; } + mPrevCtx = next; + // remember when we resumed this fiber so our next call can measure how + // long the previous resumption was + mResumeTime = LLDate::now().secondsSinceEpoch(); return next; } +LL::WorkQueue::ptr_t scheduler::getWorkQueue() +{ + // Cache a weak_ptr to our target work queue, presuming that + // std::weak_ptr::lock() is cheaper than WorkQueue::getInstance(). + LL::WorkQueue::ptr_t queue{ mQueue.lock() }; + // We probably started before the relevant WorkQueue was created. + if (! queue) + { + // Try again to locate the specified WorkQueue. + queue = LL::WorkQueue::getInstance(qname); + mQueue = queue; + } + return queue; +} + void scheduler::use() { boost::fibers::use_scheduling_algorithm<scheduler>(); diff --git a/indra/llcommon/coro_scheduler.h b/indra/llcommon/coro_scheduler.h index eee2d746b5..7af90685dc 100644 --- a/indra/llcommon/coro_scheduler.h +++ b/indra/llcommon/coro_scheduler.h @@ -47,17 +47,20 @@ public: static void use(); private: - // This is the fiber::id of the main fiber. We use this to discover - // whether the fiber passed to awakened() is in fact the main fiber. + LL::WorkQueue::ptr_t getWorkQueue(); + + // This is the fiber::id of the main fiber. boost::fibers::fiber::id mMainID; - // This context* is nullptr until awakened() notices that the main fiber - // has become ready, at which point it contains the main fiber's context*. + // This context* is nullptr while the main fiber is running or suspended, + // but is set to the main fiber's context each time the main fiber is ready. boost::fibers::context* mMainCtx{}; - // Set when pick_next() returns the main fiber. - bool mMainRunning{ false }; + // Remember the context returned by the previous pick_next() call. + boost::fibers::context* mPrevCtx{}; // If it's been at least this long since the last time the main fiber got // control, jump it to the head of the queue. F64 mTimeslice{ DEFAULT_TIMESLICE }; + // Time when we resumed the most recently running fiber + F64 mResumeTime{ 0 }; // Timestamp as of the last time we suspended the main fiber. F64 mMainLast{ 0 }; // Timestamp of start time diff --git a/indra/llcommon/fsyspath.h b/indra/llcommon/fsyspath.h index 1b4aec09b4..f66970ed8f 100644 --- a/indra/llcommon/fsyspath.h +++ b/indra/llcommon/fsyspath.h @@ -12,7 +12,10 @@ #if ! defined(LL_FSYSPATH_H) #define LL_FSYSPATH_H +#include <boost/iterator/transform_iterator.hpp> #include <filesystem> +#include <string> +#include <string_view> // While std::filesystem::path can be directly constructed from std::string on // both Posix and Windows, that's not what we want on Windows. Per @@ -33,42 +36,55 @@ // char"), the "native narrow encoding" isn't UTF-8, so file paths containing // non-ASCII characters get mangled. // -// Once we're building with C++20, we could pass a UTF-8 std::string through a -// vector<char8_t> to engage std::filesystem::path's own UTF-8 conversion. But -// sigh, as of 2024-04-03 we're not yet there. -// -// Anyway, encapsulating the important UTF-8 conversions in our own subclass -// allows us to migrate forward to C++20 conventions without changing -// referencing code. +// Encapsulating the important UTF-8 conversions in our own subclass allows us +// to migrate forward to C++20 conventions without changing referencing code. class fsyspath: public std::filesystem::path { using super = std::filesystem::path; + // In C++20 (__cpp_lib_char8_t), std::filesystem::u8path() is deprecated. + // std::filesystem::path(iter, iter) performs UTF-8 conversions when the + // value_type of the iterators is char8_t. While we could copy into a + // temporary std::u8string and from there into std::filesystem::path, to + // minimize string copying we'll define a transform_iterator that accepts + // a std::string_view::iterator and dereferences to char8_t. + struct u8ify + { + char8_t operator()(char c) const { return char8_t(c); } + }; + using u8iter = boost::transform_iterator<u8ify, std::string_view::iterator>; + public: // default fsyspath() {} - // construct from UTF-8 encoded std::string - fsyspath(const std::string& path): super(std::filesystem::u8path(path)) {} - // construct from UTF-8 encoded const char* - fsyspath(const char* path): super(std::filesystem::u8path(path)) {} + // construct from UTF-8 encoded string + fsyspath(const std::string& path): fsyspath(std::string_view(path)) {} + fsyspath(const char* path): fsyspath(std::string_view(path)) {} + fsyspath(std::string_view path): + super(u8iter(path.begin(), u8ify()), u8iter(path.end(), u8ify())) + {} // construct from existing path fsyspath(const super& path): super(path) {} - fsyspath& operator=(const super& p) { super::operator=(p); return *this; } - fsyspath& operator=(const std::string& p) - { - super::operator=(std::filesystem::u8path(p)); - return *this; - } - fsyspath& operator=(const char* p) + fsyspath& operator=(const super& p) { super::operator=(p); return *this; } + fsyspath& operator=(const std::string& p) { return (*this) = std::string_view(p); } + fsyspath& operator=(const char* p) { return (*this) = std::string_view(p); } + fsyspath& operator=(std::string_view p) { - super::operator=(std::filesystem::u8path(p)); + assign(u8iter(p.begin(), u8ify()), u8iter(p.end(), u8ify())); return *this; } // shadow base-class string() method with UTF-8 aware method - std::string string() const { return super::u8string(); } + std::string string() const + { + // Short of forbidden type punning, I see no way to avoid copying this + // std::u8string to a std::string. + auto u8str{ super::u8string() }; + // from https://github.com/tahonermann/char8_t-remediation/blob/master/char8_t-remediation.h#L180-L182 + return { u8str.begin(), u8str.end() }; + } // On Posix systems, where value_type is already char, this operator // std::string() method shadows the base class operator string_type() // method. But on Windows, where value_type is wchar_t, the base class diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index 566ea2ea14..a0394da281 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -355,5 +355,7 @@ constexpr U8 CLICK_ACTION_DISABLED = 8; constexpr U8 CLICK_ACTION_IGNORE = 9; // DO NOT CHANGE THE SEQUENCE OF THIS LIST!! +constexpr U32 BEACON_SHOW_MAP = 0x0001; +constexpr U32 BEACON_FOCUS_MAP = 0x0002; #endif diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 1ae5c87a00..5a3cbd2ef1 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -57,16 +57,19 @@ #include "llsdutil.h" #include "lltimer.h" #include "stringize.h" +#include "scope_exit.h" #if LL_WINDOWS #include <excpt.h> #endif +thread_local std::unordered_map<std::string, int> LLCoros::mPrefixMap; +thread_local std::unordered_map<std::string, LLCoros::id> LLCoros::mNameMap; + // static bool LLCoros::on_main_coro() { - return (!LLCoros::instanceExists() || - LLCoros::getName().empty()); + return (!instanceExists() || get_CoroData().isMain); } // static @@ -76,11 +79,11 @@ bool LLCoros::on_main_thread_main_coro() } // static -LLCoros::CoroData& LLCoros::get_CoroData(const std::string&) +LLCoros::CoroData& LLCoros::get_CoroData() { CoroData* current{ nullptr }; // be careful about attempted accesses in the final throes of app shutdown - if (! wasDeleted()) + if (instanceExists()) { current = instance().mCurrent.get(); } @@ -89,16 +92,26 @@ LLCoros::CoroData& LLCoros::get_CoroData(const std::string&) // canonical values. if (! current) { - static std::atomic<int> which_thread(0); - // Use alternate CoroData constructor. - static thread_local CoroData sMain(which_thread++); // We need not reset() the local_ptr to this instance; we'll simply // find it again every time we discover that current is null. - current = &sMain; + current = &main_CoroData(); } return *current; } +LLCoros::CoroData& LLCoros::get_CoroData(id id) +{ + auto found = CoroData::getInstance(id); + return found? *found : main_CoroData(); +} + +LLCoros::CoroData& LLCoros::main_CoroData() +{ + // tell CoroData we're "main" + static thread_local CoroData sMain(""); + return sMain; +} + //static LLCoros::coro::id LLCoros::get_self() { @@ -108,28 +121,28 @@ LLCoros::coro::id LLCoros::get_self() //static void LLCoros::set_consuming(bool consuming) { - auto& data(get_CoroData("set_consuming()")); + auto& data(get_CoroData()); // DO NOT call this on the main() coroutine. - llassert_always(! data.mName.empty()); + llassert_always(! data.isMain); data.mConsuming = consuming; } //static bool LLCoros::get_consuming() { - return get_CoroData("get_consuming()").mConsuming; + return get_CoroData().mConsuming; } // static void LLCoros::setStatus(const std::string& status) { - get_CoroData("setStatus()").mStatus = status; + get_CoroData().mStatus = status; } // static std::string LLCoros::getStatus() { - return get_CoroData("getStatus()").mStatus; + return get_CoroData().mStatus; } LLCoros::LLCoros(): @@ -186,9 +199,8 @@ void LLCoros::cleanupSingleton() std::string LLCoros::generateDistinctName(const std::string& prefix) const { - static int unique = 0; - - // Allowing empty name would make getName()'s not-found return ambiguous. + // Empty name would trigger CoroData's constructor's special case for the + // main coroutine. if (prefix.empty()) { LL_ERRS("LLCoros") << "LLCoros::launch(): pass non-empty name string" << LL_ENDL; @@ -196,9 +208,11 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const // If the specified name isn't already in the map, just use that. std::string name(prefix); + // maintain a distinct int suffix for each prefix + int& unique = mPrefixMap[prefix]; - // Until we find an unused name, append a numeric suffix for uniqueness. - while (CoroData::getInstance(name)) + // Until we find an unused name, append int suffix for uniqueness. + while (mNameMap.find(name) != mNameMap.end()) { name = stringize(prefix, unique++); } @@ -207,9 +221,16 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const bool LLCoros::killreq(const std::string& name) { - auto found = CoroData::getInstance(name); + auto foundName = mNameMap.find(name); + if (foundName == mNameMap.end()) + { + // couldn't find that name in map + return false; + } + auto found = CoroData::getInstance(foundName->second); if (! found) { + // found name, but CoroData with that ID key no longer exists return false; } // Next time the subject coroutine calls checkStop(), make it terminate. @@ -224,14 +245,13 @@ bool LLCoros::killreq(const std::string& name) //static std::string LLCoros::getName() { - return get_CoroData("getName()").mName; + return get_CoroData().getName(); } -//static -std::string LLCoros::logname() +// static +std::string LLCoros::getName(id id) { - auto& data(get_CoroData("logname()")); - return data.mName.empty()? data.getKey() : data.mName; + return get_CoroData(id).getName(); } void LLCoros::saveException(const std::string& name, std::exception_ptr exc) @@ -264,7 +284,7 @@ void LLCoros::printActiveCoroutines(const std::string& when) { LL_INFOS("LLCoros") << "-------------- List of active coroutines ------------"; F64 time = LLTimer::getTotalSeconds(); - for (auto& cd : CoroData::instance_snapshot()) + for (const auto& cd : CoroData::instance_snapshot()) { F64 life_time = time - cd.mCreationTime; LL_CONT << LL_NEWLINE @@ -323,6 +343,33 @@ void LLCoros::toplevel(std::string name, callable_t callable) // run the code the caller actually wants in the coroutine try { + LL::scope_exit report{ + [&corodata] + { + bool allzero = true; + for (const auto& [threshold, occurs] : corodata.mHistogram) + { + if (occurs) + { + allzero = false; + break; + } + } + if (! allzero) + { + LL_WARNS("LLCoros") << "coroutine " << corodata.mName; + const char* sep = " exceeded "; + for (const auto& [threshold, occurs] : corodata.mHistogram) + { + if (occurs) + { + LL_CONT << sep << threshold << " " << occurs << " times"; + sep = ", "; + } + } + LL_ENDL; + } + }}; LL::seh::catcher(callable); } catch (const Stop& exc) @@ -364,8 +411,8 @@ void LLCoros::checkStop(callable_t cleanup) // do this AFTER the check above, because get_CoroData() depends on the // local_ptr in our instance(). - auto& data(get_CoroData("checkStop()")); - if (data.mName.empty()) + auto& data(get_CoroData()); + if (data.isMain) { // Our Stop exception and its subclasses are intended to stop loitering // coroutines. Don't throw it from the main coroutine. @@ -385,7 +432,7 @@ void LLCoros::checkStop(callable_t cleanup) { // Someone wants to kill this coroutine cleanup(); - LLTHROW(Killed(stringize("coroutine ", data.mName, " killed by ", data.mKilledBy))); + LLTHROW(Killed(stringize("coroutine ", data.getName(), " killed by ", data.mKilledBy))); } } @@ -445,20 +492,51 @@ LLBoundListener LLCoros::getStopListener(const std::string& caller, } LLCoros::CoroData::CoroData(const std::string& name): - LLInstanceTracker<CoroData, std::string>(name), + super(boost::this_fiber::get_id()), mName(name), - mCreationTime(LLTimer::getTotalSeconds()) + mCreationTime(LLTimer::getTotalSeconds()), + // Preset threshold times in mHistogram + mHistogram{ + {0.004, 0}, + {0.040, 0}, + {0.400, 0}, + {1.000, 0} + } { + // we expect the empty string for the main coroutine + if (name.empty()) + { + isMain = true; + if (on_main_thread()) + { + // main coroutine on main thread + mName = "main"; + } + else + { + // main coroutine on some other thread + static std::atomic<int> main_no{ 0 }; + mName = stringize("main", ++main_no); + } + } + // maintain LLCoros::mNameMap + LLCoros::mNameMap.emplace(mName, getKey()); +} + +LLCoros::CoroData::~CoroData() +{ + // Don't try to erase the static main CoroData from our static + // thread_local mNameMap; that could run into destruction order problems. + if (! isMain) + { + LLCoros::mNameMap.erase(mName); + } } -LLCoros::CoroData::CoroData(int n): - // This constructor is used for the thread_local instance belonging to the - // default coroutine on each thread. We must give each one a different - // LLInstanceTracker key because LLInstanceTracker's map spans all - // threads, but we want the default coroutine on each thread to have the - // empty string as its visible name because some consumers test for that. - LLInstanceTracker<CoroData, std::string>("main" + stringize(n)), - mName(), - mCreationTime(LLTimer::getTotalSeconds()) +std::string LLCoros::CoroData::getName() const { + if (mStatus.empty()) + return mName; + else + return stringize(mName, " (", mStatus, ")"); } diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 0291d7f1d9..1edcb7e387 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -37,41 +37,38 @@ #include <boost/fiber/fss.hpp> #include <exception> #include <functional> +#include <map> #include <queue> #include <string> +#include <unordered_map> + +namespace llcoro +{ +class scheduler; +} /** - * Registry of named Boost.Coroutine instances - * - * The Boost.Coroutine library supports the general case of a coroutine - * accepting arbitrary parameters and yielding multiple (sets of) results. For - * such use cases, it's natural for the invoking code to retain the coroutine - * instance: the consumer repeatedly calls into the coroutine, perhaps passing - * new parameter values, prompting it to yield its next result. - * - * Our typical coroutine usage is different, though. For us, coroutines - * provide an alternative to the @c Responder pattern. Our typical coroutine - * has @c void return, invoked in fire-and-forget mode: the handler for some - * user gesture launches the coroutine and promptly returns to the main loop. - * The coroutine initiates some action that will take multiple frames (e.g. a - * capability request), waits for its result, processes it and silently steals - * away. + * Registry of named Boost.Fiber instances * - * This usage poses two (related) problems: + * When the viewer first introduced the semi-independent execution agents now + * called fibers, the term "fiber" had not yet become current, and the only + * available libraries used the term "coroutine" instead. Within the viewer we + * continue to use the term "coroutines," though at present they are actually + * boost::fibers::fiber instances. * - * # Who should own the coroutine instance? If it's simply local to the - * handler code that launches it, return from the handler will destroy the - * coroutine object, terminating the coroutine. - * # Once the coroutine terminates, in whatever way, who's responsible for - * cleaning up the coroutine object? + * Coroutines provide an alternative to the @c Responder pattern. Our typical + * coroutine has @c void return, invoked in fire-and-forget mode: the handler + * for some user gesture launches the coroutine and promptly returns to the + * main loop. The coroutine initiates some action that will take multiple + * frames (e.g. a capability request), waits for its result, processes it and + * silently steals away. * * LLCoros is a Singleton collection of currently-active coroutine instances. * Each has a name. You ask LLCoros to launch a new coroutine with a suggested * name prefix; from your prefix it generates a distinct name, registers the * new coroutine and returns the actual name. * - * The name - * can provide diagnostic info: we can look up the name of the + * The name can provide diagnostic info: we can look up the name of the * currently-running coroutine. */ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros> @@ -91,12 +88,8 @@ public: // llassert(LLCoros::on_main_thread_main_coro()) static bool on_main_thread_main_coro(); - /// The viewer's use of the term "coroutine" became deeply embedded before - /// the industry term "fiber" emerged to distinguish userland threads from - /// simpler, more transient kinds of coroutines. Semantically they've - /// always been fibers. But at this point in history, we're pretty much - /// stuck with the term "coroutine." typedef boost::fibers::fiber coro; + typedef coro::id id; /// Canonical callable type typedef std::function<void()> callable_t; @@ -150,13 +143,16 @@ public: /** * From within a coroutine, look up the (tweaked) name string by which - * this coroutine is registered. Returns the empty string if not found - * (e.g. if the coroutine was launched by hand rather than using - * LLCoros::launch()). + * this coroutine is registered. */ static std::string getName(); /** + * Given an id, return the name of that coroutine. + */ + static std::string getName(id); + + /** * rethrow() is called by the thread's main fiber to propagate an * exception from any coroutine into the main fiber, where it can engage * the normal unhandled-exception machinery, up to and including crash @@ -170,13 +166,6 @@ public: void rethrow(); /** - * This variation returns a name suitable for log messages: the explicit - * name for an explicitly-launched coroutine, or "mainN" for the default - * coroutine on a thread. - */ - static std::string logname(); - - /** * For delayed initialization. To be clear, this will only affect * coroutines launched @em after this point. The underlying facility * provides no way to alter the stack size of any running coroutine. @@ -187,7 +176,7 @@ public: void printActiveCoroutines(const std::string& when=std::string()); /// get the current coro::id for those who really really care - static coro::id get_self(); + static id get_self(); /** * Most coroutines, most of the time, don't "consume" the events for which @@ -236,6 +225,7 @@ public: setStatus(status); } TempStatus(const TempStatus&) = delete; + TempStatus& operator=(const TempStatus&) = delete; ~TempStatus() { setStatus(mOldStatus); @@ -331,10 +321,14 @@ public: using local_ptr = boost::fibers::fiber_specific_ptr<T>; private: + friend class llcoro::scheduler; + std::string generateDistinctName(const std::string& prefix) const; void toplevel(std::string name, callable_t callable); struct CoroData; - static CoroData& get_CoroData(const std::string& caller); + static CoroData& get_CoroData(); + static CoroData& get_CoroData(id); + static CoroData& main_CoroData(); void saveException(const std::string& name, std::exception_ptr exc); LLTempBoundListener mConn; @@ -355,13 +349,18 @@ private: S32 mStackSize; // coroutine-local storage, as it were: one per coro we track - struct CoroData: public LLInstanceTracker<CoroData, std::string> + struct CoroData: public LLInstanceTracker<CoroData, id> { + using super = LLInstanceTracker<CoroData, id>; + CoroData(const std::string& name); - CoroData(int n); + ~CoroData(); + std::string getName() const; + + bool isMain{ false }; // tweaked name of the current coroutine - const std::string mName; + std::string mName; // set_consuming() state -- don't consume events unless specifically directed bool mConsuming{ false }; // killed by which coroutine @@ -369,20 +368,24 @@ private: // setStatus() state std::string mStatus; F64 mCreationTime; // since epoch + // Histogram of how many times this coroutine's timeslice exceeds + // certain thresholds. mHistogram is pre-populated with those + // thresholds as keys. If k0 is one threshold key and k1 is the next, + // mHistogram[k0] is the number of times a coroutine timeslice tn ran + // (k0 <= tn < k1). A timeslice less than mHistogram.begin()->first is + // fine; we don't need to record those. + std::map<F64, U32> mHistogram; }; // Identify the current coroutine's CoroData. This local_ptr isn't static // because it's a member of an LLSingleton, and we rely on it being // cleaned up in proper dependency order. local_ptr<CoroData> mCurrent; -}; -namespace llcoro -{ - -inline -std::string logname() { return LLCoros::logname(); } - -} // llcoro + // ensure name uniqueness + static thread_local std::unordered_map<std::string, int> mPrefixMap; + // lookup by name + static thread_local std::unordered_map<std::string, id> mNameMap; +}; #endif /* ! defined(LL_LLCOROS_H) */ diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index b17b9ff21e..b6d560a121 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -239,17 +239,12 @@ namespace LLError ~CallSite(); -#ifdef LL_LIBRARY_INCLUDE - bool shouldLog(); -#else // LL_LIBRARY_INCLUDE bool shouldLog() { return mCached ? mShouldLog : Log::shouldLog(*this); } - // this member function needs to be in-line for efficiency -#endif // LL_LIBRARY_INCLUDE void invalidate(); diff --git a/indra/llcommon/llkeybind.cpp b/indra/llcommon/llkeybind.cpp index 83c53d220d..73a207504e 100644 --- a/indra/llcommon/llkeybind.cpp +++ b/indra/llcommon/llkeybind.cpp @@ -125,20 +125,16 @@ LLKeyData& LLKeyData::operator=(const LLKeyData& rhs) bool LLKeyData::operator==(const LLKeyData& rhs) const { - if (mMouse != rhs.mMouse) return false; - if (mKey != rhs.mKey) return false; - if (mMask != rhs.mMask) return false; - if (mIgnoreMasks != rhs.mIgnoreMasks) return false; - return true; + return + (mMouse == rhs.mMouse) && + (mKey == rhs.mKey) && + (mMask == rhs.mMask) && + (mIgnoreMasks == rhs.mIgnoreMasks); } bool LLKeyData::operator!=(const LLKeyData& rhs) const { - if (mMouse != rhs.mMouse) return true; - if (mKey != rhs.mKey) return true; - if (mMask != rhs.mMask) return true; - if (mIgnoreMasks != rhs.mIgnoreMasks) return true; - return false; + return ! (*this == rhs); } bool LLKeyData::canHandle(const LLKeyData& data) const diff --git a/indra/llcommon/llpointer.cpp b/indra/llcommon/llpointer.cpp new file mode 100755 index 0000000000..adea447caa --- /dev/null +++ b/indra/llcommon/llpointer.cpp @@ -0,0 +1,26 @@ +/** + * @file llpointer.cpp + * @author Nat Goodspeed + * @date 2024-09-26 + * @brief Implementation for llpointer. + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "llpointer.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llerror.h" + +void LLPointerBase::wild_dtor(std::string_view msg) +{ +// LL_WARNS() << msg << LL_ENDL; + llassert_msg(false, msg); +} diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h index 048547e4cc..b53cfcdd1a 100644 --- a/indra/llcommon/llpointer.h +++ b/indra/llcommon/llpointer.h @@ -26,8 +26,9 @@ #ifndef LLPOINTER_H #define LLPOINTER_H -#include "llerror.h" // *TODO: consider eliminating this -#include "llmutex.h" +#include <boost/functional/hash.hpp> +#include <string_view> +#include <utility> // std::swap() //---------------------------------------------------------------------------- // RefCount objects should generally only be accessed by way of LLPointer<>'s @@ -42,8 +43,18 @@ //---------------------------------------------------------------------------- +class LLPointerBase +{ +protected: + // alert the coder that a referenced type's destructor did something very + // strange -- this is in a non-template base class so we can hide the + // implementation in llpointer.cpp + static void wild_dtor(std::string_view msg); +}; + // Note: relies on Type having ref() and unref() methods -template <class Type> class LLPointer +template <class Type> +class LLPointer: public LLPointerBase { public: template<typename Subclass> @@ -60,6 +71,13 @@ public: ref(); } + // Even though the template constructors below accepting + // (const LLPointer<Subclass>&) and (LLPointer<Subclass>&&) appear to + // subsume these specific (const LLPointer<Type>&) and (LLPointer<Type>&&) + // constructors, the compiler recognizes these as The Copy Constructor and + // The Move Constructor, respectively. In other words, even in the + // presence of the LLPointer<Subclass> constructors, we still must specify + // the LLPointer<Type> constructors. LLPointer(const LLPointer<Type>& ptr) : mPointer(ptr.mPointer) { @@ -98,39 +116,52 @@ public: const Type& operator*() const { return *mPointer; } Type& operator*() { return *mPointer; } - operator BOOL() const { return (mPointer != nullptr); } operator bool() const { return (mPointer != nullptr); } bool operator!() const { return (mPointer == nullptr); } bool isNull() const { return (mPointer == nullptr); } bool notNull() const { return (mPointer != nullptr); } operator Type*() const { return mPointer; } - bool operator !=(Type* ptr) const { return (mPointer != ptr); } - bool operator ==(Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } + template <typename Type1> + bool operator !=(Type1* ptr) const { return (mPointer != ptr); } + template <typename Type1> + bool operator ==(Type1* ptr) const { return (mPointer == ptr); } + template <typename Type1> + bool operator !=(const LLPointer<Type1>& ptr) const { return (mPointer != ptr.mPointer); } + template <typename Type1> + bool operator ==(const LLPointer<Type1>& ptr) const { return (mPointer == ptr.mPointer); } + bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } + bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } LLPointer<Type>& operator =(Type* ptr) { - assign(ptr); + // copy-and-swap idiom, see http://gotw.ca/gotw/059.htm + LLPointer temp(ptr); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } + // Even though the template assignment operators below accepting + // (const LLPointer<Subclass>&) and (LLPointer<Subclass>&&) appear to + // subsume these specific (const LLPointer<Type>&) and (LLPointer<Type>&&) + // assignment operators, the compiler recognizes these as Copy Assignment + // and Move Assignment, respectively. In other words, even in the presence + // of the LLPointer<Subclass> assignment operators, we still must specify + // the LLPointer<Type> operators. LLPointer<Type>& operator =(const LLPointer<Type>& ptr) { - assign(ptr); + LLPointer temp(ptr); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } LLPointer<Type>& operator =(LLPointer<Type>&& ptr) { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } + LLPointer temp(std::move(ptr)); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } @@ -138,210 +169,35 @@ public: template<typename Subclass> LLPointer<Type>& operator =(const LLPointer<Subclass>& ptr) { - assign(ptr.get()); + LLPointer temp(ptr); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } template<typename Subclass> LLPointer<Type>& operator =(LLPointer<Subclass>&& ptr) { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } + LLPointer temp(std::move(ptr)); + using std::swap; // per Swappable convention + swap(*this, temp); return *this; } // Just exchange the pointers, which will not change the reference counts. static void swap(LLPointer<Type>& a, LLPointer<Type>& b) { - Type* temp = a.mPointer; - a.mPointer = b.mPointer; - b.mPointer = temp; - } - -protected: -#ifdef LL_LIBRARY_INCLUDE - void ref(); - void unref(); -#else - void ref() - { - if (mPointer) - { - mPointer->ref(); - } - } - - void unref() - { - if (mPointer) - { - Type *temp = mPointer; - mPointer = nullptr; - temp->unref(); - if (mPointer != nullptr) - { - LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; - unref(); - } - } - } -#endif // LL_LIBRARY_INCLUDE - - void assign(const LLPointer<Type>& ptr) - { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ref(); - } - } - -protected: - Type* mPointer; -}; - -template <class Type> class LLConstPointer -{ - template<typename Subclass> - friend class LLConstPointer; -public: - LLConstPointer() : - mPointer(nullptr) - { - } - - LLConstPointer(const Type* ptr) : - mPointer(ptr) - { - ref(); - } - - LLConstPointer(const LLConstPointer<Type>& ptr) : - mPointer(ptr.mPointer) - { - ref(); - } - - LLConstPointer(LLConstPointer<Type>&& ptr) noexcept - { - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } - - // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLConstPointer(const LLConstPointer<Subclass>& ptr) : - mPointer(ptr.get()) - { - ref(); - } - - template<typename Subclass> - LLConstPointer(LLConstPointer<Subclass>&& ptr) noexcept : - mPointer(ptr.get()) - { - ptr.mPointer = nullptr; - } - - ~LLConstPointer() - { - unref(); - } - - const Type* get() const { return mPointer; } - const Type* operator->() const { return mPointer; } - const Type& operator*() const { return *mPointer; } - - operator BOOL() const { return (mPointer != nullptr); } - operator bool() const { return (mPointer != nullptr); } - bool operator!() const { return (mPointer == nullptr); } - bool isNull() const { return (mPointer == nullptr); } - bool notNull() const { return (mPointer != nullptr); } - - operator const Type*() const { return mPointer; } - bool operator !=(const Type* ptr) const { return (mPointer != ptr); } - bool operator ==(const Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLConstPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLConstPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLConstPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } - - LLConstPointer<Type>& operator =(const Type* ptr) - { - if( mPointer != ptr ) - { - unref(); - mPointer = ptr; - ref(); - } - - return *this; - } - - LLConstPointer<Type>& operator =(const LLConstPointer<Type>& ptr) - { - if( mPointer != ptr.mPointer ) - { - unref(); - mPointer = ptr.mPointer; - ref(); - } - return *this; - } - - LLConstPointer<Type>& operator =(LLConstPointer<Type>&& ptr) - { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } - return *this; - } - - // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLConstPointer<Type>& operator =(const LLConstPointer<Subclass>& ptr) - { - if( mPointer != ptr.get() ) - { - unref(); - mPointer = ptr.get(); - ref(); - } - return *this; - } - - template<typename Subclass> - LLConstPointer<Type>& operator =(LLConstPointer<Subclass>&& ptr) - { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ptr.mPointer = nullptr; - } - return *this; + using std::swap; // per Swappable convention + swap(a.mPointer, b.mPointer); } - // Just exchange the pointers, which will not change the reference counts. - static void swap(LLConstPointer<Type>& a, LLConstPointer<Type>& b) + // Put swap() overload in the global namespace, per Swappable convention + friend void swap(LLPointer<Type>& a, LLPointer<Type>& b) { - const Type* temp = a.mPointer; - a.mPointer = b.mPointer; - b.mPointer = temp; + LLPointer<Type>::swap(a, b); } protected: -#ifdef LL_LIBRARY_INCLUDE - void ref(); - void unref(); -#else // LL_LIBRARY_INCLUDE void ref() { if (mPointer) @@ -354,22 +210,24 @@ protected: { if (mPointer) { - const Type *temp = mPointer; + Type *temp = mPointer; mPointer = nullptr; temp->unref(); if (mPointer != nullptr) { - LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; + wild_dtor("Unreference did assignment to non-NULL because of destructor"); unref(); } } } -#endif // LL_LIBRARY_INCLUDE protected: - const Type* mPointer; + Type* mPointer; }; +template <typename Type> +using LLConstPointer = LLPointer<const Type>; + template<typename Type> class LLCopyOnWritePointer : public LLPointer<Type> { @@ -418,14 +276,14 @@ private: bool mStayUnique; }; -template<typename Type> -bool operator!=(Type* lhs, const LLPointer<Type>& rhs) +template<typename Type0, typename Type1> +bool operator!=(Type0* lhs, const LLPointer<Type1>& rhs) { return (lhs != rhs.get()); } -template<typename Type> -bool operator==(Type* lhs, const LLPointer<Type>& rhs) +template<typename Type0, typename Type1> +bool operator==(Type0* lhs, const LLPointer<Type1>& rhs) { return (lhs == rhs.get()); } diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp index 1c4ac5a7bf..0196a24b18 100644 --- a/indra/llcommon/llqueuedthread.cpp +++ b/indra/llcommon/llqueuedthread.cpp @@ -146,7 +146,7 @@ size_t LLQueuedThread::updateQueue(F32 max_time_ms) // schedule a call to threadedUpdate for every call to updateQueue if (!isQuitting()) { - mRequestQueue.post([=]() + mRequestQueue.post([=, this]() { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qt - update"); mIdleThread = false; @@ -474,7 +474,7 @@ void LLQueuedThread::processRequest(LLQueuedThread::QueuedRequest* req) #else using namespace std::chrono_literals; auto retry_time = LL::WorkQueue::TimePoint::clock::now() + 16ms; - mRequestQueue.post([=] + mRequestQueue.post([=, this] { LL_PROFILE_ZONE_NAMED("processRequest - retry"); if (LL::WorkQueue::TimePoint::clock::now() < retry_time) diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index 33666964f7..21a663a003 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -170,7 +170,7 @@ fsyspath source_path(lua_State* L) { lua_getinfo(L, i, "s", &ar); } - return ar.source; + return { ar.source }; } } // namespace lluau @@ -1108,7 +1108,7 @@ lua_function(source_path, "source_path(): return the source path of the running { lua_checkdelta(L, 1); lluau_checkstack(L, 1); - lua_pushstdstring(L, lluau::source_path(L).u8string()); + lua_pushstdstring(L, lluau::source_path(L)); return 1; } @@ -1119,7 +1119,7 @@ lua_function(source_dir, "source_dir(): return the source directory of the runni { lua_checkdelta(L, 1); lluau_checkstack(L, 1); - lua_pushstdstring(L, lluau::source_path(L).parent_path().u8string()); + lua_pushstdstring(L, fsyspath(lluau::source_path(L).parent_path())); return 1; } @@ -1132,7 +1132,7 @@ lua_function(abspath, "abspath(path): " lua_checkdelta(L); auto path{ lua_tostdstring(L, 1) }; lua_pop(L, 1); - lua_pushstdstring(L, (lluau::source_path(L).parent_path() / path).u8string()); + lua_pushstdstring(L, fsyspath(lluau::source_path(L).parent_path() / path)); return 1; } diff --git a/indra/llcommon/owning_ptr.h b/indra/llcommon/owning_ptr.h new file mode 100755 index 0000000000..7cf8d3f0ba --- /dev/null +++ b/indra/llcommon/owning_ptr.h @@ -0,0 +1,71 @@ +/** + * @file owning_ptr.h + * @author Nat Goodspeed + * @date 2024-09-27 + * @brief owning_ptr<T> is like std::unique_ptr<T>, but easier to integrate + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Copyright (c) 2024, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_OWNING_PTR_H) +#define LL_OWNING_PTR_H + +#include <functional> +#include <memory> + +/** + * owning_ptr<T> adapts std::unique_ptr<T> to make it easier to adopt into + * older code using dumb pointers. + * + * Consider a class Outer with a member Thing* mThing. After the constructor, + * each time a method wants to assign to mThing, it must test for nullptr and + * destroy the previous Thing instance. During Outer's lifetime, mThing is + * passed to legacy domain-specific functions accepting plain Thing*. Finally + * the destructor must again test for nullptr and destroy the remaining Thing + * instance. + * + * Multiply that by several different Outer members of different types, + * possibly with different domain-specific destructor functions. + * + * Dropping std::unique_ptr<Thing> into Outer is cumbersome for a several + * reasons. First, if Thing requires a domain-specific destructor function, + * the unique_ptr declaration of mThing must explicitly state the type of that + * function (as a function pointer, for a typical legacy function). Second, + * every Thing* assignment to mThing must be changed to mThing.reset(). Third, + * every time we call a legacy domain-specific function, we must pass + * mThing.get(). + * + * owning_ptr<T> is designed to drop into a situation like this. The domain- + * specific destructor function, if any, is passed to its constructor; it need + * not be encoded into the pointer type. owning_ptr<T> supports plain pointer + * assignment, internally calling std::unique_ptr<T>::reset(). It also + * supports implicit conversion to plain T*, to pass the owned pointer to + * legacy domain-specific functions. + * + * Obviously owning_ptr<T> must not be used in situations where ownership of + * the referenced object is passed on to another pointer: use std::unique_ptr + * for that. Similarly, it is not for shared ownership. It simplifies lifetime + * management for classes that currently store (and explicitly destroy) plain + * T* pointers. + */ +template <typename T> +class owning_ptr +{ + using deleter = std::function<void(T*)>; +public: + owning_ptr(T* p=nullptr, const deleter& d=std::default_delete<T>()): + mPtr(p, d) + {} + void reset(T* p=nullptr) { mPtr.reset(p); } + owning_ptr& operator=(T* p) { mPtr.reset(p); return *this; } + operator T*() const { return mPtr.get(); } + T& operator*() const { return *mPtr; } + T* operator->() const { return mPtr.operator->(); } + +private: + std::unique_ptr<T, deleter> mPtr; +}; + +#endif /* ! defined(LL_OWNING_PTR_H) */ diff --git a/indra/llcommon/resultset.cpp b/indra/llcommon/resultset.cpp index 4d7b00eabd..8bdfbec272 100644 --- a/indra/llcommon/resultset.cpp +++ b/indra/llcommon/resultset.cpp @@ -61,20 +61,6 @@ LLSD ResultSet::getSlice(int index, int count) const return getSliceStart(index, count).first; } -/*==========================================================================*| -LLSD ResultSet::getSingle(int index) const -{ - if (0 <= index && index < getLength()) - { - return getSingle_(index); - } - else - { - return {}; - } -} -|*==========================================================================*/ - ResultSet::ResultSet(const std::string& name): mName(name) { diff --git a/indra/llcommon/resultset.h b/indra/llcommon/resultset.h index 90d52b6fe4..10d84c038f 100644 --- a/indra/llcommon/resultset.h +++ b/indra/llcommon/resultset.h @@ -17,6 +17,7 @@ #include "llsd.h" #include <iosfwd> // std::ostream #include <utility> // std::pair +#include <vector> namespace LL { @@ -41,11 +42,6 @@ struct ResultSet: public LLIntTracker<ResultSet> LLSD getSlice(int index, int count) const; // Like getSlice(), but also return adjusted start position. std::pair<LLSD, int> getSliceStart(int index, int count) const; -/*==========================================================================*| - // Retrieve LLSD corresponding to a single entry from the result set, - // with index validation. - LLSD getSingle(int index) const; -|*==========================================================================*/ /*---------------- the rest is solely for debug logging ----------------*/ std::string mName; @@ -54,6 +50,21 @@ struct ResultSet: public LLIntTracker<ResultSet> virtual ~ResultSet(); }; +// VectorResultSet is for the simple case of a ResultSet managing a single +// std::vector<T>. +template <typename T> +struct VectorResultSet: public ResultSet +{ + using super = VectorResultSet<T>; + + VectorResultSet(const std::string& name): ResultSet(name) {} + int getLength() const override { return narrow(mVector.size()); } + LLSD getSingle(int index) const override { return getSingleFrom(mVector[index]); } + virtual LLSD getSingleFrom(const T&) const = 0; + + std::vector<T> mVector; +}; + } // namespace LL std::ostream& operator<<(std::ostream& out, const LL::ResultSet& self); diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp index 9138c862f9..8b7b97a1f9 100644 --- a/indra/llcommon/workqueue.cpp +++ b/indra/llcommon/workqueue.cpp @@ -127,9 +127,7 @@ void LL::WorkQueueBase::error(const std::string& msg) void LL::WorkQueueBase::checkCoroutine(const std::string& method) { - // By convention, the default coroutine on each thread has an empty name - // string. See also LLCoros::logname(). - if (LLCoros::getName().empty()) + if (LLCoros::on_main_coro()) { LLTHROW(Error("Do not call " + method + " from a thread's default coroutine")); } diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h index 6b14b68c78..1fb61673bd 100644 --- a/indra/llimage/llimage.h +++ b/indra/llimage/llimage.h @@ -27,10 +27,11 @@ #ifndef LL_LLIMAGE_H #define LL_LLIMAGE_H -#include "lluuid.h" -#include "llstring.h" +#include "llmutex.h" #include "llpointer.h" +#include "llstring.h" #include "lltrace.h" +#include "lluuid.h" constexpr S32 MIN_IMAGE_MIP = 2; // 4x4, only used for expand/contract power of 2 constexpr S32 MAX_IMAGE_MIP = 12; // 4096x4096 diff --git a/indra/llimagej2coj/llimagej2coj.cpp b/indra/llimagej2coj/llimagej2coj.cpp index 6037517103..d8f17561b3 100644 --- a/indra/llimagej2coj/llimagej2coj.cpp +++ b/indra/llimagej2coj/llimagej2coj.cpp @@ -31,6 +31,8 @@ #include "openjpeg.h" #include "event.h" #include "cio.h" +#include "owning_ptr.h" +#include <string> #define MAX_ENCODED_DISCARD_LEVELS 5 @@ -231,33 +233,6 @@ public: parameters.cp_reduce = discardLevel; } - ~JPEG2KDecode() - { - if (decoder) - { - opj_destroy_codec(decoder); - } - decoder = nullptr; - - if (image) - { - opj_image_destroy(image); - } - image = nullptr; - - if (stream) - { - opj_stream_destroy(stream); - } - stream = nullptr; - - if (codestream_info) - { - opj_destroy_cstr_info(&codestream_info); - } - codestream_info = nullptr; - } - bool readHeader( U8* data, U32 dataSize, @@ -275,11 +250,6 @@ public: return false; } - if (stream) - { - opj_stream_destroy(stream); - } - stream = opj_stream_create(dataSize, true); if (!stream) { @@ -301,13 +271,14 @@ public: opj_decoder_set_strict_mode(decoder, OPJ_FALSE); /* Read the main header of the codestream and if necessary the JP2 boxes*/ - if (!opj_read_header((opj_stream_t*)stream, decoder, &image)) + opj_image_t* img; + if (!opj_read_header(stream, decoder, &img)) { return false; } + image = img; codestream_info = opj_get_cstr_info(decoder); - if (!codestream_info) { return false; @@ -344,11 +315,6 @@ public: opj_set_warning_handler(decoder, opj_warn, this); opj_set_error_handler(decoder, opj_error, this); - if (stream) - { - opj_stream_destroy(stream); - } - stream = opj_stream_create(dataSize, true); if (!stream) { @@ -366,11 +332,7 @@ public: size = dataSize; offset = 0; - if (image) - { - opj_image_destroy(image); - image = nullptr; - } + image = nullptr; // needs to happen before opj_read_header and opj_decode... opj_set_decoded_resolution_factor(decoder, discard_level); @@ -378,10 +340,12 @@ public: // enable decoding partially loaded images opj_decoder_set_strict_mode(decoder, OPJ_FALSE); - if (!opj_read_header(stream, decoder, &image)) + opj_image_t* img; + if (!opj_read_header(stream, decoder, &img)) { return false; } + image = img; // needs to happen before decode which may fail if (channels) @@ -393,15 +357,9 @@ public: // count was zero. The latter is just a sanity check before we // dereference the array. - if (!decoded || !image || !image->numcomps) - { - opj_end_decompress(decoder, stream); - return false; - } - + bool result = (decoded && image && image->numcomps); opj_end_decompress(decoder, stream); - - return true; + return result; } opj_image_t* getImage() { return image; } @@ -409,10 +367,18 @@ public: private: opj_dparameters_t parameters; opj_event_mgr_t event_mgr; - opj_image_t* image = nullptr; - opj_codec_t* decoder = nullptr; - opj_stream_t* stream = nullptr; - opj_codestream_info_v2_t* codestream_info = nullptr; + owning_ptr<opj_codestream_info_v2_t> codestream_info{ + nullptr, + // opj_destroy_cstr_info(opj_codestream_info_v2_t**) requires a + // pointer to pointer, which is too bad because otherwise we could + // directly pass that function as the owning_ptr's deleter. + [](opj_codestream_info_v2_t* doomed) + { + opj_destroy_cstr_info(&doomed); + }}; + owning_ptr<opj_stream_t> stream{ nullptr, opj_stream_destroy }; + owning_ptr<opj_image_t> image{ nullptr, opj_image_destroy }; + owning_ptr<opj_codec_t> decoder{ nullptr, opj_destroy_codec }; }; class JPEG2KEncode : public JPEG2KBase @@ -447,43 +413,19 @@ public: parameters.irreversible = 1; } - if (comment_text) - { - free(comment_text); - } - comment_text = comment_text_in ? strdup(comment_text_in) : nullptr; + comment_text.assign(comment_text_in? comment_text_in : "no comment"); - parameters.cp_comment = comment_text ? comment_text : (char*)"no comment"; + // Because comment_text is a member declared before parameters, + // it will outlive parameters, so we can safely store in parameters a + // pointer into comment_text's data. Unfortunately cp_comment is + // declared as (non-const) char*. We just have to trust that this is + // legacy C style coding, rather than any intention to modify the + // comment string. (If there was actual modification, we could use a + // std::vector<char> instead, but let's only go there if we must.) + parameters.cp_comment = const_cast<char*>(comment_text.c_str()); llassert(parameters.cp_comment); } - ~JPEG2KEncode() - { - if (encoder) - { - opj_destroy_codec(encoder); - } - encoder = nullptr; - - if (image) - { - opj_image_destroy(image); - } - image = nullptr; - - if (stream) - { - opj_stream_destroy(stream); - } - stream = nullptr; - - if (comment_text) - { - free(comment_text); - } - comment_text = nullptr; - } - bool encode(const LLImageRaw& rawImageIn, LLImageJ2C &compressedImageOut) { LLImageDataSharedLock lockIn(&rawImageIn); @@ -561,11 +503,6 @@ public: memset(buffer, 0, data_size_guess); - if (stream) - { - opj_stream_destroy(stream); - } - stream = opj_stream_create(data_size_guess, false); if (!stream) { @@ -742,12 +679,12 @@ public: opj_image_t* getImage() { return image; } private: - opj_cparameters_t parameters; - opj_event_mgr_t event_mgr; - opj_image_t* image = nullptr; - opj_codec_t* encoder = nullptr; - opj_stream_t* stream = nullptr; - char* comment_text = nullptr; + std::string comment_text; + opj_cparameters_t parameters; + opj_event_mgr_t event_mgr; + owning_ptr<opj_stream_t> stream{ nullptr, opj_stream_destroy }; + owning_ptr<opj_image_t> image{ nullptr, opj_image_destroy }; + owning_ptr<opj_codec_t> encoder{ nullptr, opj_destroy_codec }; }; diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp index 3685915ffd..ce6244d038 100644 --- a/indra/llinventory/llsettingssky.cpp +++ b/indra/llinventory/llsettingssky.cpp @@ -1995,6 +1995,7 @@ F32 LLSettingsSky::getCloudShadow() const void LLSettingsSky::setCloudShadow(F32 val) { mCloudShadow = val; + setDirtyFlag(true); setLLSDDirty(); } @@ -2051,6 +2052,7 @@ F32 LLSettingsSky::getMaxY() const void LLSettingsSky::setMaxY(F32 val) { mMaxY = val; + setDirtyFlag(true); setLLSDDirty(); } @@ -2062,6 +2064,7 @@ LLQuaternion LLSettingsSky::getMoonRotation() const void LLSettingsSky::setMoonRotation(const LLQuaternion &val) { mMoonRotation = val; + setDirtyFlag(true); setLLSDDirty(); } @@ -2073,6 +2076,7 @@ F32 LLSettingsSky::getMoonScale() const void LLSettingsSky::setMoonScale(F32 val) { mMoonScale = val; + setDirtyFlag(true); setLLSDDirty(); } @@ -2095,6 +2099,7 @@ F32 LLSettingsSky::getMoonBrightness() const void LLSettingsSky::setMoonBrightness(F32 brightness_factor) { mMoonBrightness = brightness_factor; + setDirtyFlag(true); setLLSDDirty(); } @@ -2131,6 +2136,7 @@ LLColor3 LLSettingsSky::getSunlightColorClamped() const void LLSettingsSky::setSunlightColor(const LLColor3 &val) { mSunlightColor = val; + setDirtyFlag(true); setLLSDDirty(); } @@ -2142,6 +2148,7 @@ LLQuaternion LLSettingsSky::getSunRotation() const void LLSettingsSky::setSunRotation(const LLQuaternion &val) { mSunRotation = val; + setDirtyFlag(true); setLLSDDirty(); } @@ -2153,6 +2160,7 @@ F32 LLSettingsSky::getSunScale() const void LLSettingsSky::setSunScale(F32 val) { mSunScale = val; + setDirtyFlag(true); setLLSDDirty(); } diff --git a/indra/llinventory/llsettingswater.cpp b/indra/llinventory/llsettingswater.cpp index 08e18ea26e..b30dbfeac2 100644 --- a/indra/llinventory/llsettingswater.cpp +++ b/indra/llinventory/llsettingswater.cpp @@ -239,15 +239,15 @@ void LLSettingsWater::blend(LLSettingsBase::ptr_t &end, F64 blendf) { mSettingFlags |= other->mSettingFlags; - mBlurMultiplier = lerp((F32)blendf, mBlurMultiplier, other->mBlurMultiplier); + mBlurMultiplier = lerp(mBlurMultiplier, other->mBlurMultiplier, (F32)blendf); lerpColor(mWaterFogColor, other->mWaterFogColor, (F32)blendf); - mWaterFogDensity = lerp((F32)blendf, mWaterFogDensity, other->mWaterFogDensity); - mFogMod = lerp((F32)blendf, mFogMod, other->mFogMod); - mFresnelOffset = lerp((F32)blendf, mFresnelOffset, other->mFresnelOffset); - mFresnelScale = lerp((F32)blendf, mFresnelScale, other->mFresnelScale); + mWaterFogDensity = lerp(mWaterFogDensity, other->mWaterFogDensity, (F32)blendf); + mFogMod = lerp(mFogMod, other->mFogMod, (F32)blendf); + mFresnelOffset = lerp(mFresnelOffset, other->mFresnelOffset, (F32)blendf); + mFresnelScale = lerp(mFresnelScale, other->mFresnelScale, (F32)blendf); lerpVector3(mNormalScale, other->mNormalScale, (F32)blendf); - mScaleAbove = lerp((F32)blendf, mScaleAbove, other->mScaleAbove); - mScaleBelow = lerp((F32)blendf, mScaleBelow, other->mScaleBelow); + mScaleAbove = lerp(mScaleAbove, other->mScaleAbove, (F32)blendf); + mScaleBelow = lerp(mScaleBelow, other->mScaleBelow, (F32)blendf); lerpVector2(mWave1Dir, other->mWave1Dir, (F32)blendf); lerpVector2(mWave2Dir, other->mWave2Dir, (F32)blendf); diff --git a/indra/llkdu/CMakeLists.txt b/indra/llkdu/CMakeLists.txt index 7cd9f5eb24..230803a630 100644 --- a/indra/llkdu/CMakeLists.txt +++ b/indra/llkdu/CMakeLists.txt @@ -42,10 +42,7 @@ if (USE_KDU) target_include_directories( llkdu INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) # Add tests - # ND: llkdu tests are very strange as they include stubs for KDU classes/methods - # if not having access to the right KDU version this test will fail to compile, incidentally I do not - # have access to a matching version of KDU and thus cannot get this tests to compile - if (LL_TESTS_KDU) + if (LL_TESTS) include(LLAddBuildTest) include(Tut) SET(llkdu_TEST_SOURCE_FILES @@ -62,6 +59,6 @@ if (USE_KDU) set_property( SOURCE ${llkdu_TEST_SOURCE_FILES} PROPERTY LL_TEST_ADDITIONAL_INCLUDE_DIRS ${llimage_include_dir}) LL_ADD_PROJECT_UNIT_TESTS(llkdu "${llkdu_TEST_SOURCE_FILES}") - endif (LL_TESTS_KDU) + endif (LL_TESTS) endif (USE_KDU) diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index bf7cfbe071..8e2bd5b2d9 100644 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -163,6 +163,7 @@ private: S32 mNumComponents; bool mUseYCC; kdu_dims mDims; + kdu_push_pull_params mParams; kdu_sample_allocator mAllocator; kdu_tile_comp mComps[4]; kdu_line_buf mLines[4]; @@ -255,7 +256,7 @@ LLImageJ2CKDU::LLImageJ2CKDU() : LLImageJ2CImpl(), mCodeStreamp(), mTPosp(), mTileIndicesp(), - mRawImagep(NULL), + mRawImagep(nullptr), mDecodeState(), mBlocksSize(-1), mPrecinctsSize(-1), @@ -292,17 +293,17 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECod // two U32s and a pointer, so it's not as if it would be a huge overhead // to allocate a new one every time. // Also -- why is base.getData() tested specifically here? If that returns - // NULL, shouldn't we bail out of the whole method? + // nullptr, shouldn't we bail out of the whole method? if (!mInputp && base.getData()) { // The compressed data has been loaded // Setup the source for the codestream - mInputp.reset(new LLKDUMemSource(base.getData(), data_size)); + mInputp = std::make_unique<LLKDUMemSource>(base.getData(), data_size); } if (mInputp) { - // This is LLKDUMemSource::reset(), not boost::scoped_ptr::reset(). + // This is LLKDUMemSource::reset(), not std::unique_ptr::reset(). mInputp->reset(); } @@ -312,7 +313,7 @@ void LLImageJ2CKDU::setupCodeStream(LLImageJ2C &base, bool keep_codestream, ECod // *TODO: This seems to be wrong. The base class should have no idea of // how j2c compression works so no good way of computing what's the byte // range to be used. - mCodeStreamp->set_max_bytes(max_bytes,true); + mCodeStreamp->set_max_bytes(max_bytes); // If you want to flip or rotate the image for some reason, change // the resolution, or identify a restricted region of interest, this is @@ -446,8 +447,8 @@ bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco mCodeStreamp->change_appearance(false, true, false); // Apply loading discard level and cropping if required - kdu_dims* region_kdu = NULL; - if (region != NULL) + kdu_dims* region_kdu = nullptr; + if (region != nullptr) { region_kdu = new kdu_dims; region_kdu->pos.x = region[0]; @@ -464,7 +465,7 @@ bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco if (region_kdu) { delete region_kdu; - region_kdu = NULL; + region_kdu = nullptr; } // Resize raw_image according to the image to be decoded @@ -475,12 +476,12 @@ bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco if (!mTileIndicesp) { - mTileIndicesp.reset(new kdu_dims); + mTileIndicesp = std::make_unique<kdu_dims>(); } mCodeStreamp->get_valid_tiles(*mTileIndicesp); if (!mTPosp) { - mTPosp.reset(new kdu_coords); + mTPosp = std::make_unique<kdu_coords>(); mTPosp->y = 0; mTPosp->x = 0; } @@ -490,7 +491,7 @@ bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco base.setLastError(msg.what()); return false; } - catch (kdu_exception kdu_value) + catch (const kdu_exception& kdu_value) { // KDU internally throws kdu_exception. It's possible that such an // exception might leak out into our code. Catch kdu_exception @@ -581,8 +582,7 @@ bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco kdu_coords offset = tile_dims.pos - dims.pos; int row_gap = channels*dims.size.x; // inter-row separation kdu_byte *buf = buffer + offset.y*row_gap + offset.x*channels; - mDecodeState.reset(new LLKDUDecodeState(tile, buf, row_gap, - mCodeStreamp.get())); + mDecodeState = std::make_unique<LLKDUDecodeState>(tile, buf, row_gap, mCodeStreamp.get()); } // Do the actual processing F32 remaining_time = limit_time ? decode_time - decode_timer.getElapsedTimeF32().value() : 0.0f; @@ -607,7 +607,7 @@ bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco cleanupCodeStream(); return true; // done } - catch (kdu_exception kdu_value) + catch (const kdu_exception& kdu_value) { // KDU internally throws kdu_exception. It's possible that such an // exception might leak out into our code. Catch kdu_exception @@ -810,7 +810,7 @@ bool LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co base.setLastError(msg.what()); return false; } - catch (kdu_exception kdu_value) + catch (const kdu_exception& kdu_value) { // KDU internally throws kdu_exception. It's possible that such an // exception might leak out into our code. Catch kdu_exception @@ -844,7 +844,7 @@ bool LLImageJ2CKDU::getMetadata(LLImageJ2C &base) base.setLastError(msg.what()); return false; } - catch (kdu_exception kdu_value) + catch (const kdu_exception& kdu_value) { // KDU internally throws kdu_exception. It's possible that such an // exception might leak out into our code. Catch kdu_exception @@ -976,8 +976,8 @@ void LLImageJ2CKDU::findDiscardLevelsBoundaries(LLImageJ2C &base) //std::cout << "Parsing discard level = " << discard_level << std::endl; // Create the input codestream object. setupCodeStream(base, true, MODE_FAST); - mCodeStreamp->apply_input_restrictions(0, 4, discard_level, 0, NULL); - mCodeStreamp->set_max_bytes(KDU_LONG_MAX,true); + mCodeStreamp->apply_input_restrictions(0, 4, discard_level, 0, nullptr); + mCodeStreamp->set_max_bytes(KDU_LONG_MAX,false); siz_params *siz_in = mCodeStreamp->access_siz(); // Create the output codestream object. @@ -1073,8 +1073,10 @@ void LLImageJ2CKDU::findDiscardLevelsBoundaries(LLImageJ2C &base) void set_default_colour_weights(kdu_params *siz) { + kdu_params *enc = siz->access_cluster(ENC_params); + assert(enc != nullptr); kdu_params *cod = siz->access_cluster(COD_params); - assert(cod != NULL); + assert(cod != nullptr); bool can_use_ycc = true; bool rev0 = false; @@ -1111,7 +1113,7 @@ void set_default_colour_weights(kdu_params *siz) return; } float weight; - if (cod->get(Clev_weights,0,0,weight) || cod->get(Cband_weights,0,0,weight)) + if (enc->get(Clev_weights,0,0,weight) || enc->get(Cband_weights,0,0,weight)) { // Weights already specified explicitly -> nothing to do return; @@ -1120,17 +1122,16 @@ void set_default_colour_weights(kdu_params *siz) // These example weights are adapted from numbers generated by Marcus Nadenau // at EPFL, for a viewing distance of 15 cm and a display resolution of // 300 DPI. - - cod->parse_string("Cband_weights:C0=" + enc->parse_string("Cband_weights:C0=" "{0.0901},{0.2758},{0.2758}," "{0.7018},{0.8378},{0.8378},{1}"); - cod->parse_string("Cband_weights:C1=" + enc->parse_string("Cband_weights:C1=" "{0.0263},{0.0863},{0.0863}," "{0.1362},{0.2564},{0.2564}," "{0.3346},{0.4691},{0.4691}," "{0.5444},{0.6523},{0.6523}," "{0.7078},{0.7797},{0.7797},{1}"); - cod->parse_string("Cband_weights:C2=" + enc->parse_string("Cband_weights:C2=" "{0.0773},{0.1835},{0.1835}," "{0.2598},{0.4130},{0.4130}," "{0.5040},{0.6464},{0.6464}," @@ -1149,7 +1150,7 @@ byte buffer, spacing successive output samples apart by `gap' bytes all necessary level shifting, type conversion, rounding and truncation. */ { int width = src.get_width(); - if (src.get_buf32() != NULL) + if (src.get_buf32() != nullptr) { // Decompressed samples have a 32-bit representation (integer or float) assert(precision >= 8); // Else would have used 16 bit representation kdu_sample32 *sp = src.get_buf32(); @@ -1312,11 +1313,11 @@ LLKDUDecodeState::LLKDUDecodeState(kdu_tile tile, kdu_byte *buf, S32 row_gap, mLines[c].pre_create(&mAllocator,mDims.size.x,mReversible[c],use_shorts,0,0); if (res.which() == 0) // No DWT levels used { - mEngines[c] = kdu_decoder(res.access_subband(LL_BAND),&mAllocator,use_shorts); + mEngines[c] = kdu_decoder(res.access_subband(LL_BAND), &mAllocator, mParams, use_shorts); } else { - mEngines[c] = kdu_synthesis(res,&mAllocator,use_shorts); + mEngines[c] = kdu_synthesis(res, &mAllocator, mParams, use_shorts); } } mAllocator.finalize(*codestreamp); // Actually creates buffering resources @@ -1394,7 +1395,7 @@ kdc_flow_control::kdc_flow_control (kdu_supp::kdu_image_in_base *img_in, kdu_cod this->codestream = codestream; codestream.get_valid_tiles(valid_tile_indices); tile_idx = valid_tile_indices.pos; - tile = codestream.open_tile(tile_idx,NULL); + tile = codestream.open_tile(tile_idx, nullptr); // Set up the individual components num_components = codestream.get_num_components(true); @@ -1403,7 +1404,7 @@ kdc_flow_control::kdc_flow_control (kdu_supp::kdu_image_in_base *img_in, kdu_cod kdc_component_flow_control *comp = components; for (n = 0; n < num_components; n++, comp++) { - comp->line = NULL; + comp->line = nullptr; comp->reader = img_in; kdu_coords subsampling; codestream.get_subsampling(n,subsampling,true); @@ -1420,12 +1421,12 @@ kdc_flow_control::kdc_flow_control (kdu_supp::kdu_image_in_base *img_in, kdu_cod assert(num_components >= 0); tile.set_components_of_interest(num_components); - max_buffer_memory = engine.create(codestream,tile,false,NULL,false,1,NULL,NULL,false); + max_buffer_memory = engine.create(codestream, tile, false, nullptr, false, 1, nullptr, nullptr,false); } kdc_flow_control::~kdc_flow_control() { - if (components != NULL) + if (components != nullptr) { delete[] components; } @@ -1452,8 +1453,8 @@ bool kdc_flow_control::advance_components() if (comp->ratio_counter < 0) { found_line = true; - comp->line = engine.exchange_line(n,NULL,NULL); - assert(comp->line != NULL); + comp->line = engine.exchange_line(n,nullptr,nullptr); + assert(comp->line != nullptr); if (comp->line->get_width()) { comp->reader->get(n,*(comp->line),0); @@ -1480,9 +1481,9 @@ void kdc_flow_control::process_components() assert(comp->ratio_counter >= 0); assert(comp->remaining_lines > 0); comp->remaining_lines--; - assert(comp->line != NULL); - engine.exchange_line(n,comp->line,NULL); - comp->line = NULL; + assert(comp->line != nullptr); + engine.exchange_line(n,comp->line,nullptr); + comp->line = nullptr; } } } diff --git a/indra/llkdu/llimagej2ckdu.h b/indra/llkdu/llimagej2ckdu.h index 8037c81868..8481201cbc 100644 --- a/indra/llkdu/llimagej2ckdu.h +++ b/indra/llkdu/llimagej2ckdu.h @@ -32,7 +32,6 @@ // // KDU core header files // -#define KDU_NO_THREADS #include "kdu_elementary.h" #include "kdu_messaging.h" #include "kdu_params.h" @@ -41,7 +40,6 @@ #include "include_kdu_xxxx.h" #include "kdu_sample_processing.h" -#include <boost/scoped_ptr.hpp> #include <boost/noncopyable.hpp> class LLKDUDecodeState; diff --git a/indra/llkdu/llkdumem.h b/indra/llkdu/llkdumem.h index 177a8ea976..0ad67a3f92 100644 --- a/indra/llkdu/llkdumem.h +++ b/indra/llkdu/llkdumem.h @@ -28,8 +28,6 @@ #define LL_LLKDUMEM_H // Support classes for reading and writing from memory buffers in KDU -#define KDU_NO_THREADS - #define kdu_xxxx "kdu_image.h" #include "include_kdu_xxxx.h" @@ -54,9 +52,7 @@ public: mCurPos = 0; } - ~LLKDUMemSource() - { - } + ~LLKDUMemSource() = default; int read(kdu_core::kdu_byte *buf, int num_bytes) { @@ -94,9 +90,7 @@ public: mOutputSize = &output_size; } - ~LLKDUMemTarget() - { - } + ~LLKDUMemTarget() = default; bool write(const kdu_core::kdu_byte *buf, int num_bytes) { diff --git a/indra/llkdu/tests/llimagej2ckdu_test.cpp b/indra/llkdu/tests/llimagej2ckdu_test.cpp index db81d60d9e..36b79047b6 100644 --- a/indra/llkdu/tests/llimagej2ckdu_test.cpp +++ b/indra/llkdu/tests/llimagej2ckdu_test.cpp @@ -139,18 +139,19 @@ int kdu_tile_comp::get_bit_depth(bool ) { return 8; } bool kdu_tile_comp::get_reversible() { return false; } int kdu_tile_comp::get_num_resolutions() { return 1; } kdu_subband kdu_resolution::access_subband(int ) { kdu_subband a; return a; } -void kdu_resolution::get_dims(kdu_dims& ) { } -int kdu_resolution::which() { return 0; } -int kdu_resolution::get_valid_band_indices(int &) { return 1; } -kdu_synthesis::kdu_synthesis(kdu_resolution, kdu_sample_allocator*, bool, float, kdu_thread_env*, kdu_thread_queue*) { } +void kdu_resolution::get_dims(kdu_dims& ) const { } +int kdu_resolution::which() const { return 0; } +int kdu_resolution::get_valid_band_indices(int &) const { return 1; } +kdu_synthesis::kdu_synthesis(kdu_resolution, kdu_sample_allocator*, kdu_push_pull_params&, bool, float, kdu_thread_env*, kdu_thread_queue*) { } //kdu_params::kdu_params(const char*, bool, bool, bool, bool, bool) { } -kdu_params::kdu_params(const char*, bool, bool, bool, bool, bool, kd_core_local::kd_coremem*) {} +kdu_params::kdu_params(const char*, bool, bool, bool, bool, bool) {} kdu_params::~kdu_params() { } +void kdu_params::destroy() { } void kdu_params::set(const char* , int , int , bool ) { } void kdu_params::set(const char* , int , int , int ) { } void kdu_params::finalize_all(bool ) { } void kdu_params::finalize_all(int, bool ) { } -void kdu_params::copy_from(kdu_params*, int, int, int, int, int, bool, bool, bool) { } +void kdu_params::copy_from(kdu_params*, int, int, int, int, int, bool, bool, bool, bool) { } bool kdu_params::parse_string(const char*) { return false; } bool kdu_params::get(const char*, int, int, bool&, bool, bool, bool) { return false; } bool kdu_params::get(const char*, int, int, float&, bool, bool, bool) { return false; } @@ -159,13 +160,13 @@ kdu_params* kdu_params::access_relation(int, int, int, bool) { return NULL; } kdu_params* kdu_params::access_cluster(const char*) { return NULL; } void kdu_codestream::set_fast() { } void kdu_codestream::set_fussy() { } -void kdu_codestream::get_dims(int, kdu_dims&, bool ) { } +void kdu_codestream::get_dims(int, kdu_dims&, bool ) const { } int kdu_codestream::get_min_dwt_levels() { return 5; } int kdu_codestream::get_max_tile_layers() { return 1; } void kdu_codestream::change_appearance(bool, bool, bool, kdu_thread_env *) {} void kdu_codestream::get_tile_dims(kdu_coords, int, kdu_dims&, bool ) { } void kdu_codestream::destroy() { } -void kdu_codestream::collect_timing_stats(int ) { } +void kdu_codestream::collect_timing_stats(int ) const { } void kdu_codestream::set_max_bytes(kdu_long, bool, bool ) { } void kdu_codestream::get_valid_tiles(kdu_dims& ) { } void kdu_codestream::create( @@ -182,19 +183,21 @@ void kdu_codestream::get_subsampling(int , kdu_coords&, bool ) { } void kdu_codestream::flush(kdu_long *, int, kdu_uint16 *, bool, bool, double, kdu_thread_env*, int) { } void kdu_codestream::set_resilient(bool ) { } int kdu_codestream::get_num_components(bool ) { return 0; } -kdu_long kdu_codestream::get_total_bytes(bool ) { return 0; } +kdu_long kdu_codestream::get_total_bytes(bool ) const { return 0; } kdu_long kdu_codestream::get_compressed_data_memory(bool ) const {return 0; } void kdu_codestream::share_buffering(kdu_codestream ) { } -int kdu_codestream::get_num_tparts() { return 0; } +int kdu_codestream::get_num_tparts() const { return 0; } int kdu_codestream::trans_out(kdu_long, kdu_long*, int, bool, kdu_thread_env* ) { return 0; } bool kdu_codestream::ready_for_flush(kdu_thread_env*) { return false; } siz_params* kdu_codestream::access_siz() { return NULL; } kdu_tile kdu_codestream::open_tile(kdu_coords , kdu_thread_env* ) { kdu_tile a; return a; } kdu_codestream_comment kdu_codestream::add_comment(kdu_thread_env*) { kdu_codestream_comment a; return a; } +kdu_codestream_comment kdu_codestream::get_comment(kdu_codestream_comment) { kdu_codestream_comment a; return a; }; void kdu_subband::close_block(kdu_block*, kdu_thread_env*) { } void kdu_subband::get_valid_blocks(kdu_dims &indices) const { } kdu_block * kdu_subband::open_block(kdu_coords, int *, kdu_thread_env *, int, bool) { return NULL; } bool kdu_codestream_comment::put_text(const char*) { return false; } +const char *kdu_codestream_comment::get_text() { return nullptr; }; void kdu_customize_warnings(kdu_message*) { } void kdu_customize_errors(kdu_message*) { } kdu_long kdu_multi_analysis::create( @@ -209,16 +212,18 @@ kdu_long kdu_multi_analysis::create( const kdu_push_pull_params*, kdu_membroker*) { return kdu_long(0); } void kdu_multi_analysis::destroy(kdu_thread_env *) {} +siz_params::siz_params() : kdu_params(NULL, false, false, false, false, false) { } siz_params::siz_params(kd_core_local::kd_coremem*) : kdu_params(NULL, false, false, false, false, false) { } siz_params::~siz_params() {} void siz_params::finalize(bool ) { } void siz_params::copy_with_xforms(kdu_params*, int, int, bool, bool, bool) { } -int siz_params::write_marker_segment(kdu_output*, kdu_params*, int) { return 0; } +int siz_params::write_marker_segment(kdu_output*, kdu_params*, int, int&) { return 0; } bool siz_params::check_marker_segment(kdu_uint16, int, kdu_byte a[], int&) { return false; } -bool siz_params::read_marker_segment(kdu_uint16, int, kdu_byte a[], int) { return false; } +int siz_params::read_marker_segment(kdu_uint16 code, int num_bytes, kdu_byte bytes[], int tpart_idx) { return false; } kdu_decoder::kdu_decoder( kdu_subband subband, kdu_sample_allocator*, + kdu_push_pull_params&, bool, float, int, kdu_thread_env*, kdu_thread_queue*, diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index eb29df245a..801d3fe63e 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -45,8 +45,6 @@ set(llmath_SOURCE_FILES set(llmath_HEADER_FILES CMakeLists.txt - camera.h - coordframe.h llbbox.h llbboxlocal.h llcalc.h diff --git a/indra/llmath/llcalc.cpp b/indra/llmath/llcalc.cpp index e060b5df58..311e1e7059 100644 --- a/indra/llmath/llcalc.cpp +++ b/indra/llmath/llcalc.cpp @@ -116,17 +116,6 @@ void LLCalc::clearAllVariables() mVariables.clear(); } -/* -void LLCalc::updateVariables(LLSD& vars) -{ - LLSD::map_iterator cIt = vars.beginMap(); - for(; cIt != vars.endMap(); cIt++) - { - setVar(cIt->first, (F32)(LLSD::Real)cIt->second); - } -} -*/ - bool LLCalc::evalString(const std::string& expression, F32& result) { std::string expr_upper = expression; diff --git a/indra/llmath/llcalc.h b/indra/llmath/llcalc.h index 09672eb13b..14797de996 100644 --- a/indra/llmath/llcalc.h +++ b/indra/llmath/llcalc.h @@ -73,10 +73,9 @@ public: void setVar(const std::string& name, const F32& value); void clearVar(const std::string& name); void clearAllVariables(); -// void updateVariables(LLSD& vars); bool evalString(const std::string& expression, F32& result); - std::string::size_type getLastErrorPos() { return mLastErrorPos; } + std::string::size_type getLastErrorPos() const { return mLastErrorPos; } static LLCalc* getInstance(); static void cleanUp(); @@ -89,10 +88,6 @@ private: calc_map_t mConstants; calc_map_t mVariables; - // *TODO: Add support for storing user defined variables, and stored functions. - // Will need UI work, and a means to save them between sessions. -// calc_map_t mUserVariables; - // "There shall be only one" static LLCalc* sInstance; }; diff --git a/indra/llmath/llcalcparser.h b/indra/llmath/llcalcparser.h index ec7f6a2cb6..b7f84a6546 100644 --- a/indra/llmath/llcalcparser.h +++ b/indra/llmath/llcalcparser.h @@ -182,7 +182,6 @@ private: LLCalc::calc_map_t* mConstants; LLCalc::calc_map_t* mVariables; -// LLCalc::calc_map_t* mUserVariables; F32& mResult; }; diff --git a/indra/llmath/llcoordframe.cpp b/indra/llmath/llcoordframe.cpp index 4d6276b2cd..15c9f6ff3f 100644 --- a/indra/llmath/llcoordframe.cpp +++ b/indra/llmath/llcoordframe.cpp @@ -26,7 +26,6 @@ #include "linden_common.h" -//#include "vmath.h" #include "v3math.h" #include "m3math.h" #include "v4math.h" diff --git a/indra/llmath/llcoordframe.h b/indra/llmath/llcoordframe.h index aaa701f792..391d58dc27 100644 --- a/indra/llmath/llcoordframe.h +++ b/indra/llmath/llcoordframe.h @@ -61,7 +61,7 @@ public: //LLCoordFrame(const F32 *origin, const F32 *rotation); // Assumes "origin" is 1x3 and "rotation" is 1x9 array //LLCoordFrame(const F32 *origin_and_rotation); // Assumes "origin_and_rotation" is 1x12 array - bool isFinite() { return mOrigin.isFinite() && mXAxis.isFinite() && mYAxis.isFinite() && mZAxis.isFinite(); } + bool isFinite() const { return mOrigin.isFinite() && mXAxis.isFinite() && mYAxis.isFinite() && mZAxis.isFinite(); } void reset(); void resetAxes(); @@ -118,8 +118,6 @@ public: // Same as above, except it also includes the translation of the LLFrame // LLMatrix4 getMatrix4() const; // Returns position and axes in 4x4 matrix - // Returns matrix which expresses point in local frame in the parent frame - void getMatrixToParent(LLMatrix4 &mat) const; // Returns matrix which expresses point in parent frame in the local frame void getMatrixToLocal(LLMatrix4 &mat) const; // Returns matrix which expresses point in parent frame in the local frame diff --git a/indra/llmath/llinterp.h b/indra/llmath/llinterp.h index f4faa82a82..336539fb2e 100644 --- a/indra/llmath/llinterp.h +++ b/indra/llmath/llinterp.h @@ -29,11 +29,11 @@ #if defined(LL_WINDOWS) // macro definitions for common math constants (e.g. M_PI) are declared under the _USE_MATH_DEFINES // on Windows system. -// So, let's define _USE_MATH_DEFINES before including math.h +// So, let's define _USE_MATH_DEFINES before including cmath #define _USE_MATH_DEFINES #endif -#include "math.h" +#include <cmath> // Class from which different types of interpolators can be derived @@ -354,7 +354,7 @@ void LLInterpAttractor<Type>::update(const F32 time) ////////////////////////////// // -// LLInterpFucn derived class implementation. +// LLInterpFunc derived class implementation. // diff --git a/indra/llmath/llline.h b/indra/llmath/llline.h index 33c1eb61a4..e98e173d1f 100644 --- a/indra/llmath/llline.h +++ b/indra/llmath/llline.h @@ -33,7 +33,7 @@ #include "stdtypes.h" #include "v3math.h" -const F32 DEFAULT_INTERSECTION_ERROR = 0.000001f; +constexpr F32 DEFAULT_INTERSECTION_ERROR = 0.000001f; class LLLine { diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index fa315291a3..0348dad8fe 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -358,10 +358,7 @@ inline F32 snap_to_sig_figs(F32 foo, S32 sig_figs) return new_foo; } -inline F32 lerp(F32 a, F32 b, F32 u) -{ - return a + ((b - a) * u); -} +using std::lerp; inline F32 lerp2d(F32 x00, F32 x01, F32 x10, F32 x11, F32 u, F32 v) { @@ -486,7 +483,7 @@ inline U32 get_next_power_two(U32 val, U32 max_power_two) //get the gaussian value given the linear distance from axis x and guassian value o inline F32 llgaussian(F32 x, F32 o) { - return 1.f/(F_SQRT_TWO_PI*o)*powf(F_E, -(x*x)/(2*o*o)); + return 1.f/(F_SQRT_TWO_PI*o)*powf(F_E, -(x*x)/(2.f*o*o)); } //helper function for removing outliers diff --git a/indra/llmath/llquaternion.cpp b/indra/llmath/llquaternion.cpp index aefb82b2f0..1ab3a73d79 100644 --- a/indra/llmath/llquaternion.cpp +++ b/indra/llmath/llquaternion.cpp @@ -30,7 +30,6 @@ #include "llquaternion.h" -//#include "vmath.h" #include "v3math.h" #include "v3dmath.h" #include "v4math.h" diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 700e61467b..00a56edf68 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -1294,9 +1294,9 @@ void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 en c = cos(ang)*lerp(radius_start, radius_end, t); - pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) + pt->mPos.set(0 + lerp(0.f,params.getShear().mV[0],s) + lerp(-skew ,skew, t) * 0.5f, - c + lerp(0,params.getShear().mV[1],s), + c + lerp(0.f,params.getShear().mV[1],s), s); pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), hole_y * lerp(taper_y_begin, taper_y_end, t), @@ -1327,9 +1327,9 @@ void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 en c = cos(ang)*lerp(radius_start, radius_end, t); s = sin(ang)*lerp(radius_start, radius_end, t); - pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) + pt->mPos.set(0 + lerp(0.f,params.getShear().mV[0],s) + lerp(-skew ,skew, t) * 0.5f, - c + lerp(0,params.getShear().mV[1],s), + c + lerp(0.f,params.getShear().mV[1],s), s); pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), @@ -1354,9 +1354,9 @@ void LLPath::genNGon(const LLPathParams& params, S32 sides, F32 startOff, F32 en c = cos(ang)*lerp(radius_start, radius_end, t); s = sin(ang)*lerp(radius_start, radius_end, t); - pt->mPos.set(0 + lerp(0,params.getShear().mV[0],s) + pt->mPos.set(0 + lerp(0.f,params.getShear().mV[0],s) + lerp(-skew ,skew, t) * 0.5f, - c + lerp(0,params.getShear().mV[1],s), + c + lerp(0.f,params.getShear().mV[1],s), s); pt->mScale.set(hole_x * lerp(taper_x_begin, taper_x_end, t), hole_y * lerp(taper_y_begin, taper_y_end, t), @@ -1494,8 +1494,8 @@ bool LLPath::generate(const LLPathParams& params, F32 detail, S32 split, for (S32 i=0;i<np;i++) { F32 t = lerp(params.getBegin(),params.getEnd(),(F32)i * mStep); - mPath[i].mPos.set(lerp(0,params.getShear().mV[0],t), - lerp(0,params.getShear().mV[1],t), + mPath[i].mPos.set(lerp(0.f,params.getShear().mV[0],t), + lerp(0.f,params.getShear().mV[1],t), t - 0.5f); LLQuaternion quat; quat.setQuat(lerp(F_PI * params.getTwistBegin(),F_PI * params.getTwist(),t),0,0,1); @@ -1559,10 +1559,10 @@ bool LLPath::generate(const LLPathParams& params, F32 detail, S32 split, { F32 t = (F32)i * mStep; mPath[i].mPos.set(0, - lerp(0, -sin(F_PI*params.getTwist()*t)*0.5f,t), + lerp(0.f, -sin(F_PI*params.getTwist()*t)*0.5f,t), lerp(-0.5f, cos(F_PI*params.getTwist()*t)*0.5f,t)); - mPath[i].mScale.set(lerp(1,params.getScale().mV[0],t), - lerp(1,params.getScale().mV[1],t), 0,1); + mPath[i].mScale.set(lerp(1.f,params.getScale().mV[0],t), + lerp(1.f,params.getScale().mV[1],t), 0.f, 1.f); mPath[i].mTexT = t; LLQuaternion quat; quat.setQuat(F_PI * params.getTwist() * t,1,0,0); diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index 27c5fc5a49..3496928f7b 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -45,7 +45,6 @@ class LLVolumeOctree; #include "lluuid.h" #include "v4color.h" -//#include "vmath.h" #include "v2math.h" #include "v3math.h" #include "v3dmath.h" diff --git a/indra/llmath/llvolumemgr.cpp b/indra/llmath/llvolumemgr.cpp index bb0c94d513..d8f649140f 100644 --- a/indra/llmath/llvolumemgr.cpp +++ b/indra/llmath/llvolumemgr.cpp @@ -25,6 +25,7 @@ #include "linden_common.h" +#include "llmutex.h" #include "llvolumemgr.h" #include "llvolume.h" diff --git a/indra/llmath/m3math.cpp b/indra/llmath/m3math.cpp index 472d340af5..3c2097f947 100644 --- a/indra/llmath/m3math.cpp +++ b/indra/llmath/m3math.cpp @@ -26,7 +26,6 @@ #include "linden_common.h" -//#include "vmath.h" #include "v3math.h" #include "v3dmath.h" #include "v4math.h" diff --git a/indra/llmath/m3math.h b/indra/llmath/m3math.h index cd14290246..22b11d13b1 100644 --- a/indra/llmath/m3math.h +++ b/indra/llmath/m3math.h @@ -119,7 +119,6 @@ class LLMatrix3 // foo.rotate(bar) // foo = foo * bar // That is, foo.rotate(bar) multiplies foo by bar FROM THE RIGHT - const LLMatrix3& rotate(const F32 angle, const F32 x, const F32 y, const F32 z); // Rotate matrix by rotating angle radians about (x, y, z) const LLMatrix3& rotate(const F32 angle, const LLVector3 &vec); // Rotate matrix by rotating angle radians about vec const LLMatrix3& rotate(const F32 roll, const F32 pitch, const F32 yaw); // Rotate matrix by roll (about x), pitch (about y), and yaw (about z) const LLMatrix3& rotate(const LLQuaternion &q); // Transform matrix by Euler angles and translating by pos diff --git a/indra/llmath/m4math.cpp b/indra/llmath/m4math.cpp index c46ee587cb..a9853fe7e9 100644 --- a/indra/llmath/m4math.cpp +++ b/indra/llmath/m4math.cpp @@ -26,7 +26,6 @@ #include "linden_common.h" -//#include "vmath.h" #include "v3math.h" #include "v4math.h" #include "m4math.h" diff --git a/indra/llmath/raytrace.cpp b/indra/llmath/raytrace.cpp index 893bf1fc70..465e56b6f3 100644 --- a/indra/llmath/raytrace.cpp +++ b/indra/llmath/raytrace.cpp @@ -26,8 +26,7 @@ #include "linden_common.h" -#include "math.h" -//#include "vmath.h" +#include <cmath> #include "v3math.h" #include "llquaternion.h" #include "m3math.h" diff --git a/indra/llmath/tests/v3dmath_test.cpp b/indra/llmath/tests/v3dmath_test.cpp index db08419012..36ad1067a2 100644 --- a/indra/llmath/tests/v3dmath_test.cpp +++ b/indra/llmath/tests/v3dmath_test.cpp @@ -504,14 +504,11 @@ namespace tut template<> template<> void v3dmath_object::test<24>() { -#if LL_WINDOWS && _MSC_VER < 1400 - skip("This fails on VS2003!"); -#else F64 x = 10., y = 20., z = -15.; F64 angle1, angle2; LLVector3d vec3Da(x,y,z), vec3Db(x,y,z); angle1 = angle_between(vec3Da, vec3Db); - ensure("1:angle_between: Fail ", (0 == angle1)); + ensure_approximately_equals_range("1:angle_between: Fail ", angle1, 0., 1.5e-8); F64 x1 = -1., y1 = -20., z1 = -1.; vec3Da.clearVec(); vec3Da.setVec(x1,y1,z1); @@ -520,12 +517,6 @@ namespace tut vec3Da.normVec(); F64 angle = vec3Db*vec3Da; angle = acos(angle); -#if LL_WINDOWS && _MSC_VER > 1900 - skip("This fails on VS2017!"); -#else - ensure("2:angle_between: Fail ", (angle == angle2)); -#endif - -#endif + ensure_equals("2:angle_between: Fail ", angle, angle2); } } diff --git a/indra/llmath/v3color.cpp b/indra/llmath/v3color.cpp index 4367b993f8..08b3795020 100644 --- a/indra/llmath/v3color.cpp +++ b/indra/llmath/v3color.cpp @@ -32,74 +32,79 @@ LLColor3 LLColor3::white(1.0f, 1.0f, 1.0f); LLColor3 LLColor3::black(0.0f, 0.0f, 0.0f); -LLColor3 LLColor3::grey (0.5f, 0.5f, 0.5f); +LLColor3 LLColor3::grey(0.5f, 0.5f, 0.5f); -LLColor3::LLColor3(const LLColor4 &a) +LLColor3::LLColor3(const LLColor4& a) { - mV[0] = a.mV[0]; - mV[1] = a.mV[1]; - mV[2] = a.mV[2]; + mV[VRED] = a.mV[VRED]; + mV[VGREEN] = a.mV[VGREEN]; + mV[VBLUE] = a.mV[VBLUE]; } -LLColor3::LLColor3(const LLVector4 &a) +LLColor3::LLColor3(const LLVector4& a) { - mV[0] = a.mV[0]; - mV[1] = a.mV[1]; - mV[2] = a.mV[2]; + mV[VRED] = a.mV[VRED]; + mV[VGREEN] = a.mV[VGREEN]; + mV[VBLUE] = a.mV[VBLUE]; } -LLColor3::LLColor3(const LLSD &sd) +LLColor3::LLColor3(const LLSD& sd) { setValue(sd); } -const LLColor3& LLColor3::operator=(const LLColor4 &a) +const LLColor3& LLColor3::operator=(const LLColor4& a) { - mV[0] = a.mV[0]; - mV[1] = a.mV[1]; - mV[2] = a.mV[2]; + mV[VRED] = a.mV[VRED]; + mV[VGREEN] = a.mV[VGREEN]; + mV[VBLUE] = a.mV[VBLUE]; return (*this); } -std::ostream& operator<<(std::ostream& s, const LLColor3 &a) +std::ostream& operator<<(std::ostream& s, const LLColor3& a) { s << "{ " << a.mV[VRED] << ", " << a.mV[VGREEN] << ", " << a.mV[VBLUE] << " }"; return s; } -static F32 hueToRgb ( F32 val1In, F32 val2In, F32 valHUeIn ) +static F32 hueToRgb(F32 val1In, F32 val2In, F32 valHUeIn) { - if ( valHUeIn < 0.0f ) valHUeIn += 1.0f; - if ( valHUeIn > 1.0f ) valHUeIn -= 1.0f; - if ( ( 6.0f * valHUeIn ) < 1.0f ) return ( val1In + ( val2In - val1In ) * 6.0f * valHUeIn ); - if ( ( 2.0f * valHUeIn ) < 1.0f ) return ( val2In ); - if ( ( 3.0f * valHUeIn ) < 2.0f ) return ( val1In + ( val2In - val1In ) * ( ( 2.0f / 3.0f ) - valHUeIn ) * 6.0f ); - return ( val1In ); + if (valHUeIn < 0.0f) + valHUeIn += 1.0f; + if (valHUeIn > 1.0f) + valHUeIn -= 1.0f; + if ((6.0f * valHUeIn) < 1.0f) + return (val1In + (val2In - val1In) * 6.0f * valHUeIn); + if ((2.0f * valHUeIn) < 1.0f) + return (val2In); + if ((3.0f * valHUeIn) < 2.0f) + return (val1In + (val2In - val1In) * ((2.0f / 3.0f) - valHUeIn) * 6.0f); + return (val1In); } -void LLColor3::setHSL ( F32 hValIn, F32 sValIn, F32 lValIn) +void LLColor3::setHSL(F32 hValIn, F32 sValIn, F32 lValIn) { - if ( sValIn < 0.00001f ) + if (sValIn < 0.00001f) { - mV[VRED] = lValIn; + mV[VRED] = lValIn; mV[VGREEN] = lValIn; - mV[VBLUE] = lValIn; + mV[VBLUE] = lValIn; } else { F32 interVal1; F32 interVal2; - if ( lValIn < 0.5f ) - interVal2 = lValIn * ( 1.0f + sValIn ); + if (lValIn < 0.5f) + interVal2 = lValIn * (1.0f + sValIn); else - interVal2 = ( lValIn + sValIn ) - ( sValIn * lValIn ); + interVal2 = (lValIn + sValIn) - (sValIn * lValIn); interVal1 = 2.0f * lValIn - interVal2; - mV[VRED] = hueToRgb ( interVal1, interVal2, hValIn + ( 1.f / 3.f ) ); - mV[VGREEN] = hueToRgb ( interVal1, interVal2, hValIn ); - mV[VBLUE] = hueToRgb ( interVal1, interVal2, hValIn - ( 1.f / 3.f ) ); + mV[VRED] = hueToRgb(interVal1, interVal2, hValIn + (1.f / 3.f)); + mV[VGREEN] = hueToRgb(interVal1, interVal2, hValIn); + mV[VBLUE] = hueToRgb(interVal1, interVal2, hValIn - (1.f / 3.f)); } } @@ -109,45 +114,48 @@ void LLColor3::calcHSL(F32* hue, F32* saturation, F32* luminance) const F32 var_G = mV[VGREEN]; F32 var_B = mV[VBLUE]; - F32 var_Min = ( var_R < ( var_G < var_B ? var_G : var_B ) ? var_R : ( var_G < var_B ? var_G : var_B ) ); - F32 var_Max = ( var_R > ( var_G > var_B ? var_G : var_B ) ? var_R : ( var_G > var_B ? var_G : var_B ) ); + F32 var_Min = (var_R < (var_G < var_B ? var_G : var_B) ? var_R : (var_G < var_B ? var_G : var_B)); + F32 var_Max = (var_R > (var_G > var_B ? var_G : var_B) ? var_R : (var_G > var_B ? var_G : var_B)); F32 del_Max = var_Max - var_Min; - F32 L = ( var_Max + var_Min ) / 2.0f; + F32 L = (var_Max + var_Min) / 2.0f; F32 H = 0.0f; F32 S = 0.0f; - if ( del_Max == 0.0f ) + if (del_Max == 0.0f) { - H = 0.0f; - S = 0.0f; + H = 0.0f; + S = 0.0f; } else { - if ( L < 0.5 ) - S = del_Max / ( var_Max + var_Min ); + if (L < 0.5) + S = del_Max / (var_Max + var_Min); else - S = del_Max / ( 2.0f - var_Max - var_Min ); + S = del_Max / (2.0f - var_Max - var_Min); - F32 del_R = ( ( ( var_Max - var_R ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; - F32 del_G = ( ( ( var_Max - var_G ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; - F32 del_B = ( ( ( var_Max - var_B ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; + F32 del_R = (((var_Max - var_R) / 6.0f) + (del_Max / 2.0f)) / del_Max; + F32 del_G = (((var_Max - var_G) / 6.0f) + (del_Max / 2.0f)) / del_Max; + F32 del_B = (((var_Max - var_B) / 6.0f) + (del_Max / 2.0f)) / del_Max; - if ( var_R >= var_Max ) + if (var_R >= var_Max) H = del_B - del_G; - else - if ( var_G >= var_Max ) - H = ( 1.0f / 3.0f ) + del_R - del_B; - else - if ( var_B >= var_Max ) - H = ( 2.0f / 3.0f ) + del_G - del_R; - - if ( H < 0.0f ) H += 1.0f; - if ( H > 1.0f ) H -= 1.0f; + else if (var_G >= var_Max) + H = (1.0f / 3.0f) + del_R - del_B; + else if (var_B >= var_Max) + H = (2.0f / 3.0f) + del_G - del_R; + + if (H < 0.0f) + H += 1.0f; + if (H > 1.0f) + H -= 1.0f; } - if (hue) *hue = H; - if (saturation) *saturation = S; - if (luminance) *luminance = L; + if (hue) + *hue = H; + if (saturation) + *saturation = S; + if (luminance) + *luminance = L; } diff --git a/indra/llmath/v3color.h b/indra/llmath/v3color.h index f7af469e66..3763fc6725 100644 --- a/indra/llmath/v3color.h +++ b/indra/llmath/v3color.h @@ -33,12 +33,12 @@ class LLVector4; #include "llerror.h" #include "llmath.h" #include "llsd.h" -#include "v3math.h" // needed for linearColor3v implemtation below +#include "v3math.h" // needed for linearColor3v implemtation below #include <string.h> // LLColor3 = |r g b| -static const U32 LENGTHOFCOLOR3 = 3; +static constexpr U32 LENGTHOFCOLOR3 = 3; class LLColor3 { @@ -50,44 +50,43 @@ public: static LLColor3 grey; public: - LLColor3(); // Initializes LLColor3 to (0, 0, 0) - LLColor3(F32 r, F32 g, F32 b); // Initializes LLColor3 to (r, g, b) - LLColor3(const F32 *vec); // Initializes LLColor3 to (vec[0]. vec[1], vec[2]) - LLColor3(const char *color_string); // html format color ie "#FFDDEE" - explicit LLColor3(const LLColor4& color4); // "explicit" to avoid automatic conversion - explicit LLColor3(const LLVector4& vector4); // "explicit" to avoid automatic conversion + LLColor3(); // Initializes LLColor3 to (0, 0, 0) + LLColor3(F32 r, F32 g, F32 b); // Initializes LLColor3 to (r, g, b) + LLColor3(const F32* vec); // Initializes LLColor3 to (vec[0]. vec[1], vec[2]) + LLColor3(const char* color_string); // html format color ie "#FFDDEE" + explicit LLColor3(const LLColor4& color4); // "explicit" to avoid automatic conversion + explicit LLColor3(const LLVector4& vector4); // "explicit" to avoid automatic conversion LLColor3(const LLSD& sd); - LLSD getValue() const { LLSD ret; - ret[0] = mV[0]; - ret[1] = mV[1]; - ret[2] = mV[2]; + ret[0] = mV[VRED]; + ret[1] = mV[VGREEN]; + ret[2] = mV[VBLUE]; return ret; } void setValue(const LLSD& sd) { - mV[0] = (F32) sd[0].asReal();; - mV[1] = (F32) sd[1].asReal();; - mV[2] = (F32) sd[2].asReal();; + mV[VRED] = (F32)sd[0].asReal(); + mV[VGREEN] = (F32)sd[1].asReal(); + mV[VBLUE] = (F32)sd[2].asReal(); } void setHSL(F32 hue, F32 saturation, F32 luminance); void calcHSL(F32* hue, F32* saturation, F32* luminance) const; - const LLColor3& setToBlack(); // Clears LLColor3 to (0, 0, 0) - const LLColor3& setToWhite(); // Zero LLColor3 to (0, 0, 0) + const LLColor3& setToBlack(); // Clears LLColor3 to (0, 0, 0) + const LLColor3& setToWhite(); // Zero LLColor3 to (0, 0, 0) - const LLColor3& setVec(F32 x, F32 y, F32 z); // deprecated - const LLColor3& setVec(const LLColor3 &vec); // deprecated - const LLColor3& setVec(const F32 *vec); // deprecated + const LLColor3& setVec(F32 x, F32 y, F32 z); // deprecated + const LLColor3& setVec(const LLColor3& vec); // deprecated + const LLColor3& setVec(const F32* vec); // deprecated - const LLColor3& set(F32 x, F32 y, F32 z); // Sets LLColor3 to (x, y, z) - const LLColor3& set(const LLColor3 &vec); // Sets LLColor3 to vec - const LLColor3& set(const F32 *vec); // Sets LLColor3 to vec + const LLColor3& set(F32 x, F32 y, F32 z); // Sets LLColor3 to (x, y, z) + const LLColor3& set(const LLColor3& vec); // Sets LLColor3 to vec + const LLColor3& set(const F32* vec); // Sets LLColor3 to vec // set from a vector of unknown type and size // may leave some data unmodified @@ -99,414 +98,390 @@ public: template<typename T> void write(std::vector<T>& v) const; - F32 magVec() const; // deprecated - F32 magVecSquared() const; // deprecated - F32 normVec(); // deprecated + F32 magVec() const; // deprecated + F32 magVecSquared() const; // deprecated + F32 normVec(); // deprecated - F32 length() const; // Returns magnitude of LLColor3 - F32 lengthSquared() const; // Returns magnitude squared of LLColor3 - F32 normalize(); // Normalizes and returns the magnitude of LLColor3 + F32 length() const; // Returns magnitude of LLColor3 + F32 lengthSquared() const; // Returns magnitude squared of LLColor3 + F32 normalize(); // Normalizes and returns the magnitude of LLColor3 - F32 brightness() const; // Returns brightness of LLColor3 + F32 brightness() const; // Returns brightness of LLColor3 - const LLColor3& operator=(const LLColor4 &a); + const LLColor3& operator=(const LLColor4& a); - LL_FORCE_INLINE LLColor3 divide(const LLColor3 &col2) + LL_FORCE_INLINE LLColor3 divide(const LLColor3& col2) const { - return LLColor3( - mV[0] / col2.mV[0], - mV[1] / col2.mV[1], - mV[2] / col2.mV[2] ); + return LLColor3(mV[VRED] / col2.mV[VRED], mV[VGREEN] / col2.mV[VGREEN], mV[VBLUE] / col2.mV[VBLUE]); } - LL_FORCE_INLINE LLColor3 color_norm() + LL_FORCE_INLINE LLColor3 color_norm() const { F32 l = length(); - return LLColor3( - mV[0] / l, - mV[1] / l, - mV[2] / l ); + return LLColor3(mV[VRED] / l, mV[VGREEN] / l, mV[VBLUE] / l); } - friend std::ostream& operator<<(std::ostream& s, const LLColor3 &a); // Print a - friend LLColor3 operator+(const LLColor3 &a, const LLColor3 &b); // Return vector a + b - friend LLColor3 operator-(const LLColor3 &a, const LLColor3 &b); // Return vector a minus b + friend std::ostream& operator<<(std::ostream& s, const LLColor3& a); // Print a + friend LLColor3 operator+(const LLColor3& a, const LLColor3& b); // Return vector a + b + friend LLColor3 operator-(const LLColor3& a, const LLColor3& b); // Return vector a minus b - friend const LLColor3& operator+=(LLColor3 &a, const LLColor3 &b); // Return vector a + b - friend const LLColor3& operator-=(LLColor3 &a, const LLColor3 &b); // Return vector a minus b - friend const LLColor3& operator*=(LLColor3 &a, const LLColor3 &b); + friend const LLColor3& operator+=(LLColor3& a, const LLColor3& b); // Return vector a + b + friend const LLColor3& operator-=(LLColor3& a, const LLColor3& b); // Return vector a minus b + friend const LLColor3& operator*=(LLColor3& a, const LLColor3& b); - friend LLColor3 operator*(const LLColor3 &a, const LLColor3 &b); // Return component wise a * b - friend LLColor3 operator*(const LLColor3 &a, F32 k); // Return a times scaler k - friend LLColor3 operator*(F32 k, const LLColor3 &a); // Return a times scaler k + friend LLColor3 operator*(const LLColor3& a, const LLColor3& b); // Return component wise a * b + friend LLColor3 operator*(const LLColor3& a, F32 k); // Return a times scaler k + friend LLColor3 operator*(F32 k, const LLColor3& a); // Return a times scaler k - friend bool operator==(const LLColor3 &a, const LLColor3 &b); // Return a == b - friend bool operator!=(const LLColor3 &a, const LLColor3 &b); // Return a != b + friend bool operator==(const LLColor3& a, const LLColor3& b); // Return a == b + friend bool operator!=(const LLColor3& a, const LLColor3& b); // Return a != b - friend const LLColor3& operator*=(LLColor3 &a, F32 k); // Return a times scaler k + friend const LLColor3& operator*=(LLColor3& a, F32 k); // Return a times scaler k - friend LLColor3 operator-(const LLColor3 &a); // Return vector 1-rgb (inverse) + friend LLColor3 operator-(const LLColor3& a); // Return vector 1-rgb (inverse) inline void clamp(); - inline void exp(); // Do an exponential on the color + inline void exp(); // Do an exponential on the color }; -LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u); - +LLColor3 lerp(const LLColor3& a, const LLColor3& b, F32 u); void LLColor3::clamp() { // Clamp the color... - if (mV[0] < 0.f) + if (mV[VRED] < 0.f) { - mV[0] = 0.f; + mV[VRED] = 0.f; } - else if (mV[0] > 1.f) + else if (mV[VRED] > 1.f) { - mV[0] = 1.f; + mV[VRED] = 1.f; } - if (mV[1] < 0.f) + if (mV[VGREEN] < 0.f) { - mV[1] = 0.f; + mV[VGREEN] = 0.f; } - else if (mV[1] > 1.f) + else if (mV[VGREEN] > 1.f) { - mV[1] = 1.f; + mV[VGREEN] = 1.f; } - if (mV[2] < 0.f) + if (mV[VBLUE] < 0.f) { - mV[2] = 0.f; + mV[VBLUE] = 0.f; } - else if (mV[2] > 1.f) + else if (mV[VBLUE] > 1.f) { - mV[2] = 1.f; + mV[VBLUE] = 1.f; } } // Non-member functions -F32 distVec(const LLColor3 &a, const LLColor3 &b); // Returns distance between a and b -F32 distVec_squared(const LLColor3 &a, const LLColor3 &b);// Returns distance squared between a and b +F32 distVec(const LLColor3& a, const LLColor3& b); // Returns distance between a and b +F32 distVec_squared(const LLColor3& a, const LLColor3& b); // Returns distance squared between a and b -inline LLColor3::LLColor3(void) +inline LLColor3::LLColor3() { - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; + mV[VRED] = 0.f; + mV[VGREEN] = 0.f; + mV[VBLUE] = 0.f; } inline LLColor3::LLColor3(F32 r, F32 g, F32 b) { - mV[VRED] = r; + mV[VRED] = r; mV[VGREEN] = g; - mV[VBLUE] = b; + mV[VBLUE] = b; } - -inline LLColor3::LLColor3(const F32 *vec) +inline LLColor3::LLColor3(const F32* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; } inline LLColor3::LLColor3(const char* color_string) // takes a string of format "RRGGBB" where RR is hex 00..FF { - if (strlen(color_string) < 6) /* Flawfinder: ignore */ + if (strlen(color_string) < 6) /* Flawfinder: ignore */ { - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; + mV[VRED] = 0.f; + mV[VGREEN] = 0.f; + mV[VBLUE] = 0.f; return; } char tempstr[7]; - strncpy(tempstr,color_string,6); /* Flawfinder: ignore */ + strncpy(tempstr, color_string, 6); /* Flawfinder: ignore */ tempstr[6] = '\0'; - mV[VBLUE] = (F32)strtol(&tempstr[4],NULL,16)/255.f; + mV[VBLUE] = (F32)strtol(&tempstr[4], nullptr, 16) / 255.f; tempstr[4] = '\0'; - mV[VGREEN] = (F32)strtol(&tempstr[2],NULL,16)/255.f; + mV[VGREEN] = (F32)strtol(&tempstr[2], nullptr, 16) / 255.f; tempstr[2] = '\0'; - mV[VRED] = (F32)strtol(&tempstr[0],NULL,16)/255.f; + mV[VRED] = (F32)strtol(&tempstr[0], nullptr, 16) / 255.f; } -inline const LLColor3& LLColor3::setToBlack(void) +inline const LLColor3& LLColor3::setToBlack() { - mV[0] = 0.f; - mV[1] = 0.f; - mV[2] = 0.f; + mV[VRED] = 0.f; + mV[VGREEN] = 0.f; + mV[VBLUE] = 0.f; return (*this); } -inline const LLColor3& LLColor3::setToWhite(void) +inline const LLColor3& LLColor3::setToWhite() { - mV[0] = 1.f; - mV[1] = 1.f; - mV[2] = 1.f; + mV[VRED] = 1.f; + mV[VGREEN] = 1.f; + mV[VBLUE] = 1.f; return (*this); } -inline const LLColor3& LLColor3::set(F32 r, F32 g, F32 b) +inline const LLColor3& LLColor3::set(F32 r, F32 g, F32 b) { - mV[0] = r; - mV[1] = g; - mV[2] = b; + mV[VRED] = r; + mV[VGREEN] = g; + mV[VBLUE] = b; return (*this); } -inline const LLColor3& LLColor3::set(const LLColor3 &vec) +inline const LLColor3& LLColor3::set(const LLColor3& vec) { - mV[0] = vec.mV[0]; - mV[1] = vec.mV[1]; - mV[2] = vec.mV[2]; + mV[VRED] = vec.mV[VRED]; + mV[VGREEN] = vec.mV[VGREEN]; + mV[VBLUE] = vec.mV[VBLUE]; return (*this); } -inline const LLColor3& LLColor3::set(const F32 *vec) +inline const LLColor3& LLColor3::set(const F32* vec) { - mV[0] = vec[0]; - mV[1] = vec[1]; - mV[2] = vec[2]; + mV[VRED] = vec[0]; + mV[VGREEN] = vec[1]; + mV[VBLUE] = vec[2]; return (*this); } // deprecated -inline const LLColor3& LLColor3::setVec(F32 r, F32 g, F32 b) +inline const LLColor3& LLColor3::setVec(F32 r, F32 g, F32 b) { - mV[0] = r; - mV[1] = g; - mV[2] = b; + mV[VRED] = r; + mV[VGREEN] = g; + mV[VBLUE] = b; return (*this); } // deprecated -inline const LLColor3& LLColor3::setVec(const LLColor3 &vec) +inline const LLColor3& LLColor3::setVec(const LLColor3& vec) { - mV[0] = vec.mV[0]; - mV[1] = vec.mV[1]; - mV[2] = vec.mV[2]; + mV[VRED] = vec.mV[VRED]; + mV[VGREEN] = vec.mV[VGREEN]; + mV[VBLUE] = vec.mV[VBLUE]; return (*this); } // deprecated -inline const LLColor3& LLColor3::setVec(const F32 *vec) +inline const LLColor3& LLColor3::setVec(const F32* vec) { - mV[0] = vec[0]; - mV[1] = vec[1]; - mV[2] = vec[2]; + mV[VRED] = vec[0]; + mV[VGREEN] = vec[1]; + mV[VBLUE] = vec[2]; return (*this); } -inline F32 LLColor3::brightness(void) const +inline F32 LLColor3::brightness() const { - return (mV[0] + mV[1] + mV[2]) / 3.0f; + return (mV[VRED] + mV[VGREEN] + mV[VBLUE]) / 3.0f; } -inline F32 LLColor3::length(void) const +inline F32 LLColor3::length() const { - return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + return sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); } -inline F32 LLColor3::lengthSquared(void) const +inline F32 LLColor3::lengthSquared() const { - return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]; + return mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]; } -inline F32 LLColor3::normalize(void) +inline F32 LLColor3::normalize() { - F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + F32 mag = sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); F32 oomag; if (mag) { - oomag = 1.f/mag; - mV[0] *= oomag; - mV[1] *= oomag; - mV[2] *= oomag; + oomag = 1.f / mag; + mV[VRED] *= oomag; + mV[VGREEN] *= oomag; + mV[VBLUE] *= oomag; } - return (mag); + return mag; } // deprecated -inline F32 LLColor3::magVec(void) const +inline F32 LLColor3::magVec() const { - return (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + return sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); } // deprecated -inline F32 LLColor3::magVecSquared(void) const +inline F32 LLColor3::magVecSquared() const { - return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]; + return mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]; } // deprecated -inline F32 LLColor3::normVec(void) +inline F32 LLColor3::normVec() { - F32 mag = (F32) sqrt(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]); + F32 mag = sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); F32 oomag; if (mag) { - oomag = 1.f/mag; - mV[0] *= oomag; - mV[1] *= oomag; - mV[2] *= oomag; + oomag = 1.f / mag; + mV[VRED] *= oomag; + mV[VGREEN] *= oomag; + mV[VBLUE] *= oomag; } - return (mag); + return mag; } inline void LLColor3::exp() { #if 0 - mV[0] = ::exp(mV[0]); - mV[1] = ::exp(mV[1]); - mV[2] = ::exp(mV[2]); + mV[VRED] = ::exp(mV[VRED]); + mV[VGREEN] = ::exp(mV[VGREEN]); + mV[VBLUE] = ::exp(mV[VBLUE]); #else - mV[0] = (F32)LL_FAST_EXP(mV[0]); - mV[1] = (F32)LL_FAST_EXP(mV[1]); - mV[2] = (F32)LL_FAST_EXP(mV[2]); + mV[VRED] = (F32)LL_FAST_EXP(mV[VRED]); + mV[VGREEN] = (F32)LL_FAST_EXP(mV[VGREEN]); + mV[VBLUE] = (F32)LL_FAST_EXP(mV[VBLUE]); #endif } - -inline LLColor3 operator+(const LLColor3 &a, const LLColor3 &b) +inline LLColor3 operator+(const LLColor3& a, const LLColor3& b) { - return LLColor3( - a.mV[0] + b.mV[0], - a.mV[1] + b.mV[1], - a.mV[2] + b.mV[2]); + return LLColor3(a.mV[VRED] + b.mV[VRED], a.mV[VGREEN] + b.mV[VGREEN], a.mV[VBLUE] + b.mV[VBLUE]); } -inline LLColor3 operator-(const LLColor3 &a, const LLColor3 &b) +inline LLColor3 operator-(const LLColor3& a, const LLColor3& b) { - return LLColor3( - a.mV[0] - b.mV[0], - a.mV[1] - b.mV[1], - a.mV[2] - b.mV[2]); + return LLColor3(a.mV[VRED] - b.mV[VRED], a.mV[VGREEN] - b.mV[VGREEN], a.mV[VBLUE] - b.mV[VBLUE]); } -inline LLColor3 operator*(const LLColor3 &a, const LLColor3 &b) +inline LLColor3 operator*(const LLColor3& a, const LLColor3& b) { - return LLColor3( - a.mV[0] * b.mV[0], - a.mV[1] * b.mV[1], - a.mV[2] * b.mV[2]); + return LLColor3(a.mV[VRED] * b.mV[VRED], a.mV[VGREEN] * b.mV[VGREEN], a.mV[VBLUE] * b.mV[VBLUE]); } -inline LLColor3 operator*(const LLColor3 &a, F32 k) +inline LLColor3 operator*(const LLColor3& a, F32 k) { - return LLColor3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k ); + return LLColor3(a.mV[VRED] * k, a.mV[VGREEN] * k, a.mV[VBLUE] * k); } -inline LLColor3 operator*(F32 k, const LLColor3 &a) +inline LLColor3 operator*(F32 k, const LLColor3& a) { - return LLColor3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k ); + return LLColor3(a.mV[VRED] * k, a.mV[VGREEN] * k, a.mV[VBLUE] * k); } -inline bool operator==(const LLColor3 &a, const LLColor3 &b) +inline bool operator==(const LLColor3& a, const LLColor3& b) { - return ( (a.mV[0] == b.mV[0]) - &&(a.mV[1] == b.mV[1]) - &&(a.mV[2] == b.mV[2])); + return ((a.mV[VRED] == b.mV[VRED]) && (a.mV[VGREEN] == b.mV[VGREEN]) && (a.mV[VBLUE] == b.mV[VBLUE])); } -inline bool operator!=(const LLColor3 &a, const LLColor3 &b) +inline bool operator!=(const LLColor3& a, const LLColor3& b) { - return ( (a.mV[0] != b.mV[0]) - ||(a.mV[1] != b.mV[1]) - ||(a.mV[2] != b.mV[2])); + return ((a.mV[VRED] != b.mV[VRED]) || (a.mV[VGREEN] != b.mV[VGREEN]) || (a.mV[VBLUE] != b.mV[VBLUE])); } -inline const LLColor3 &operator*=(LLColor3 &a, const LLColor3 &b) +inline const LLColor3& operator*=(LLColor3& a, const LLColor3& b) { - a.mV[0] *= b.mV[0]; - a.mV[1] *= b.mV[1]; - a.mV[2] *= b.mV[2]; + a.mV[VRED] *= b.mV[VRED]; + a.mV[VGREEN] *= b.mV[VGREEN]; + a.mV[VBLUE] *= b.mV[VBLUE]; return a; } -inline const LLColor3& operator+=(LLColor3 &a, const LLColor3 &b) +inline const LLColor3& operator+=(LLColor3& a, const LLColor3& b) { - a.mV[0] += b.mV[0]; - a.mV[1] += b.mV[1]; - a.mV[2] += b.mV[2]; + a.mV[VRED] += b.mV[VRED]; + a.mV[VGREEN] += b.mV[VGREEN]; + a.mV[VBLUE] += b.mV[VBLUE]; return a; } -inline const LLColor3& operator-=(LLColor3 &a, const LLColor3 &b) +inline const LLColor3& operator-=(LLColor3& a, const LLColor3& b) { - a.mV[0] -= b.mV[0]; - a.mV[1] -= b.mV[1]; - a.mV[2] -= b.mV[2]; + a.mV[VRED] -= b.mV[VRED]; + a.mV[VGREEN] -= b.mV[VGREEN]; + a.mV[VBLUE] -= b.mV[VBLUE]; return a; } -inline const LLColor3& operator*=(LLColor3 &a, F32 k) +inline const LLColor3& operator*=(LLColor3& a, F32 k) { - a.mV[0] *= k; - a.mV[1] *= k; - a.mV[2] *= k; + a.mV[VRED] *= k; + a.mV[VGREEN] *= k; + a.mV[VBLUE] *= k; return a; } -inline LLColor3 operator-(const LLColor3 &a) +inline LLColor3 operator-(const LLColor3& a) { - return LLColor3( - 1.f - a.mV[0], - 1.f - a.mV[1], - 1.f - a.mV[2] ); + return LLColor3(1.f - a.mV[VRED], 1.f - a.mV[VGREEN], 1.f - a.mV[VBLUE]); } // Non-member functions -inline F32 distVec(const LLColor3 &a, const LLColor3 &b) +inline F32 distVec(const LLColor3& a, const LLColor3& b) { - F32 x = a.mV[0] - b.mV[0]; - F32 y = a.mV[1] - b.mV[1]; - F32 z = a.mV[2] - b.mV[2]; - return (F32) sqrt( x*x + y*y + z*z ); + F32 x = a.mV[VRED] - b.mV[VRED]; + F32 y = a.mV[VGREEN] - b.mV[VGREEN]; + F32 z = a.mV[VBLUE] - b.mV[VBLUE]; + return sqrt(x * x + y * y + z * z); } -inline F32 distVec_squared(const LLColor3 &a, const LLColor3 &b) +inline F32 distVec_squared(const LLColor3& a, const LLColor3& b) { - F32 x = a.mV[0] - b.mV[0]; - F32 y = a.mV[1] - b.mV[1]; - F32 z = a.mV[2] - b.mV[2]; - return x*x + y*y + z*z; + F32 x = a.mV[VRED] - b.mV[VRED]; + F32 y = a.mV[VGREEN] - b.mV[VGREEN]; + F32 z = a.mV[VBLUE] - b.mV[VBLUE]; + return x * x + y * y + z * z; } -inline LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u) +inline LLColor3 lerp(const LLColor3& a, const LLColor3& b, F32 u) { - return LLColor3( - a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u, - a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u, - a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u); + return LLColor3(a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u, a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u, a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u); } -inline const LLColor3 srgbColor3(const LLColor3 &a) { +inline const LLColor3 srgbColor3(const LLColor3& a) +{ LLColor3 srgbColor; - srgbColor.mV[0] = linearTosRGB(a.mV[0]); - srgbColor.mV[1] = linearTosRGB(a.mV[1]); - srgbColor.mV[2] = linearTosRGB(a.mV[2]); + srgbColor.mV[VRED] = linearTosRGB(a.mV[VRED]); + srgbColor.mV[VGREEN] = linearTosRGB(a.mV[VGREEN]); + srgbColor.mV[VBLUE] = linearTosRGB(a.mV[VBLUE]); return srgbColor; } -inline const LLColor3 linearColor3p(const F32* v) { +inline const LLColor3 linearColor3p(const F32* v) +{ LLColor3 linearColor; - linearColor.mV[0] = sRGBtoLinear(v[0]); - linearColor.mV[1] = sRGBtoLinear(v[1]); - linearColor.mV[2] = sRGBtoLinear(v[2]); + linearColor.mV[VRED] = sRGBtoLinear(v[0]); + linearColor.mV[VGREEN] = sRGBtoLinear(v[1]); + linearColor.mV[VBLUE] = sRGBtoLinear(v[2]); return linearColor; } template<class T> -inline const LLColor3 linearColor3(const T& a) { +inline const LLColor3 linearColor3(const T& a) +{ return linearColor3p(a.mV); } template<class T> -inline const LLVector3 linearColor3v(const T& a) { +inline const LLVector3 linearColor3v(const T& a) +{ return LLVector3(linearColor3p(a.mV).mV); } diff --git a/indra/llmath/v3colorutil.h b/indra/llmath/v3colorutil.h index 62005f76a0..1378d46450 100644 --- a/indra/llmath/v3colorutil.h +++ b/indra/llmath/v3colorutil.h @@ -29,59 +29,46 @@ #include "v3color.h" -inline LLColor3 componentDiv(LLColor3 const &left, LLColor3 const & right) +inline LLColor3 componentDiv(const LLColor3& left, const LLColor3& right) { - return LLColor3(left.mV[0] / right.mV[0], - left.mV[1] / right.mV[1], - left.mV[2] / right.mV[2]); + return LLColor3(left.mV[VRED] / right.mV[VRED], left.mV[VGREEN] / right.mV[VGREEN], left.mV[VBLUE] / right.mV[VBLUE]); } - -inline LLColor3 componentMult(LLColor3 const &left, LLColor3 const & right) +inline LLColor3 componentMult(const LLColor3& left, const LLColor3& right) { - return LLColor3(left.mV[0] * right.mV[0], - left.mV[1] * right.mV[1], - left.mV[2] * right.mV[2]); + return LLColor3(left.mV[VRED] * right.mV[VRED], left.mV[VGREEN] * right.mV[VGREEN], left.mV[VBLUE] * right.mV[VBLUE]); } - -inline LLColor3 componentExp(LLColor3 const &v) +inline LLColor3 componentExp(const LLColor3& v) { - return LLColor3(exp(v.mV[0]), - exp(v.mV[1]), - exp(v.mV[2])); + return LLColor3(exp(v.mV[VRED]), exp(v.mV[VGREEN]), exp(v.mV[VBLUE])); } -inline LLColor3 componentPow(LLColor3 const &v, F32 exponent) +inline LLColor3 componentPow(const LLColor3& v, F32 exponent) { - return LLColor3(pow(v.mV[0], exponent), - pow(v.mV[1], exponent), - pow(v.mV[2], exponent)); + return LLColor3(pow(v.mV[VRED], exponent), pow(v.mV[VGREEN], exponent), pow(v.mV[VBLUE], exponent)); } -inline LLColor3 componentSaturate(LLColor3 const &v) +inline LLColor3 componentSaturate(const LLColor3& v) { - return LLColor3(std::max(std::min(v.mV[0], 1.f), 0.f), - std::max(std::min(v.mV[1], 1.f), 0.f), - std::max(std::min(v.mV[2], 1.f), 0.f)); + return LLColor3(std::max(std::min(v.mV[VRED], 1.f), 0.f), + std::max(std::min(v.mV[VGREEN], 1.f), 0.f), + std::max(std::min(v.mV[VBLUE], 1.f), 0.f)); } - -inline LLColor3 componentSqrt(LLColor3 const &v) +inline LLColor3 componentSqrt(const LLColor3& v) { - return LLColor3(sqrt(v.mV[0]), - sqrt(v.mV[1]), - sqrt(v.mV[2])); + return LLColor3(sqrt(v.mV[VRED]), sqrt(v.mV[VGREEN]), sqrt(v.mV[VBLUE])); } -inline void componentMultBy(LLColor3 & left, LLColor3 const & right) +inline void componentMultBy(LLColor3& left, const LLColor3& right) { - left.mV[0] *= right.mV[0]; - left.mV[1] *= right.mV[1]; - left.mV[2] *= right.mV[2]; + left.mV[VRED] *= right.mV[VRED]; + left.mV[VGREEN] *= right.mV[VGREEN]; + left.mV[VBLUE] *= right.mV[VBLUE]; } -inline LLColor3 colorMix(LLColor3 const & left, LLColor3 const & right, F32 amount) +inline LLColor3 colorMix(const LLColor3& left, const LLColor3& right, F32 amount) { return (left + ((right - left) * amount)); } @@ -91,25 +78,24 @@ inline LLColor3 smear(F32 val) return LLColor3(val, val, val); } -inline F32 color_intens(const LLColor3 &col) +inline F32 color_intens(const LLColor3& col) { - return col.mV[0] + col.mV[1] + col.mV[2]; + return col.mV[VRED] + col.mV[VGREEN] + col.mV[VBLUE]; } -inline F32 color_max(const LLColor3 &col) +inline F32 color_max(const LLColor3& col) { - return llmax(col.mV[0], col.mV[1], col.mV[2]); + return llmax(col.mV[VRED], col.mV[VGREEN], col.mV[VBLUE]); } -inline F32 color_max(const LLColor4 &col) +inline F32 color_max(const LLColor4& col) { - return llmax(col.mV[0], col.mV[1], col.mV[2]); + return llmax(col.mV[VRED], col.mV[VGREEN], col.mV[VBLUE]); } - -inline F32 color_min(const LLColor3 &col) +inline F32 color_min(const LLColor3& col) { - return llmin(col.mV[0], col.mV[1], col.mV[2]); + return llmin(col.mV[VRED], col.mV[VGREEN], col.mV[VBLUE]); } #endif diff --git a/indra/llmath/v3dmath.cpp b/indra/llmath/v3dmath.cpp index bd6d838186..a2d1b03622 100644 --- a/indra/llmath/v3dmath.cpp +++ b/indra/llmath/v3dmath.cpp @@ -30,7 +30,6 @@ #include "v3dmath.h" -//#include "vmath.h" #include "v4math.h" #include "m4math.h" #include "m3math.h" diff --git a/indra/llmath/v3math.cpp b/indra/llmath/v3math.cpp index 941a122528..bd96417190 100644 --- a/indra/llmath/v3math.cpp +++ b/indra/llmath/v3math.cpp @@ -28,7 +28,6 @@ #include "v3math.h" -//#include "vmath.h" #include "v2math.h" #include "v4math.h" #include "m4math.h" diff --git a/indra/llmath/v4color.cpp b/indra/llmath/v4color.cpp index ad13656bbd..0fdf2883b2 100644 --- a/indra/llmath/v4color.cpp +++ b/indra/llmath/v4color.cpp @@ -38,6 +38,7 @@ ////////////////////////////////////////////////////////////////////////////// +// clang-format off LLColor4 LLColor4::red( 1.f, 0.f, 0.f, 1.f); LLColor4 LLColor4::green( 0.f, 1.f, 0.f, 1.f); LLColor4 LLColor4::blue( 0.f, 0.f, 1.f, 1.f); @@ -52,6 +53,7 @@ LLColor4 LLColor4::orange( 1.f, 0.5, 0.f, 1.f ); LLColor4 LLColor4::purple( 0.6f, 0.2f, 0.8f, 1.0f); LLColor4 LLColor4::pink( 1.0f, 0.5f, 0.8f, 1.0f); LLColor4 LLColor4::transparent( 0.f, 0.f, 0.f, 0.f ); +// clang-format on ////////////////////////////////////////////////////////////////////////////// @@ -124,65 +126,64 @@ LLColor4 LLColor4::cyan6(0.2f, 0.6f, 0.6f, 1.0f); // conversion LLColor4::operator LLColor4U() const { - return LLColor4U( - (U8)llclampb(ll_round(mV[VRED]*255.f)), - (U8)llclampb(ll_round(mV[VGREEN]*255.f)), - (U8)llclampb(ll_round(mV[VBLUE]*255.f)), - (U8)llclampb(ll_round(mV[VALPHA]*255.f))); + return LLColor4U((U8)llclampb(ll_round(mV[VRED] * 255.f)), + (U8)llclampb(ll_round(mV[VGREEN] * 255.f)), + (U8)llclampb(ll_round(mV[VBLUE] * 255.f)), + (U8)llclampb(ll_round(mV[VALPHA] * 255.f))); } -LLColor4::LLColor4(const LLColor3 &vec, F32 a) +LLColor4::LLColor4(const LLColor3& vec, F32 a) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = a; } LLColor4::LLColor4(const LLColor4U& color4u) { - const F32 SCALE = 1.f/255.f; - mV[VRED] = color4u.mV[VRED] * SCALE; - mV[VGREEN] = color4u.mV[VGREEN] * SCALE; - mV[VBLUE] = color4u.mV[VBLUE] * SCALE; - mV[VALPHA] = color4u.mV[VALPHA] * SCALE; + constexpr F32 SCALE = 1.f / 255.f; + mV[VRED] = color4u.mV[VRED] * SCALE; + mV[VGREEN] = color4u.mV[VGREEN] * SCALE; + mV[VBLUE] = color4u.mV[VBLUE] * SCALE; + mV[VALPHA] = color4u.mV[VALPHA] * SCALE; } LLColor4::LLColor4(const LLVector4& vector4) { - mV[VRED] = vector4.mV[VRED]; + mV[VRED] = vector4.mV[VRED]; mV[VGREEN] = vector4.mV[VGREEN]; - mV[VBLUE] = vector4.mV[VBLUE]; + mV[VBLUE] = vector4.mV[VBLUE]; mV[VALPHA] = vector4.mV[VALPHA]; } const LLColor4& LLColor4::set(const LLColor4U& color4u) { - const F32 SCALE = 1.f/255.f; - mV[VRED] = color4u.mV[VRED] * SCALE; - mV[VGREEN] = color4u.mV[VGREEN] * SCALE; - mV[VBLUE] = color4u.mV[VBLUE] * SCALE; - mV[VALPHA] = color4u.mV[VALPHA] * SCALE; + constexpr F32 SCALE = 1.f / 255.f; + mV[VRED] = color4u.mV[VRED] * SCALE; + mV[VGREEN] = color4u.mV[VGREEN] * SCALE; + mV[VBLUE] = color4u.mV[VBLUE] * SCALE; + mV[VALPHA] = color4u.mV[VALPHA] * SCALE; return (*this); } -const LLColor4& LLColor4::set(const LLColor3 &vec) +const LLColor4& LLColor4::set(const LLColor3& vec) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; -// no change to alpha! -// mV[VALPHA] = 1.f; + // no change to alpha! + // mV[VALPHA] = 1.f; return (*this); } -const LLColor4& LLColor4::set(const LLColor3 &vec, F32 a) +const LLColor4& LLColor4::set(const LLColor3& vec, F32 a) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = a; return (*this); } @@ -190,33 +191,33 @@ const LLColor4& LLColor4::set(const LLColor3 &vec, F32 a) // deprecated -- use set() const LLColor4& LLColor4::setVec(const LLColor4U& color4u) { - const F32 SCALE = 1.f/255.f; - mV[VRED] = color4u.mV[VRED] * SCALE; - mV[VGREEN] = color4u.mV[VGREEN] * SCALE; - mV[VBLUE] = color4u.mV[VBLUE] * SCALE; - mV[VALPHA] = color4u.mV[VALPHA] * SCALE; + constexpr F32 SCALE = 1.f / 255.f; + mV[VRED] = color4u.mV[VRED] * SCALE; + mV[VGREEN] = color4u.mV[VGREEN] * SCALE; + mV[VBLUE] = color4u.mV[VBLUE] * SCALE; + mV[VALPHA] = color4u.mV[VALPHA] * SCALE; return (*this); } // deprecated -- use set() -const LLColor4& LLColor4::setVec(const LLColor3 &vec) +const LLColor4& LLColor4::setVec(const LLColor3& vec) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; -// no change to alpha! -// mV[VALPHA] = 1.f; + // no change to alpha! + // mV[VALPHA] = 1.f; return (*this); } // deprecated -- use set() -const LLColor4& LLColor4::setVec(const LLColor3 &vec, F32 a) +const LLColor4& LLColor4::setVec(const LLColor3& vec, F32 a) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = a; return (*this); } @@ -228,110 +229,110 @@ void LLColor4::setValue(const LLSD& sd) F32 val; bool out_of_range = false; val = sd[0].asReal(); - mV[0] = llclamp(val, 0.f, 1.f); - out_of_range = mV[0] != val; + mV[VRED] = llclamp(val, 0.f, 1.f); + out_of_range = mV[VRED] != val; val = sd[1].asReal(); - mV[1] = llclamp(val, 0.f, 1.f); - out_of_range |= mV[1] != val; + mV[VGREEN] = llclamp(val, 0.f, 1.f); + out_of_range |= mV[VGREEN] != val; val = sd[2].asReal(); - mV[2] = llclamp(val, 0.f, 1.f); - out_of_range |= mV[2] != val; + mV[VBLUE] = llclamp(val, 0.f, 1.f); + out_of_range |= mV[VBLUE] != val; val = sd[3].asReal(); - mV[3] = llclamp(val, 0.f, 1.f); - out_of_range |= mV[3] != val; + mV[VALPHA] = llclamp(val, 0.f, 1.f); + out_of_range |= mV[VALPHA] != val; if (out_of_range) { LL_WARNS() << "LLSD color value out of range!" << LL_ENDL; } #else - mV[0] = (F32) sd[0].asReal(); - mV[1] = (F32) sd[1].asReal(); - mV[2] = (F32) sd[2].asReal(); - mV[3] = (F32) sd[3].asReal(); + mV[VRED] = (F32)sd[0].asReal(); + mV[VGREEN] = (F32)sd[1].asReal(); + mV[VBLUE] = (F32)sd[2].asReal(); + mV[VALPHA] = (F32)sd[3].asReal(); #endif } -const LLColor4& LLColor4::operator=(const LLColor3 &a) +const LLColor4& LLColor4::operator=(const LLColor3& a) { - mV[VRED] = a.mV[VRED]; + mV[VRED] = a.mV[VRED]; mV[VGREEN] = a.mV[VGREEN]; - mV[VBLUE] = a.mV[VBLUE]; + mV[VBLUE] = a.mV[VBLUE]; -// converting from an rgb sets a=1 (opaque) + // converting from an rgb sets a=1 (opaque) mV[VALPHA] = 1.f; return (*this); } - -std::ostream& operator<<(std::ostream& s, const LLColor4 &a) +std::ostream& operator<<(std::ostream& s, const LLColor4& a) { s << "{ " << a.mV[VRED] << ", " << a.mV[VGREEN] << ", " << a.mV[VBLUE] << ", " << a.mV[VALPHA] << " }"; return s; } -bool operator==(const LLColor4 &a, const LLColor3 &b) +bool operator==(const LLColor4& a, const LLColor3& b) { - return ( (a.mV[VRED] == b.mV[VRED]) - &&(a.mV[VGREEN] == b.mV[VGREEN]) - &&(a.mV[VBLUE] == b.mV[VBLUE])); + return ((a.mV[VRED] == b.mV[VRED]) && (a.mV[VGREEN] == b.mV[VGREEN]) && (a.mV[VBLUE] == b.mV[VBLUE])); } -bool operator!=(const LLColor4 &a, const LLColor3 &b) +bool operator!=(const LLColor4& a, const LLColor3& b) { - return ( (a.mV[VRED] != b.mV[VRED]) - ||(a.mV[VGREEN] != b.mV[VGREEN]) - ||(a.mV[VBLUE] != b.mV[VBLUE])); + return ((a.mV[VRED] != b.mV[VRED]) || (a.mV[VGREEN] != b.mV[VGREEN]) || (a.mV[VBLUE] != b.mV[VBLUE])); } -LLColor3 vec4to3(const LLColor4 &vec) +LLColor3 vec4to3(const LLColor4& vec) { - LLColor3 temp(vec.mV[VRED], vec.mV[VGREEN], vec.mV[VBLUE]); + LLColor3 temp(vec.mV[VRED], vec.mV[VGREEN], vec.mV[VBLUE]); return temp; } -LLColor4 vec3to4(const LLColor3 &vec) +LLColor4 vec3to4(const LLColor3& vec) { - LLColor3 temp(vec.mV[VRED], vec.mV[VGREEN], vec.mV[VBLUE]); + LLColor3 temp(vec.mV[VRED], vec.mV[VGREEN], vec.mV[VBLUE]); return temp; } -static F32 hueToRgb ( F32 val1In, F32 val2In, F32 valHUeIn ) +static F32 hueToRgb(F32 val1In, F32 val2In, F32 valHUeIn) { - if ( valHUeIn < 0.0f ) valHUeIn += 1.0f; - if ( valHUeIn > 1.0f ) valHUeIn -= 1.0f; - if ( ( 6.0f * valHUeIn ) < 1.0f ) return ( val1In + ( val2In - val1In ) * 6.0f * valHUeIn ); - if ( ( 2.0f * valHUeIn ) < 1.0f ) return ( val2In ); - if ( ( 3.0f * valHUeIn ) < 2.0f ) return ( val1In + ( val2In - val1In ) * ( ( 2.0f / 3.0f ) - valHUeIn ) * 6.0f ); - return ( val1In ); + if (valHUeIn < 0.0f) + valHUeIn += 1.0f; + if (valHUeIn > 1.0f) + valHUeIn -= 1.0f; + if ((6.0f * valHUeIn) < 1.0f) + return (val1In + (val2In - val1In) * 6.0f * valHUeIn); + if ((2.0f * valHUeIn) < 1.0f) + return (val2In); + if ((3.0f * valHUeIn) < 2.0f) + return (val1In + (val2In - val1In) * ((2.0f / 3.0f) - valHUeIn) * 6.0f); + return (val1In); } -void LLColor4::setHSL ( F32 hValIn, F32 sValIn, F32 lValIn) +void LLColor4::setHSL(F32 hValIn, F32 sValIn, F32 lValIn) { - if ( sValIn < 0.00001f ) + if (sValIn < 0.00001f) { - mV[VRED] = lValIn; + mV[VRED] = lValIn; mV[VGREEN] = lValIn; - mV[VBLUE] = lValIn; + mV[VBLUE] = lValIn; } else { F32 interVal1; F32 interVal2; - if ( lValIn < 0.5f ) - interVal2 = lValIn * ( 1.0f + sValIn ); + if (lValIn < 0.5f) + interVal2 = lValIn * (1.0f + sValIn); else - interVal2 = ( lValIn + sValIn ) - ( sValIn * lValIn ); + interVal2 = (lValIn + sValIn) - (sValIn * lValIn); interVal1 = 2.0f * lValIn - interVal2; - mV[VRED] = hueToRgb ( interVal1, interVal2, hValIn + ( 1.f / 3.f ) ); - mV[VGREEN] = hueToRgb ( interVal1, interVal2, hValIn ); - mV[VBLUE] = hueToRgb ( interVal1, interVal2, hValIn - ( 1.f / 3.f ) ); + mV[VRED] = hueToRgb(interVal1, interVal2, hValIn + (1.f / 3.f)); + mV[VGREEN] = hueToRgb(interVal1, interVal2, hValIn); + mV[VBLUE] = hueToRgb(interVal1, interVal2, hValIn - (1.f / 3.f)); } } @@ -341,58 +342,61 @@ void LLColor4::calcHSL(F32* hue, F32* saturation, F32* luminance) const F32 var_G = mV[VGREEN]; F32 var_B = mV[VBLUE]; - F32 var_Min = ( var_R < ( var_G < var_B ? var_G : var_B ) ? var_R : ( var_G < var_B ? var_G : var_B ) ); - F32 var_Max = ( var_R > ( var_G > var_B ? var_G : var_B ) ? var_R : ( var_G > var_B ? var_G : var_B ) ); + F32 var_Min = (var_R < (var_G < var_B ? var_G : var_B) ? var_R : (var_G < var_B ? var_G : var_B)); + F32 var_Max = (var_R > (var_G > var_B ? var_G : var_B) ? var_R : (var_G > var_B ? var_G : var_B)); F32 del_Max = var_Max - var_Min; - F32 L = ( var_Max + var_Min ) / 2.0f; + F32 L = (var_Max + var_Min) / 2.0f; F32 H = 0.0f; F32 S = 0.0f; - if ( del_Max == 0.0f ) + if (del_Max == 0.0f) { - H = 0.0f; - S = 0.0f; + H = 0.0f; + S = 0.0f; } else { - if ( L < 0.5 ) - S = del_Max / ( var_Max + var_Min ); + if (L < 0.5) + S = del_Max / (var_Max + var_Min); else - S = del_Max / ( 2.0f - var_Max - var_Min ); + S = del_Max / (2.0f - var_Max - var_Min); - F32 del_R = ( ( ( var_Max - var_R ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; - F32 del_G = ( ( ( var_Max - var_G ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; - F32 del_B = ( ( ( var_Max - var_B ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max; + F32 del_R = (((var_Max - var_R) / 6.0f) + (del_Max / 2.0f)) / del_Max; + F32 del_G = (((var_Max - var_G) / 6.0f) + (del_Max / 2.0f)) / del_Max; + F32 del_B = (((var_Max - var_B) / 6.0f) + (del_Max / 2.0f)) / del_Max; - if ( var_R >= var_Max ) + if (var_R >= var_Max) H = del_B - del_G; - else - if ( var_G >= var_Max ) - H = ( 1.0f / 3.0f ) + del_R - del_B; - else - if ( var_B >= var_Max ) - H = ( 2.0f / 3.0f ) + del_G - del_R; - - if ( H < 0.0f ) H += 1.0f; - if ( H > 1.0f ) H -= 1.0f; + else if (var_G >= var_Max) + H = (1.0f / 3.0f) + del_R - del_B; + else if (var_B >= var_Max) + H = (2.0f / 3.0f) + del_G - del_R; + + if (H < 0.0f) + H += 1.0f; + if (H > 1.0f) + H -= 1.0f; } - if (hue) *hue = H; - if (saturation) *saturation = S; - if (luminance) *luminance = L; + if (hue) + *hue = H; + if (saturation) + *saturation = S; + if (luminance) + *luminance = L; } // static bool LLColor4::parseColor(const std::string& buf, LLColor4* color) { - if( buf.empty() || color == nullptr) + if (buf.empty() || color == nullptr) { return false; } - boost_tokenizer tokens(buf, boost::char_separator<char>(", ")); + boost_tokenizer tokens(buf, boost::char_separator<char>(", ")); boost_tokenizer::iterator token_iter = tokens.begin(); if (token_iter == tokens.end()) { @@ -401,16 +405,16 @@ bool LLColor4::parseColor(const std::string& buf, LLColor4* color) // Grab the first token into a string, since we don't know // if this is a float or a color name. - std::string color_name( (*token_iter) ); + std::string color_name((*token_iter)); ++token_iter; if (token_iter != tokens.end()) { // There are more tokens to read. This must be a vector. LLColor4 v; - LLStringUtil::convertToF32( color_name, v.mV[VRED] ); - LLStringUtil::convertToF32( *token_iter, v.mV[VGREEN] ); - v.mV[VBLUE] = 0.0f; + LLStringUtil::convertToF32(color_name, v.mV[VRED]); + LLStringUtil::convertToF32(*token_iter, v.mV[VGREEN]); + v.mV[VBLUE] = 0.0f; v.mV[VALPHA] = 1.0f; ++token_iter; @@ -422,283 +426,284 @@ bool LLColor4::parseColor(const std::string& buf, LLColor4* color) else { // There is a z-component. - LLStringUtil::convertToF32( *token_iter, v.mV[VBLUE] ); + LLStringUtil::convertToF32(*token_iter, v.mV[VBLUE]); ++token_iter; if (token_iter != tokens.end()) { // There is an alpha component. - LLStringUtil::convertToF32( *token_iter, v.mV[VALPHA] ); + LLStringUtil::convertToF32(*token_iter, v.mV[VALPHA]); } } // Make sure all values are between 0 and 1. if (v.mV[VRED] > 1.f || v.mV[VGREEN] > 1.f || v.mV[VBLUE] > 1.f || v.mV[VALPHA] > 1.f) { - v = v * (1.f / 255.f); + constexpr F32 SCALE{ 1.f / 255.f }; + v *= SCALE; } - color->set( v ); + color->set(v); } else // Single value. Read as a named color. { // We have a color name - if ( "red" == color_name ) + if ("red" == color_name) { color->set(LLColor4::red); } - else if ( "red1" == color_name ) + else if ("red1" == color_name) { color->set(LLColor4::red1); } - else if ( "red2" == color_name ) + else if ("red2" == color_name) { color->set(LLColor4::red2); } - else if ( "red3" == color_name ) + else if ("red3" == color_name) { color->set(LLColor4::red3); } - else if ( "red4" == color_name ) + else if ("red4" == color_name) { color->set(LLColor4::red4); } - else if ( "red5" == color_name ) + else if ("red5" == color_name) { color->set(LLColor4::red5); } - else if( "green" == color_name ) + else if ("green" == color_name) { color->set(LLColor4::green); } - else if( "green1" == color_name ) + else if ("green1" == color_name) { color->set(LLColor4::green1); } - else if( "green2" == color_name ) + else if ("green2" == color_name) { color->set(LLColor4::green2); } - else if( "green3" == color_name ) + else if ("green3" == color_name) { color->set(LLColor4::green3); } - else if( "green4" == color_name ) + else if ("green4" == color_name) { color->set(LLColor4::green4); } - else if( "green5" == color_name ) + else if ("green5" == color_name) { color->set(LLColor4::green5); } - else if( "green6" == color_name ) + else if ("green6" == color_name) { color->set(LLColor4::green6); } - else if( "blue" == color_name ) + else if ("blue" == color_name) { color->set(LLColor4::blue); } - else if( "blue1" == color_name ) + else if ("blue1" == color_name) { color->set(LLColor4::blue1); } - else if( "blue2" == color_name ) + else if ("blue2" == color_name) { color->set(LLColor4::blue2); } - else if( "blue3" == color_name ) + else if ("blue3" == color_name) { color->set(LLColor4::blue3); } - else if( "blue4" == color_name ) + else if ("blue4" == color_name) { color->set(LLColor4::blue4); } - else if( "blue5" == color_name ) + else if ("blue5" == color_name) { color->set(LLColor4::blue5); } - else if( "blue6" == color_name ) + else if ("blue6" == color_name) { color->set(LLColor4::blue6); } - else if( "black" == color_name ) + else if ("black" == color_name) { color->set(LLColor4::black); } - else if( "white" == color_name ) + else if ("white" == color_name) { color->set(LLColor4::white); } - else if( "yellow" == color_name ) + else if ("yellow" == color_name) { color->set(LLColor4::yellow); } - else if( "yellow1" == color_name ) + else if ("yellow1" == color_name) { color->set(LLColor4::yellow1); } - else if( "yellow2" == color_name ) + else if ("yellow2" == color_name) { color->set(LLColor4::yellow2); } - else if( "yellow3" == color_name ) + else if ("yellow3" == color_name) { color->set(LLColor4::yellow3); } - else if( "yellow4" == color_name ) + else if ("yellow4" == color_name) { color->set(LLColor4::yellow4); } - else if( "yellow5" == color_name ) + else if ("yellow5" == color_name) { color->set(LLColor4::yellow5); } - else if( "yellow6" == color_name ) + else if ("yellow6" == color_name) { color->set(LLColor4::yellow6); } - else if( "magenta" == color_name ) + else if ("magenta" == color_name) { color->set(LLColor4::magenta); } - else if( "magenta1" == color_name ) + else if ("magenta1" == color_name) { color->set(LLColor4::magenta1); } - else if( "magenta2" == color_name ) + else if ("magenta2" == color_name) { color->set(LLColor4::magenta2); } - else if( "magenta3" == color_name ) + else if ("magenta3" == color_name) { color->set(LLColor4::magenta3); } - else if( "magenta4" == color_name ) + else if ("magenta4" == color_name) { color->set(LLColor4::magenta4); } - else if( "purple" == color_name ) + else if ("purple" == color_name) { color->set(LLColor4::purple); } - else if( "purple1" == color_name ) + else if ("purple1" == color_name) { color->set(LLColor4::purple1); } - else if( "purple2" == color_name ) + else if ("purple2" == color_name) { color->set(LLColor4::purple2); } - else if( "purple3" == color_name ) + else if ("purple3" == color_name) { color->set(LLColor4::purple3); } - else if( "purple4" == color_name ) + else if ("purple4" == color_name) { color->set(LLColor4::purple4); } - else if( "purple5" == color_name ) + else if ("purple5" == color_name) { color->set(LLColor4::purple5); } - else if( "purple6" == color_name ) + else if ("purple6" == color_name) { color->set(LLColor4::purple6); } - else if( "pink" == color_name ) + else if ("pink" == color_name) { color->set(LLColor4::pink); } - else if( "pink1" == color_name ) + else if ("pink1" == color_name) { color->set(LLColor4::pink1); } - else if( "pink2" == color_name ) + else if ("pink2" == color_name) { color->set(LLColor4::pink2); } - else if( "cyan" == color_name ) + else if ("cyan" == color_name) { color->set(LLColor4::cyan); } - else if( "cyan1" == color_name ) + else if ("cyan1" == color_name) { color->set(LLColor4::cyan1); } - else if( "cyan2" == color_name ) + else if ("cyan2" == color_name) { color->set(LLColor4::cyan2); } - else if( "cyan3" == color_name ) + else if ("cyan3" == color_name) { color->set(LLColor4::cyan3); } - else if( "cyan4" == color_name ) + else if ("cyan4" == color_name) { color->set(LLColor4::cyan4); } - else if( "cyan5" == color_name ) + else if ("cyan5" == color_name) { color->set(LLColor4::cyan5); } - else if( "cyan6" == color_name ) + else if ("cyan6" == color_name) { color->set(LLColor4::cyan6); } - else if( "smoke" == color_name ) + else if ("smoke" == color_name) { color->set(LLColor4::smoke); } - else if( "grey" == color_name ) + else if ("grey" == color_name) { color->set(LLColor4::grey); } - else if( "grey1" == color_name ) + else if ("grey1" == color_name) { color->set(LLColor4::grey1); } - else if( "grey2" == color_name ) + else if ("grey2" == color_name) { color->set(LLColor4::grey2); } - else if( "grey3" == color_name ) + else if ("grey3" == color_name) { color->set(LLColor4::grey3); } - else if( "grey4" == color_name ) + else if ("grey4" == color_name) { color->set(LLColor4::grey4); } - else if( "orange" == color_name ) + else if ("orange" == color_name) { color->set(LLColor4::orange); } - else if( "orange1" == color_name ) + else if ("orange1" == color_name) { color->set(LLColor4::orange1); } - else if( "orange2" == color_name ) + else if ("orange2" == color_name) { color->set(LLColor4::orange2); } - else if( "orange3" == color_name ) + else if ("orange3" == color_name) { color->set(LLColor4::orange3); } - else if( "orange4" == color_name ) + else if ("orange4" == color_name) { color->set(LLColor4::orange4); } - else if( "orange5" == color_name ) + else if ("orange5" == color_name) { color->set(LLColor4::orange5); } - else if( "orange6" == color_name ) + else if ("orange6" == color_name) { color->set(LLColor4::orange6); } - else if ( "clear" == color_name ) + else if ("clear" == color_name) { color->set(0.f, 0.f, 0.f, 0.f); } @@ -714,21 +719,21 @@ bool LLColor4::parseColor(const std::string& buf, LLColor4* color) // static bool LLColor4::parseColor4(const std::string& buf, LLColor4* value) { - if( buf.empty() || value == nullptr) + if (buf.empty() || value == nullptr) { return false; } LLColor4 v; - S32 count = sscanf( buf.c_str(), "%f, %f, %f, %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3 ); - if (1 == count ) + S32 count = sscanf(buf.c_str(), "%f, %f, %f, %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3); + if (1 == count) { // try this format - count = sscanf( buf.c_str(), "%f %f %f %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3 ); + count = sscanf(buf.c_str(), "%f %f %f %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3); } - if( 4 == count ) + if (4 == count) { - value->setVec( v ); + value->setVec(v); return true; } diff --git a/indra/llmath/v4color.h b/indra/llmath/v4color.h index cafdbd9d7c..a26db0428b 100644 --- a/indra/llmath/v4color.h +++ b/indra/llmath/v4color.h @@ -28,7 +28,6 @@ #define LL_V4COLOR_H #include "llerror.h" -//#include "vmath.h" #include "llmath.h" #include "llsd.h" @@ -38,213 +37,212 @@ class LLVector4; // LLColor4 = |x y z w| -static const U32 LENGTHOFCOLOR4 = 4; +static constexpr U32 LENGTHOFCOLOR4 = 4; -static const U32 MAX_LENGTH_OF_COLOR_NAME = 15; //Give plenty of room for additional colors... +static constexpr U32 MAX_LENGTH_OF_COLOR_NAME = 15; // Give plenty of room for additional colors... class LLColor4 { - public: - F32 mV[LENGTHOFCOLOR4]; - LLColor4(); // Initializes LLColor4 to (0, 0, 0, 1) - LLColor4(F32 r, F32 g, F32 b); // Initializes LLColor4 to (r, g, b, 1) - LLColor4(F32 r, F32 g, F32 b, F32 a); // Initializes LLColor4 to (r. g, b, a) - LLColor4(const LLColor3 &vec, F32 a = 1.f); // Initializes LLColor4 to (vec, a) - explicit LLColor4(const LLSD& sd); - explicit LLColor4(const F32 *vec); // Initializes LLColor4 to (vec[0]. vec[1], vec[2], 1) - explicit LLColor4(U32 clr); // Initializes LLColor4 to (r=clr>>24, etc)) - explicit LLColor4(const LLColor4U& color4u); // "explicit" to avoid automatic conversion - explicit LLColor4(const LLVector4& vector4); // "explicit" to avoid automatic conversion - - LLSD getValue() const - { - LLSD ret; - ret[0] = mV[0]; - ret[1] = mV[1]; - ret[2] = mV[2]; - ret[3] = mV[3]; - return ret; - } - - void setValue(const LLSD& sd); - - void setHSL(F32 hue, F32 saturation, F32 luminance); - void calcHSL(F32* hue, F32* saturation, F32* luminance) const; - - const LLColor4& setToBlack(); // zero LLColor4 to (0, 0, 0, 1) - const LLColor4& setToWhite(); // zero LLColor4 to (0, 0, 0, 1) - - const LLColor4& setVec(F32 r, F32 g, F32 b, F32 a); // deprecated -- use set() - const LLColor4& setVec(F32 r, F32 g, F32 b); // deprecated -- use set() - const LLColor4& setVec(const LLColor4 &vec); // deprecated -- use set() - const LLColor4& setVec(const LLColor3 &vec); // deprecated -- use set() - const LLColor4& setVec(const LLColor3 &vec, F32 a); // deprecated -- use set() - const LLColor4& setVec(const F32 *vec); // deprecated -- use set() - const LLColor4& setVec(const LLColor4U& color4u); // deprecated -- use set() - - const LLColor4& set(F32 r, F32 g, F32 b, F32 a); // Sets LLColor4 to (r, g, b, a) - const LLColor4& set(F32 r, F32 g, F32 b); // Sets LLColor4 to (r, g, b) (no change in a) - const LLColor4& set(const LLColor4 &vec); // Sets LLColor4 to vec - const LLColor4& set(const LLColor3 &vec); // Sets LLColor4 to LLColor3 vec (no change in alpha) - const LLColor4& set(const LLColor3 &vec, F32 a); // Sets LLColor4 to LLColor3 vec, with alpha specified - const LLColor4& set(const F32 *vec); // Sets LLColor4 to vec - const LLColor4& set(const F64 *vec); // Sets LLColor4 to (double)vec - const LLColor4& set(const LLColor4U& color4u); // Sets LLColor4 to color4u, rescaled. - - // set from a vector of unknown type and size - // may leave some data unmodified - template<typename T> - const LLColor4& set(const std::vector<T>& v); - - // write to a vector of unknown type and size - // maye leave some data unmodified - template<typename T> - void write(std::vector<T>& v) const; - - const LLColor4& setAlpha(F32 a); - - F32 magVec() const; // deprecated -- use length() - F32 magVecSquared() const; // deprecated -- use lengthSquared() - F32 normVec(); // deprecated -- use normalize() - - F32 length() const; // Returns magnitude of LLColor4 - F32 lengthSquared() const; // Returns magnitude squared of LLColor4 - F32 normalize(); // deprecated -- use normalize() - - bool isOpaque() { return mV[VALPHA] == 1.f; } - - F32 operator[](int idx) const { return mV[idx]; } - F32 &operator[](int idx) { return mV[idx]; } - - const LLColor4& operator=(const LLColor3 &a); // Assigns vec3 to vec4 and returns vec4 - - bool operator<(const LLColor4& rhs) const; - friend std::ostream& operator<<(std::ostream& s, const LLColor4 &a); // Print a - friend LLColor4 operator+(const LLColor4 &a, const LLColor4 &b); // Return vector a + b - friend LLColor4 operator-(const LLColor4 &a, const LLColor4 &b); // Return vector a minus b - friend LLColor4 operator*(const LLColor4 &a, const LLColor4 &b); // Return component wise a * b - friend LLColor4 operator*(const LLColor4 &a, F32 k); // Return rgb times scaler k (no alpha change) - friend LLColor4 operator/(const LLColor4 &a, F32 k); // Return rgb divided by scalar k (no alpha change) - friend LLColor4 operator*(F32 k, const LLColor4 &a); // Return rgb times scaler k (no alpha change) - friend LLColor4 operator%(const LLColor4 &a, F32 k); // Return alpha times scaler k (no rgb change) - friend LLColor4 operator%(F32 k, const LLColor4 &a); // Return alpha times scaler k (no rgb change) - - friend bool operator==(const LLColor4 &a, const LLColor4 &b); // Return a == b - friend bool operator!=(const LLColor4 &a, const LLColor4 &b); // Return a != b - - friend bool operator==(const LLColor4 &a, const LLColor3 &b); // Return a == b - friend bool operator!=(const LLColor4 &a, const LLColor3 &b); // Return a != b - - friend const LLColor4& operator+=(LLColor4 &a, const LLColor4 &b); // Return vector a + b - friend const LLColor4& operator-=(LLColor4 &a, const LLColor4 &b); // Return vector a minus b - friend const LLColor4& operator*=(LLColor4 &a, F32 k); // Return rgb times scaler k (no alpha change) - friend const LLColor4& operator%=(LLColor4 &a, F32 k); // Return alpha times scaler k (no rgb change) - - friend const LLColor4& operator*=(LLColor4 &a, const LLColor4 &b); // Doesn't multiply alpha! (for lighting) - - // conversion - operator LLColor4U() const; - - // Basic color values. - static LLColor4 red; - static LLColor4 green; - static LLColor4 blue; - static LLColor4 black; - static LLColor4 white; - static LLColor4 yellow; - static LLColor4 magenta; - static LLColor4 cyan; - static LLColor4 smoke; - static LLColor4 grey; - static LLColor4 orange; - static LLColor4 purple; - static LLColor4 pink; - static LLColor4 transparent; - - // Extra color values. - static LLColor4 grey1; - static LLColor4 grey2; - static LLColor4 grey3; - static LLColor4 grey4; - - static LLColor4 red1; - static LLColor4 red2; - static LLColor4 red3; - static LLColor4 red4; - static LLColor4 red5; - - static LLColor4 green1; - static LLColor4 green2; - static LLColor4 green3; - static LLColor4 green4; - static LLColor4 green5; - static LLColor4 green6; - - static LLColor4 blue1; - static LLColor4 blue2; - static LLColor4 blue3; - static LLColor4 blue4; - static LLColor4 blue5; - static LLColor4 blue6; - - static LLColor4 yellow1; - static LLColor4 yellow2; - static LLColor4 yellow3; - static LLColor4 yellow4; - static LLColor4 yellow5; - static LLColor4 yellow6; - static LLColor4 yellow7; - static LLColor4 yellow8; - static LLColor4 yellow9; - - static LLColor4 orange1; - static LLColor4 orange2; - static LLColor4 orange3; - static LLColor4 orange4; - static LLColor4 orange5; - static LLColor4 orange6; - - static LLColor4 magenta1; - static LLColor4 magenta2; - static LLColor4 magenta3; - static LLColor4 magenta4; - - static LLColor4 purple1; - static LLColor4 purple2; - static LLColor4 purple3; - static LLColor4 purple4; - static LLColor4 purple5; - static LLColor4 purple6; - - static LLColor4 pink1; - static LLColor4 pink2; - - static LLColor4 cyan1; - static LLColor4 cyan2; - static LLColor4 cyan3; - static LLColor4 cyan4; - static LLColor4 cyan5; - static LLColor4 cyan6; - - static bool parseColor(const std::string& buf, LLColor4* color); - static bool parseColor4(const std::string& buf, LLColor4* color); - - inline void clamp(); -}; +public: + F32 mV[LENGTHOFCOLOR4]; + LLColor4(); // Initializes LLColor4 to (0, 0, 0, 1) + LLColor4(F32 r, F32 g, F32 b); // Initializes LLColor4 to (r, g, b, 1) + LLColor4(F32 r, F32 g, F32 b, F32 a); // Initializes LLColor4 to (r. g, b, a) + LLColor4(const LLColor3& vec, F32 a = 1.f); // Initializes LLColor4 to (vec, a) + explicit LLColor4(const LLSD& sd); + explicit LLColor4(const F32* vec); // Initializes LLColor4 to (vec[0]. vec[1], vec[2], 1) + explicit LLColor4(U32 clr); // Initializes LLColor4 to (r=clr>>24, etc)) + explicit LLColor4(const LLColor4U& color4u); // "explicit" to avoid automatic conversion + explicit LLColor4(const LLVector4& vector4); // "explicit" to avoid automatic conversion + + LLSD getValue() const + { + LLSD ret; + ret[0] = mV[VRED]; + ret[1] = mV[VGREEN]; + ret[2] = mV[VBLUE]; + ret[3] = mV[VALPHA]; + return ret; + } + void setValue(const LLSD& sd); + + void setHSL(F32 hue, F32 saturation, F32 luminance); + void calcHSL(F32* hue, F32* saturation, F32* luminance) const; + + const LLColor4& setToBlack(); // zero LLColor4 to (0, 0, 0, 1) + const LLColor4& setToWhite(); // zero LLColor4 to (0, 0, 0, 1) + + const LLColor4& setVec(F32 r, F32 g, F32 b, F32 a); // deprecated -- use set() + const LLColor4& setVec(F32 r, F32 g, F32 b); // deprecated -- use set() + const LLColor4& setVec(const LLColor4& vec); // deprecated -- use set() + const LLColor4& setVec(const LLColor3& vec); // deprecated -- use set() + const LLColor4& setVec(const LLColor3& vec, F32 a); // deprecated -- use set() + const LLColor4& setVec(const F32* vec); // deprecated -- use set() + const LLColor4& setVec(const LLColor4U& color4u); // deprecated -- use set() + + const LLColor4& set(F32 r, F32 g, F32 b, F32 a); // Sets LLColor4 to (r, g, b, a) + const LLColor4& set(F32 r, F32 g, F32 b); // Sets LLColor4 to (r, g, b) (no change in a) + const LLColor4& set(const LLColor4& vec); // Sets LLColor4 to vec + const LLColor4& set(const LLColor3& vec); // Sets LLColor4 to LLColor3 vec (no change in alpha) + const LLColor4& set(const LLColor3& vec, F32 a); // Sets LLColor4 to LLColor3 vec, with alpha specified + const LLColor4& set(const F32* vec); // Sets LLColor4 to vec + const LLColor4& set(const F64* vec); // Sets LLColor4 to (double)vec + const LLColor4& set(const LLColor4U& color4u); // Sets LLColor4 to color4u, rescaled. + + // set from a vector of unknown type and size + // may leave some data unmodified + template<typename T> + const LLColor4& set(const std::vector<T>& v); + + // write to a vector of unknown type and size + // maye leave some data unmodified + template<typename T> + void write(std::vector<T>& v) const; + + const LLColor4& setAlpha(F32 a); + + F32 magVec() const; // deprecated -- use length() + F32 magVecSquared() const; // deprecated -- use lengthSquared() + F32 normVec(); // deprecated -- use normalize() + + F32 length() const; // Returns magnitude of LLColor4 + F32 lengthSquared() const; // Returns magnitude squared of LLColor4 + F32 normalize(); // deprecated -- use normalize() + + bool isOpaque() const { return mV[VALPHA] == 1.f; } + + F32 operator[](int idx) const { return mV[idx]; } + F32& operator[](int idx) { return mV[idx]; } + + const LLColor4& operator=(const LLColor3& a); // Assigns vec3 to vec4 and returns vec4 + + bool operator<(const LLColor4& rhs) const; + friend std::ostream& operator<<(std::ostream& s, const LLColor4& a); // Print a + friend LLColor4 operator+(const LLColor4& a, const LLColor4& b); // Return vector a + b + friend LLColor4 operator-(const LLColor4& a, const LLColor4& b); // Return vector a minus b + friend LLColor4 operator*(const LLColor4& a, const LLColor4& b); // Return component wise a * b + friend LLColor4 operator*(const LLColor4& a, F32 k); // Return rgb times scaler k (no alpha change) + friend LLColor4 operator/(const LLColor4& a, F32 k); // Return rgb divided by scalar k (no alpha change) + friend LLColor4 operator*(F32 k, const LLColor4& a); // Return rgb times scaler k (no alpha change) + friend LLColor4 operator%(const LLColor4& a, F32 k); // Return alpha times scaler k (no rgb change) + friend LLColor4 operator%(F32 k, const LLColor4& a); // Return alpha times scaler k (no rgb change) + + friend bool operator==(const LLColor4& a, const LLColor4& b); // Return a == b + friend bool operator!=(const LLColor4& a, const LLColor4& b); // Return a != b + + friend bool operator==(const LLColor4& a, const LLColor3& b); // Return a == b + friend bool operator!=(const LLColor4& a, const LLColor3& b); // Return a != b + + friend const LLColor4& operator+=(LLColor4& a, const LLColor4& b); // Return vector a + b + friend const LLColor4& operator-=(LLColor4& a, const LLColor4& b); // Return vector a minus b + friend const LLColor4& operator*=(LLColor4& a, F32 k); // Return rgb times scaler k (no alpha change) + friend const LLColor4& operator%=(LLColor4& a, F32 k); // Return alpha times scaler k (no rgb change) + + friend const LLColor4& operator*=(LLColor4& a, const LLColor4& b); // Doesn't multiply alpha! (for lighting) + + // conversion + operator LLColor4U() const; + + // Basic color values. + static LLColor4 red; + static LLColor4 green; + static LLColor4 blue; + static LLColor4 black; + static LLColor4 white; + static LLColor4 yellow; + static LLColor4 magenta; + static LLColor4 cyan; + static LLColor4 smoke; + static LLColor4 grey; + static LLColor4 orange; + static LLColor4 purple; + static LLColor4 pink; + static LLColor4 transparent; + + // Extra color values. + static LLColor4 grey1; + static LLColor4 grey2; + static LLColor4 grey3; + static LLColor4 grey4; + + static LLColor4 red1; + static LLColor4 red2; + static LLColor4 red3; + static LLColor4 red4; + static LLColor4 red5; + + static LLColor4 green1; + static LLColor4 green2; + static LLColor4 green3; + static LLColor4 green4; + static LLColor4 green5; + static LLColor4 green6; + + static LLColor4 blue1; + static LLColor4 blue2; + static LLColor4 blue3; + static LLColor4 blue4; + static LLColor4 blue5; + static LLColor4 blue6; + + static LLColor4 yellow1; + static LLColor4 yellow2; + static LLColor4 yellow3; + static LLColor4 yellow4; + static LLColor4 yellow5; + static LLColor4 yellow6; + static LLColor4 yellow7; + static LLColor4 yellow8; + static LLColor4 yellow9; + + static LLColor4 orange1; + static LLColor4 orange2; + static LLColor4 orange3; + static LLColor4 orange4; + static LLColor4 orange5; + static LLColor4 orange6; + + static LLColor4 magenta1; + static LLColor4 magenta2; + static LLColor4 magenta3; + static LLColor4 magenta4; + + static LLColor4 purple1; + static LLColor4 purple2; + static LLColor4 purple3; + static LLColor4 purple4; + static LLColor4 purple5; + static LLColor4 purple6; + + static LLColor4 pink1; + static LLColor4 pink2; + + static LLColor4 cyan1; + static LLColor4 cyan2; + static LLColor4 cyan3; + static LLColor4 cyan4; + static LLColor4 cyan5; + static LLColor4 cyan6; + + static bool parseColor(const std::string& buf, LLColor4* color); + static bool parseColor4(const std::string& buf, LLColor4* color); + + inline void clamp(); +}; // Non-member functions -F32 distVec(const LLColor4 &a, const LLColor4 &b); // Returns distance between a and b -F32 distVec_squared(const LLColor4 &a, const LLColor4 &b); // Returns distance squared between a and b -LLColor3 vec4to3(const LLColor4 &vec); -LLColor4 vec3to4(const LLColor3 &vec); -LLColor4 lerp(const LLColor4 &a, const LLColor4 &b, F32 u); +F32 distVec(const LLColor4& a, const LLColor4& b); // Returns distance between a and b +F32 distVec_squared(const LLColor4& a, const LLColor4& b); // Returns distance squared between a and b +LLColor3 vec4to3(const LLColor4& vec); +LLColor4 vec3to4(const LLColor3& vec); +LLColor4 lerp(const LLColor4& a, const LLColor4& b, F32 u); -inline LLColor4::LLColor4(void) +inline LLColor4::LLColor4() { - mV[VRED] = 0.f; + mV[VRED] = 0.f; mV[VGREEN] = 0.f; - mV[VBLUE] = 0.f; + mV[VBLUE] = 0.f; mV[VALPHA] = 1.f; } @@ -255,149 +253,146 @@ inline LLColor4::LLColor4(const LLSD& sd) inline LLColor4::LLColor4(F32 r, F32 g, F32 b) { - mV[VRED] = r; + mV[VRED] = r; mV[VGREEN] = g; - mV[VBLUE] = b; + mV[VBLUE] = b; mV[VALPHA] = 1.f; } inline LLColor4::LLColor4(F32 r, F32 g, F32 b, F32 a) { - mV[VRED] = r; + mV[VRED] = r; mV[VGREEN] = g; - mV[VBLUE] = b; + mV[VBLUE] = b; mV[VALPHA] = a; } inline LLColor4::LLColor4(U32 clr) { - mV[VRED] = (clr&0xff) * (1.0f/255.0f); - mV[VGREEN] = ((clr>>8)&0xff) * (1.0f/255.0f); - mV[VBLUE] = ((clr>>16)&0xff) * (1.0f/255.0f); - mV[VALPHA] = (clr>>24) * (1.0f/255.0f); + mV[VRED] = (clr & 0xff) * (1.0f / 255.0f); + mV[VGREEN] = ((clr >> 8) & 0xff) * (1.0f / 255.0f); + mV[VBLUE] = ((clr >> 16) & 0xff) * (1.0f / 255.0f); + mV[VALPHA] = (clr >> 24) * (1.0f / 255.0f); } - -inline LLColor4::LLColor4(const F32 *vec) +inline LLColor4::LLColor4(const F32* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; mV[VALPHA] = vec[VALPHA]; } -inline const LLColor4& LLColor4::setToBlack(void) +inline const LLColor4& LLColor4::setToBlack(void) { - mV[VRED] = 0.f; + mV[VRED] = 0.f; mV[VGREEN] = 0.f; - mV[VBLUE] = 0.f; + mV[VBLUE] = 0.f; mV[VALPHA] = 1.f; return (*this); } -inline const LLColor4& LLColor4::setToWhite(void) +inline const LLColor4& LLColor4::setToWhite(void) { - mV[VRED] = 1.f; + mV[VRED] = 1.f; mV[VGREEN] = 1.f; - mV[VBLUE] = 1.f; + mV[VBLUE] = 1.f; mV[VALPHA] = 1.f; return (*this); } -inline const LLColor4& LLColor4::set(F32 x, F32 y, F32 z) +inline const LLColor4& LLColor4::set(F32 x, F32 y, F32 z) { - mV[VRED] = x; + mV[VRED] = x; mV[VGREEN] = y; - mV[VBLUE] = z; + mV[VBLUE] = z; -// no change to alpha! -// mV[VALPHA] = 1.f; + // no change to alpha! + // mV[VALPHA] = 1.f; return (*this); } -inline const LLColor4& LLColor4::set(F32 x, F32 y, F32 z, F32 a) +inline const LLColor4& LLColor4::set(F32 x, F32 y, F32 z, F32 a) { - mV[VRED] = x; + mV[VRED] = x; mV[VGREEN] = y; - mV[VBLUE] = z; + mV[VBLUE] = z; mV[VALPHA] = a; return (*this); } -inline const LLColor4& LLColor4::set(const LLColor4 &vec) +inline const LLColor4& LLColor4::set(const LLColor4& vec) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = vec.mV[VALPHA]; return (*this); } - -inline const LLColor4& LLColor4::set(const F32 *vec) +inline const LLColor4& LLColor4::set(const F32* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; mV[VALPHA] = vec[VALPHA]; return (*this); } -inline const LLColor4& LLColor4::set(const F64 *vec) +inline const LLColor4& LLColor4::set(const F64* vec) { - mV[VRED] = static_cast<F32>(vec[VRED]); + mV[VRED] = static_cast<F32>(vec[VRED]); mV[VGREEN] = static_cast<F32>(vec[VGREEN]); - mV[VBLUE] = static_cast<F32>(vec[VBLUE]); + mV[VBLUE] = static_cast<F32>(vec[VBLUE]); mV[VALPHA] = static_cast<F32>(vec[VALPHA]); return (*this); } // deprecated -inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z) +inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z) { - mV[VRED] = x; + mV[VRED] = x; mV[VGREEN] = y; - mV[VBLUE] = z; + mV[VBLUE] = z; -// no change to alpha! -// mV[VALPHA] = 1.f; + // no change to alpha! + // mV[VALPHA] = 1.f; return (*this); } // deprecated -inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z, F32 a) +inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z, F32 a) { - mV[VRED] = x; + mV[VRED] = x; mV[VGREEN] = y; - mV[VBLUE] = z; + mV[VBLUE] = z; mV[VALPHA] = a; return (*this); } // deprecated -inline const LLColor4& LLColor4::setVec(const LLColor4 &vec) +inline const LLColor4& LLColor4::setVec(const LLColor4& vec) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = vec.mV[VALPHA]; return (*this); } - // deprecated -inline const LLColor4& LLColor4::setVec(const F32 *vec) +inline const LLColor4& LLColor4::setVec(const F32* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; mV[VALPHA] = vec[VALPHA]; return (*this); } -inline const LLColor4& LLColor4::setAlpha(F32 a) +inline const LLColor4& LLColor4::setAlpha(F32 a) { mV[VALPHA] = a; return (*this); @@ -405,155 +400,116 @@ inline const LLColor4& LLColor4::setAlpha(F32 a) // LLColor4 Magnitude and Normalization Functions -inline F32 LLColor4::length(void) const +inline F32 LLColor4::length() const { - return (F32) sqrt(mV[VRED]*mV[VRED] + mV[VGREEN]*mV[VGREEN] + mV[VBLUE]*mV[VBLUE]); + return sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); } -inline F32 LLColor4::lengthSquared(void) const +inline F32 LLColor4::lengthSquared() const { - return mV[VRED]*mV[VRED] + mV[VGREEN]*mV[VGREEN] + mV[VBLUE]*mV[VBLUE]; + return mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]; } -inline F32 LLColor4::normalize(void) +inline F32 LLColor4::normalize() { - F32 mag = (F32) sqrt(mV[VRED]*mV[VRED] + mV[VGREEN]*mV[VGREEN] + mV[VBLUE]*mV[VBLUE]); + F32 mag = sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); F32 oomag; if (mag) { - oomag = 1.f/mag; + oomag = 1.f / mag; mV[VRED] *= oomag; mV[VGREEN] *= oomag; mV[VBLUE] *= oomag; } - return (mag); + return mag; } // deprecated -inline F32 LLColor4::magVec(void) const +inline F32 LLColor4::magVec() const { - return (F32) sqrt(mV[VRED]*mV[VRED] + mV[VGREEN]*mV[VGREEN] + mV[VBLUE]*mV[VBLUE]); + return sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); } // deprecated -inline F32 LLColor4::magVecSquared(void) const +inline F32 LLColor4::magVecSquared() const { - return mV[VRED]*mV[VRED] + mV[VGREEN]*mV[VGREEN] + mV[VBLUE]*mV[VBLUE]; + return mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]; } // deprecated -inline F32 LLColor4::normVec(void) +inline F32 LLColor4::normVec() { - F32 mag = (F32) sqrt(mV[VRED]*mV[VRED] + mV[VGREEN]*mV[VGREEN] + mV[VBLUE]*mV[VBLUE]); + F32 mag = sqrt(mV[VRED] * mV[VRED] + mV[VGREEN] * mV[VGREEN] + mV[VBLUE] * mV[VBLUE]); F32 oomag; if (mag) { - oomag = 1.f/mag; + oomag = 1.f / mag; mV[VRED] *= oomag; mV[VGREEN] *= oomag; mV[VBLUE] *= oomag; } - return (mag); + return mag; } // LLColor4 Operators - -inline LLColor4 operator+(const LLColor4 &a, const LLColor4 &b) +inline LLColor4 operator+(const LLColor4& a, const LLColor4& b) { - return LLColor4( - a.mV[VRED] + b.mV[VRED], - a.mV[VGREEN] + b.mV[VGREEN], - a.mV[VBLUE] + b.mV[VBLUE], - a.mV[VALPHA] + b.mV[VALPHA]); + return LLColor4(a.mV[VRED] + b.mV[VRED], a.mV[VGREEN] + b.mV[VGREEN], a.mV[VBLUE] + b.mV[VBLUE], a.mV[VALPHA] + b.mV[VALPHA]); } -inline LLColor4 operator-(const LLColor4 &a, const LLColor4 &b) +inline LLColor4 operator-(const LLColor4& a, const LLColor4& b) { - return LLColor4( - a.mV[VRED] - b.mV[VRED], - a.mV[VGREEN] - b.mV[VGREEN], - a.mV[VBLUE] - b.mV[VBLUE], - a.mV[VALPHA] - b.mV[VALPHA]); + return LLColor4(a.mV[VRED] - b.mV[VRED], a.mV[VGREEN] - b.mV[VGREEN], a.mV[VBLUE] - b.mV[VBLUE], a.mV[VALPHA] - b.mV[VALPHA]); } -inline LLColor4 operator*(const LLColor4 &a, const LLColor4 &b) +inline LLColor4 operator*(const LLColor4& a, const LLColor4& b) { - return LLColor4( - a.mV[VRED] * b.mV[VRED], - a.mV[VGREEN] * b.mV[VGREEN], - a.mV[VBLUE] * b.mV[VBLUE], - a.mV[VALPHA] * b.mV[VALPHA]); + return LLColor4(a.mV[VRED] * b.mV[VRED], a.mV[VGREEN] * b.mV[VGREEN], a.mV[VBLUE] * b.mV[VBLUE], a.mV[VALPHA] * b.mV[VALPHA]); } -inline LLColor4 operator*(const LLColor4 &a, F32 k) +inline LLColor4 operator*(const LLColor4& a, F32 k) { // only affects rgb (not a!) - return LLColor4( - a.mV[VRED] * k, - a.mV[VGREEN] * k, - a.mV[VBLUE] * k, - a.mV[VALPHA]); + return LLColor4(a.mV[VRED] * k, a.mV[VGREEN] * k, a.mV[VBLUE] * k, a.mV[VALPHA]); } -inline LLColor4 operator/(const LLColor4 &a, F32 k) +inline LLColor4 operator/(const LLColor4& a, F32 k) { - return LLColor4( - a.mV[VRED] / k, - a.mV[VGREEN] / k, - a.mV[VBLUE] / k, - a.mV[VALPHA]); + return LLColor4(a.mV[VRED] / k, a.mV[VGREEN] / k, a.mV[VBLUE] / k, a.mV[VALPHA]); } -inline LLColor4 operator*(F32 k, const LLColor4 &a) +inline LLColor4 operator*(F32 k, const LLColor4& a) { // only affects rgb (not a!) - return LLColor4( - a.mV[VRED] * k, - a.mV[VGREEN] * k, - a.mV[VBLUE] * k, - a.mV[VALPHA]); + return LLColor4(a.mV[VRED] * k, a.mV[VGREEN] * k, a.mV[VBLUE] * k, a.mV[VALPHA]); } -inline LLColor4 operator%(F32 k, const LLColor4 &a) +inline LLColor4 operator%(F32 k, const LLColor4& a) { // only affects alpha (not rgb!) - return LLColor4( - a.mV[VRED], - a.mV[VGREEN], - a.mV[VBLUE], - a.mV[VALPHA] * k); + return LLColor4(a.mV[VRED], a.mV[VGREEN], a.mV[VBLUE], a.mV[VALPHA] * k); } -inline LLColor4 operator%(const LLColor4 &a, F32 k) +inline LLColor4 operator%(const LLColor4& a, F32 k) { // only affects alpha (not rgb!) - return LLColor4( - a.mV[VRED], - a.mV[VGREEN], - a.mV[VBLUE], - a.mV[VALPHA] * k); + return LLColor4(a.mV[VRED], a.mV[VGREEN], a.mV[VBLUE], a.mV[VALPHA] * k); } -inline bool operator==(const LLColor4 &a, const LLColor4 &b) +inline bool operator==(const LLColor4& a, const LLColor4& b) { - return ( (a.mV[VRED] == b.mV[VRED]) - &&(a.mV[VGREEN] == b.mV[VGREEN]) - &&(a.mV[VBLUE] == b.mV[VBLUE]) - &&(a.mV[VALPHA] == b.mV[VALPHA])); + return ((a.mV[VRED] == b.mV[VRED]) && (a.mV[VGREEN] == b.mV[VGREEN]) && (a.mV[VBLUE] == b.mV[VBLUE]) && (a.mV[VALPHA] == b.mV[VALPHA])); } -inline bool operator!=(const LLColor4 &a, const LLColor4 &b) +inline bool operator!=(const LLColor4& a, const LLColor4& b) { - return ( (a.mV[VRED] != b.mV[VRED]) - ||(a.mV[VGREEN] != b.mV[VGREEN]) - ||(a.mV[VBLUE] != b.mV[VBLUE]) - ||(a.mV[VALPHA] != b.mV[VALPHA])); + return ((a.mV[VRED] != b.mV[VRED]) || (a.mV[VGREEN] != b.mV[VGREEN]) || (a.mV[VBLUE] != b.mV[VBLUE]) || (a.mV[VALPHA] != b.mV[VALPHA])); } -inline const LLColor4& operator+=(LLColor4 &a, const LLColor4 &b) +inline const LLColor4& operator+=(LLColor4& a, const LLColor4& b) { a.mV[VRED] += b.mV[VRED]; a.mV[VGREEN] += b.mV[VGREEN]; @@ -562,7 +518,7 @@ inline const LLColor4& operator+=(LLColor4 &a, const LLColor4 &b) return a; } -inline const LLColor4& operator-=(LLColor4 &a, const LLColor4 &b) +inline const LLColor4& operator-=(LLColor4& a, const LLColor4& b) { a.mV[VRED] -= b.mV[VRED]; a.mV[VGREEN] -= b.mV[VGREEN]; @@ -571,7 +527,7 @@ inline const LLColor4& operator-=(LLColor4 &a, const LLColor4 &b) return a; } -inline const LLColor4& operator*=(LLColor4 &a, F32 k) +inline const LLColor4& operator*=(LLColor4& a, F32 k) { // only affects rgb (not a!) a.mV[VRED] *= k; @@ -580,121 +536,120 @@ inline const LLColor4& operator*=(LLColor4 &a, F32 k) return a; } -inline const LLColor4& operator *=(LLColor4 &a, const LLColor4 &b) +inline const LLColor4& operator*=(LLColor4& a, const LLColor4& b) { a.mV[VRED] *= b.mV[VRED]; a.mV[VGREEN] *= b.mV[VGREEN]; a.mV[VBLUE] *= b.mV[VBLUE]; -// a.mV[VALPHA] *= b.mV[VALPHA]; + // a.mV[VALPHA] *= b.mV[VALPHA]; return a; } -inline const LLColor4& operator%=(LLColor4 &a, F32 k) +inline const LLColor4& operator%=(LLColor4& a, F32 k) { // only affects alpha (not rgb!) a.mV[VALPHA] *= k; return a; } - // Non-member functions -inline F32 distVec(const LLColor4 &a, const LLColor4 &b) +inline F32 distVec(const LLColor4& a, const LLColor4& b) { LLColor4 vec = a - b; - return (vec.length()); + return vec.length(); } -inline F32 distVec_squared(const LLColor4 &a, const LLColor4 &b) +inline F32 distVec_squared(const LLColor4& a, const LLColor4& b) { LLColor4 vec = a - b; - return (vec.lengthSquared()); + return vec.lengthSquared(); } -inline LLColor4 lerp(const LLColor4 &a, const LLColor4 &b, F32 u) +inline LLColor4 lerp(const LLColor4& a, const LLColor4& b, F32 u) { - return LLColor4( - a.mV[VRED] + (b.mV[VRED] - a.mV[VRED]) * u, - a.mV[VGREEN] + (b.mV[VGREEN] - a.mV[VGREEN]) * u, - a.mV[VBLUE] + (b.mV[VBLUE] - a.mV[VBLUE]) * u, - a.mV[VALPHA] + (b.mV[VALPHA] - a.mV[VALPHA]) * u); + return LLColor4(a.mV[VRED] + (b.mV[VRED] - a.mV[VRED]) * u, + a.mV[VGREEN] + (b.mV[VGREEN] - a.mV[VGREEN]) * u, + a.mV[VBLUE] + (b.mV[VBLUE] - a.mV[VBLUE]) * u, + a.mV[VALPHA] + (b.mV[VALPHA] - a.mV[VALPHA]) * u); } inline bool LLColor4::operator<(const LLColor4& rhs) const { - if (mV[0] != rhs.mV[0]) + if (mV[VRED] != rhs.mV[VRED]) { - return mV[0] < rhs.mV[0]; + return mV[VRED] < rhs.mV[VRED]; } - if (mV[1] != rhs.mV[1]) + if (mV[VGREEN] != rhs.mV[VGREEN]) { - return mV[1] < rhs.mV[1]; + return mV[VGREEN] < rhs.mV[VGREEN]; } - if (mV[2] != rhs.mV[2]) + if (mV[VBLUE] != rhs.mV[VBLUE]) { - return mV[2] < rhs.mV[2]; + return mV[VBLUE] < rhs.mV[VBLUE]; } - return mV[3] < rhs.mV[3]; + return mV[VALPHA] < rhs.mV[VALPHA]; } void LLColor4::clamp() { // Clamp the color... - if (mV[0] < 0.f) + if (mV[VRED] < 0.f) { - mV[0] = 0.f; + mV[VRED] = 0.f; } - else if (mV[0] > 1.f) + else if (mV[VRED] > 1.f) { - mV[0] = 1.f; + mV[VRED] = 1.f; } - if (mV[1] < 0.f) + if (mV[VGREEN] < 0.f) { - mV[1] = 0.f; + mV[VGREEN] = 0.f; } - else if (mV[1] > 1.f) + else if (mV[VGREEN] > 1.f) { - mV[1] = 1.f; + mV[VGREEN] = 1.f; } - if (mV[2] < 0.f) + if (mV[VBLUE] < 0.f) { - mV[2] = 0.f; + mV[VBLUE] = 0.f; } - else if (mV[2] > 1.f) + else if (mV[VBLUE] > 1.f) { - mV[2] = 1.f; + mV[VBLUE] = 1.f; } - if (mV[3] < 0.f) + if (mV[VALPHA] < 0.f) { - mV[3] = 0.f; + mV[VALPHA] = 0.f; } - else if (mV[3] > 1.f) + else if (mV[VALPHA] > 1.f) { - mV[3] = 1.f; + mV[VALPHA] = 1.f; } } // Return the given linear space color value in gamma corrected (sRGB) space -inline const LLColor4 srgbColor4(const LLColor4 &a) { +inline const LLColor4 srgbColor4(const LLColor4& a) +{ LLColor4 srgbColor; - srgbColor.mV[0] = linearTosRGB(a.mV[0]); - srgbColor.mV[1] = linearTosRGB(a.mV[1]); - srgbColor.mV[2] = linearTosRGB(a.mV[2]); - srgbColor.mV[3] = a.mV[3]; + srgbColor.mV[VRED] = linearTosRGB(a.mV[VRED]); + srgbColor.mV[VGREEN] = linearTosRGB(a.mV[VGREEN]); + srgbColor.mV[VBLUE] = linearTosRGB(a.mV[VBLUE]); + srgbColor.mV[VALPHA] = a.mV[VALPHA]; return srgbColor; } // Return the given gamma corrected (sRGB) color in linear space -inline const LLColor4 linearColor4(const LLColor4 &a) +inline const LLColor4 linearColor4(const LLColor4& a) { LLColor4 linearColor; - linearColor.mV[0] = sRGBtoLinear(a.mV[0]); - linearColor.mV[1] = sRGBtoLinear(a.mV[1]); - linearColor.mV[2] = sRGBtoLinear(a.mV[2]); - linearColor.mV[3] = a.mV[3]; + linearColor.mV[VRED] = sRGBtoLinear(a.mV[VRED]); + linearColor.mV[VGREEN] = sRGBtoLinear(a.mV[VGREEN]); + linearColor.mV[VBLUE] = sRGBtoLinear(a.mV[VBLUE]); + linearColor.mV[VALPHA] = a.mV[VALPHA]; return linearColor; } @@ -720,4 +675,3 @@ void LLColor4::write(std::vector<T>& v) const } #endif - diff --git a/indra/llmath/v4coloru.cpp b/indra/llmath/v4coloru.cpp index acf349245a..46314bae26 100644 --- a/indra/llmath/v4coloru.cpp +++ b/indra/llmath/v4coloru.cpp @@ -26,62 +26,19 @@ #include "linden_common.h" -//#include "v3coloru.h" #include "v4coloru.h" -#include "v4color.h" -//#include "vmath.h" #include "llmath.h" // LLColor4U +// clang-format off LLColor4U LLColor4U::white(255, 255, 255, 255); LLColor4U LLColor4U::black( 0, 0, 0, 255); LLColor4U LLColor4U::red (255, 0, 0, 255); LLColor4U LLColor4U::green( 0, 255, 0, 255); LLColor4U LLColor4U::blue ( 0, 0, 255, 255); +// clang-format on -// conversion -/* inlined to fix gcc compile link error -LLColor4U::operator LLColor4() -{ - return(LLColor4((F32)mV[VRED]/255.f,(F32)mV[VGREEN]/255.f,(F32)mV[VBLUE]/255.f,(F32)mV[VALPHA]/255.f)); -} -*/ - -// Constructors - - -/* -LLColor4U::LLColor4U(const LLColor3 &vec) -{ - mV[VRED] = vec.mV[VRED]; - mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; - mV[VALPHA] = 255; -} -*/ - - -// Clear and Assignment Functions - - - -// LLColor4U Operators - -/* -LLColor4U LLColor4U::operator=(const LLColor3 &a) -{ - mV[VRED] = a.mV[VRED]; - mV[VGREEN] = a.mV[VGREEN]; - mV[VBLUE] = a.mV[VBLUE]; - -// converting from an rgb sets a=1 (opaque) - mV[VALPHA] = 255; - return (*this); -} -*/ - - -std::ostream& operator<<(std::ostream& s, const LLColor4U &a) +std::ostream& operator<<(std::ostream& s, const LLColor4U& a) { s << "{ " << (S32)a.mV[VRED] << ", " << (S32)a.mV[VGREEN] << ", " << (S32)a.mV[VBLUE] << ", " << (S32)a.mV[VALPHA] << " }"; return s; @@ -90,31 +47,31 @@ std::ostream& operator<<(std::ostream& s, const LLColor4U &a) // static bool LLColor4U::parseColor4U(const std::string& buf, LLColor4U* value) { - if( buf.empty() || value == nullptr) + if (buf.empty() || value == nullptr) { return false; } - U32 v[4]; - S32 count = sscanf( buf.c_str(), "%u, %u, %u, %u", v + 0, v + 1, v + 2, v + 3 ); - if (1 == count ) + U32 v[4]{}; + S32 count = sscanf(buf.c_str(), "%u, %u, %u, %u", v + 0, v + 1, v + 2, v + 3); + if (1 == count) { // try this format - count = sscanf( buf.c_str(), "%u %u %u %u", v + 0, v + 1, v + 2, v + 3 ); + count = sscanf(buf.c_str(), "%u %u %u %u", v + 0, v + 1, v + 2, v + 3); } - if( 4 != count ) + if (4 != count) { return false; } - for( S32 i = 0; i < 4; i++ ) + for (S32 i = 0; i < 4; i++) { - if( v[i] > U8_MAX ) + if (v[i] > U8_MAX) { return false; } } - value->set( U8(v[0]), U8(v[1]), U8(v[2]), U8(v[3]) ); + value->set(U8(v[0]), U8(v[1]), U8(v[2]), U8(v[3])); return true; } diff --git a/indra/llmath/v4coloru.h b/indra/llmath/v4coloru.h index 29128a08a7..e1a0206461 100644 --- a/indra/llmath/v4coloru.h +++ b/indra/llmath/v4coloru.h @@ -28,104 +28,93 @@ #define LL_V4COLORU_H #include "llerror.h" -//#include "vmath.h" #include "llmath.h" -//#include "v4color.h" #include "v3color.h" #include "v4color.h" -//class LLColor3U; class LLColor4; // LLColor4U = | red green blue alpha | -static const U32 LENGTHOFCOLOR4U = 4; - +static constexpr U32 LENGTHOFCOLOR4U = 4; class LLColor4U { public: - U8 mV[LENGTHOFCOLOR4U]; - LLColor4U(); // Initializes LLColor4U to (0, 0, 0, 1) - LLColor4U(U8 r, U8 g, U8 b); // Initializes LLColor4U to (r, g, b, 1) - LLColor4U(U8 r, U8 g, U8 b, U8 a); // Initializes LLColor4U to (r. g, b, a) - LLColor4U(const U8 *vec); // Initializes LLColor4U to (vec[0]. vec[1], vec[2], 1) - explicit LLColor4U(const LLSD& sd) - { - setValue(sd); - } + LLColor4U(); // Initializes LLColor4U to (0, 0, 0, 1) + LLColor4U(U8 r, U8 g, U8 b); // Initializes LLColor4U to (r, g, b, 1) + LLColor4U(U8 r, U8 g, U8 b, U8 a); // Initializes LLColor4U to (r. g, b, a) + LLColor4U(const U8* vec); // Initializes LLColor4U to (vec[0]. vec[1], vec[2], 1) + explicit LLColor4U(const LLSD& sd) { setValue(sd); } void setValue(const LLSD& sd) { - mV[0] = sd[0].asInteger(); - mV[1] = sd[1].asInteger(); - mV[2] = sd[2].asInteger(); - mV[3] = sd[3].asInteger(); + mV[VRED] = sd[0].asInteger(); + mV[VGREEN] = sd[1].asInteger(); + mV[VBLUE] = sd[2].asInteger(); + mV[VALPHA] = sd[3].asInteger(); } LLSD getValue() const { LLSD ret; - ret[0] = mV[0]; - ret[1] = mV[1]; - ret[2] = mV[2]; - ret[3] = mV[3]; + ret[0] = mV[VRED]; + ret[1] = mV[VGREEN]; + ret[2] = mV[VBLUE]; + ret[3] = mV[VALPHA]; return ret; } - const LLColor4U& setToBlack(); // zero LLColor4U to (0, 0, 0, 1) - const LLColor4U& setToWhite(); // zero LLColor4U to (0, 0, 0, 1) + const LLColor4U& setToBlack(); // zero LLColor4U to (0, 0, 0, 1) + const LLColor4U& setToWhite(); // zero LLColor4U to (0, 0, 0, 1) - const LLColor4U& set(U8 r, U8 g, U8 b, U8 a);// Sets LLColor4U to (r, g, b, a) - const LLColor4U& set(U8 r, U8 g, U8 b); // Sets LLColor4U to (r, g, b) (no change in a) - const LLColor4U& set(const LLColor4U &vec); // Sets LLColor4U to vec - const LLColor4U& set(const U8 *vec); // Sets LLColor4U to vec + const LLColor4U& set(U8 r, U8 g, U8 b, U8 a); // Sets LLColor4U to (r, g, b, a) + const LLColor4U& set(U8 r, U8 g, U8 b); // Sets LLColor4U to (r, g, b) (no change in a) + const LLColor4U& set(const LLColor4U& vec); // Sets LLColor4U to vec + const LLColor4U& set(const U8* vec); // Sets LLColor4U to vec - const LLColor4U& setVec(U8 r, U8 g, U8 b, U8 a); // deprecated -- use set() - const LLColor4U& setVec(U8 r, U8 g, U8 b); // deprecated -- use set() - const LLColor4U& setVec(const LLColor4U &vec); // deprecated -- use set() - const LLColor4U& setVec(const U8 *vec); // deprecated -- use set() + const LLColor4U& setVec(U8 r, U8 g, U8 b, U8 a); // deprecated -- use set() + const LLColor4U& setVec(U8 r, U8 g, U8 b); // deprecated -- use set() + const LLColor4U& setVec(const LLColor4U& vec); // deprecated -- use set() + const LLColor4U& setVec(const U8* vec); // deprecated -- use set() - const LLColor4U& setAlpha(U8 a); + const LLColor4U& setAlpha(U8 a); - F32 magVec() const; // deprecated -- use length() - F32 magVecSquared() const; // deprecated -- use lengthSquared() + F32 magVec() const; // deprecated -- use length() + F32 magVecSquared() const; // deprecated -- use lengthSquared() - F32 length() const; // Returns magnitude squared of LLColor4U - F32 lengthSquared() const; // Returns magnitude squared of LLColor4U + F32 length() const; // Returns magnitude squared of LLColor4U + F32 lengthSquared() const; // Returns magnitude squared of LLColor4U - friend std::ostream& operator<<(std::ostream& s, const LLColor4U &a); // Print a - friend LLColor4U operator+(const LLColor4U &a, const LLColor4U &b); // Return vector a + b - friend LLColor4U operator-(const LLColor4U &a, const LLColor4U &b); // Return vector a minus b - friend LLColor4U operator*(const LLColor4U &a, const LLColor4U &b); // Return a * b - friend bool operator==(const LLColor4U &a, const LLColor4U &b); // Return a == b - friend bool operator!=(const LLColor4U &a, const LLColor4U &b); // Return a != b + friend std::ostream& operator<<(std::ostream& s, const LLColor4U& a); // Print a + friend LLColor4U operator+(const LLColor4U& a, const LLColor4U& b); // Return vector a + b + friend LLColor4U operator-(const LLColor4U& a, const LLColor4U& b); // Return vector a minus b + friend LLColor4U operator*(const LLColor4U& a, const LLColor4U& b); // Return a * b + friend bool operator==(const LLColor4U& a, const LLColor4U& b); // Return a == b + friend bool operator!=(const LLColor4U& a, const LLColor4U& b); // Return a != b - friend const LLColor4U& operator+=(LLColor4U &a, const LLColor4U &b); // Return vector a + b - friend const LLColor4U& operator-=(LLColor4U &a, const LLColor4U &b); // Return vector a minus b - friend const LLColor4U& operator*=(LLColor4U &a, U8 k); // Return rgb times scaler k (no alpha change) - friend const LLColor4U& operator%=(LLColor4U &a, U8 k); // Return alpha times scaler k (no rgb change) + friend const LLColor4U& operator+=(LLColor4U& a, const LLColor4U& b); // Return vector a + b + friend const LLColor4U& operator-=(LLColor4U& a, const LLColor4U& b); // Return vector a minus b + friend const LLColor4U& operator*=(LLColor4U& a, U8 k); // Return rgb times scaler k (no alpha change) + friend const LLColor4U& operator%=(LLColor4U& a, U8 k); // Return alpha times scaler k (no rgb change) - LLColor4U addClampMax(const LLColor4U &color); // Add and clamp the max + LLColor4U addClampMax(const LLColor4U& color); // Add and clamp the max - LLColor4U multAll(const F32 k); // Multiply ALL channels by scalar k + LLColor4U multAll(const F32 k); // Multiply ALL channels by scalar k - inline void setVecScaleClamp(const LLColor3 &color); - inline void setVecScaleClamp(const LLColor4 &color); + inline void setVecScaleClamp(const LLColor3& color); + inline void setVecScaleClamp(const LLColor4& color); static bool parseColor4U(const std::string& buf, LLColor4U* value); // conversion - operator LLColor4() const - { - return LLColor4(*this); - } + operator LLColor4() const { return LLColor4(*this); } - U32 asRGBA() const; - void fromRGBA( U32 aVal ); + U32 asRGBA() const; + void fromRGBA(U32 aVal); static LLColor4U white; static LLColor4U black; @@ -134,104 +123,95 @@ public: static LLColor4U blue; }; - // Non-member functions -F32 distVec(const LLColor4U &a, const LLColor4U &b); // Returns distance between a and b -F32 distVec_squared(const LLColor4U &a, const LLColor4U &b); // Returns distance squared between a and b - +F32 distVec(const LLColor4U& a, const LLColor4U& b); // Returns distance between a and b +F32 distVec_squared(const LLColor4U& a, const LLColor4U& b); // Returns distance squared between a and b inline LLColor4U::LLColor4U() { - mV[VRED] = 0; + mV[VRED] = 0; mV[VGREEN] = 0; - mV[VBLUE] = 0; + mV[VBLUE] = 0; mV[VALPHA] = 255; } inline LLColor4U::LLColor4U(U8 r, U8 g, U8 b) { - mV[VRED] = r; + mV[VRED] = r; mV[VGREEN] = g; - mV[VBLUE] = b; + mV[VBLUE] = b; mV[VALPHA] = 255; } inline LLColor4U::LLColor4U(U8 r, U8 g, U8 b, U8 a) { - mV[VRED] = r; + mV[VRED] = r; mV[VGREEN] = g; - mV[VBLUE] = b; + mV[VBLUE] = b; mV[VALPHA] = a; } -inline LLColor4U::LLColor4U(const U8 *vec) +inline LLColor4U::LLColor4U(const U8* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; mV[VALPHA] = vec[VALPHA]; } -/* -inline LLColor4U::operator LLColor4() -{ - return(LLColor4((F32)mV[VRED]/255.f,(F32)mV[VGREEN]/255.f,(F32)mV[VBLUE]/255.f,(F32)mV[VALPHA]/255.f)); -} -*/ - inline const LLColor4U& LLColor4U::setToBlack(void) { - mV[VRED] = 0; + mV[VRED] = 0; mV[VGREEN] = 0; - mV[VBLUE] = 0; + mV[VBLUE] = 0; mV[VALPHA] = 255; return (*this); } inline const LLColor4U& LLColor4U::setToWhite(void) { - mV[VRED] = 255; + mV[VRED] = 255; mV[VGREEN] = 255; - mV[VBLUE] = 255; + mV[VBLUE] = 255; mV[VALPHA] = 255; return (*this); } inline const LLColor4U& LLColor4U::set(const U8 x, const U8 y, const U8 z) { - mV[VRED] = x; + mV[VRED] = x; mV[VGREEN] = y; - mV[VBLUE] = z; + mV[VBLUE] = z; -// no change to alpha! -// mV[VALPHA] = 255; + // no change to alpha! + // mV[VALPHA] = 255; return (*this); } inline const LLColor4U& LLColor4U::set(const U8 r, const U8 g, const U8 b, U8 a) { - mV[0] = r; - mV[1] = g; - mV[2] = b; - mV[3] = a; + mV[VRED] = r; + mV[VGREEN] = g; + mV[VBLUE] = b; + mV[VALPHA] = a; return (*this); } -inline const LLColor4U& LLColor4U::set(const LLColor4U &vec) +inline const LLColor4U& LLColor4U::set(const LLColor4U& vec) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = vec.mV[VALPHA]; return (*this); } -inline const LLColor4U& LLColor4U::set(const U8 *vec) +inline const LLColor4U& LLColor4U::set(const U8* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; mV[VALPHA] = vec[VALPHA]; return (*this); } @@ -239,12 +219,12 @@ inline const LLColor4U& LLColor4U::set(const U8 *vec) // deprecated inline const LLColor4U& LLColor4U::setVec(const U8 x, const U8 y, const U8 z) { - mV[VRED] = x; + mV[VRED] = x; mV[VGREEN] = y; - mV[VBLUE] = z; + mV[VBLUE] = z; -// no change to alpha! -// mV[VALPHA] = 255; + // no change to alpha! + // mV[VALPHA] = 255; return (*this); } @@ -252,29 +232,29 @@ inline const LLColor4U& LLColor4U::setVec(const U8 x, const U8 y, const U8 z) // deprecated inline const LLColor4U& LLColor4U::setVec(const U8 r, const U8 g, const U8 b, U8 a) { - mV[0] = r; - mV[1] = g; - mV[2] = b; - mV[3] = a; + mV[VRED] = r; + mV[VGREEN] = g; + mV[VBLUE] = b; + mV[VALPHA] = a; return (*this); } // deprecated -inline const LLColor4U& LLColor4U::setVec(const LLColor4U &vec) +inline const LLColor4U& LLColor4U::setVec(const LLColor4U& vec) { - mV[VRED] = vec.mV[VRED]; + mV[VRED] = vec.mV[VRED]; mV[VGREEN] = vec.mV[VGREEN]; - mV[VBLUE] = vec.mV[VBLUE]; + mV[VBLUE] = vec.mV[VBLUE]; mV[VALPHA] = vec.mV[VALPHA]; return (*this); } // deprecated -inline const LLColor4U& LLColor4U::setVec(const U8 *vec) +inline const LLColor4U& LLColor4U::setVec(const U8* vec) { - mV[VRED] = vec[VRED]; + mV[VRED] = vec[VRED]; mV[VGREEN] = vec[VGREEN]; - mV[VBLUE] = vec[VBLUE]; + mV[VBLUE] = vec[VBLUE]; mV[VALPHA] = vec[VALPHA]; return (*this); } @@ -287,131 +267,68 @@ inline const LLColor4U& LLColor4U::setAlpha(U8 a) // LLColor4U Magnitude and Normalization Functions -inline F32 LLColor4U::length(void) const +inline F32 LLColor4U::length() const { - return (F32) sqrt( ((F32)mV[VRED]) * mV[VRED] + ((F32)mV[VGREEN]) * mV[VGREEN] + ((F32)mV[VBLUE]) * mV[VBLUE] ); + return sqrt(((F32)mV[VRED]) * mV[VRED] + ((F32)mV[VGREEN]) * mV[VGREEN] + ((F32)mV[VBLUE]) * mV[VBLUE]); } -inline F32 LLColor4U::lengthSquared(void) const +inline F32 LLColor4U::lengthSquared() const { return ((F32)mV[VRED]) * mV[VRED] + ((F32)mV[VGREEN]) * mV[VGREEN] + ((F32)mV[VBLUE]) * mV[VBLUE]; } // deprecated -inline F32 LLColor4U::magVec(void) const +inline F32 LLColor4U::magVec() const { - return (F32) sqrt( ((F32)mV[VRED]) * mV[VRED] + ((F32)mV[VGREEN]) * mV[VGREEN] + ((F32)mV[VBLUE]) * mV[VBLUE] ); + return sqrt(((F32)mV[VRED]) * mV[VRED] + ((F32)mV[VGREEN]) * mV[VGREEN] + ((F32)mV[VBLUE]) * mV[VBLUE]); } // deprecated -inline F32 LLColor4U::magVecSquared(void) const +inline F32 LLColor4U::magVecSquared() const { return ((F32)mV[VRED]) * mV[VRED] + ((F32)mV[VGREEN]) * mV[VGREEN] + ((F32)mV[VBLUE]) * mV[VBLUE]; } -inline LLColor4U operator+(const LLColor4U &a, const LLColor4U &b) +inline LLColor4U operator+(const LLColor4U& a, const LLColor4U& b) { - return LLColor4U( - a.mV[VRED] + b.mV[VRED], - a.mV[VGREEN] + b.mV[VGREEN], - a.mV[VBLUE] + b.mV[VBLUE], - a.mV[VALPHA] + b.mV[VALPHA]); + return LLColor4U(a.mV[VRED] + b.mV[VRED], a.mV[VGREEN] + b.mV[VGREEN], a.mV[VBLUE] + b.mV[VBLUE], a.mV[VALPHA] + b.mV[VALPHA]); } -inline LLColor4U operator-(const LLColor4U &a, const LLColor4U &b) +inline LLColor4U operator-(const LLColor4U& a, const LLColor4U& b) { - return LLColor4U( - a.mV[VRED] - b.mV[VRED], - a.mV[VGREEN] - b.mV[VGREEN], - a.mV[VBLUE] - b.mV[VBLUE], - a.mV[VALPHA] - b.mV[VALPHA]); + return LLColor4U(a.mV[VRED] - b.mV[VRED], a.mV[VGREEN] - b.mV[VGREEN], a.mV[VBLUE] - b.mV[VBLUE], a.mV[VALPHA] - b.mV[VALPHA]); } -inline LLColor4U operator*(const LLColor4U &a, const LLColor4U &b) +inline LLColor4U operator*(const LLColor4U& a, const LLColor4U& b) { - return LLColor4U( - a.mV[VRED] * b.mV[VRED], - a.mV[VGREEN] * b.mV[VGREEN], - a.mV[VBLUE] * b.mV[VBLUE], - a.mV[VALPHA] * b.mV[VALPHA]); + return LLColor4U(a.mV[VRED] * b.mV[VRED], a.mV[VGREEN] * b.mV[VGREEN], a.mV[VBLUE] * b.mV[VBLUE], a.mV[VALPHA] * b.mV[VALPHA]); } -inline LLColor4U LLColor4U::addClampMax(const LLColor4U &color) +inline LLColor4U LLColor4U::addClampMax(const LLColor4U& color) { return LLColor4U(llmin((S32)mV[VRED] + color.mV[VRED], 255), - llmin((S32)mV[VGREEN] + color.mV[VGREEN], 255), - llmin((S32)mV[VBLUE] + color.mV[VBLUE], 255), - llmin((S32)mV[VALPHA] + color.mV[VALPHA], 255)); + llmin((S32)mV[VGREEN] + color.mV[VGREEN], 255), + llmin((S32)mV[VBLUE] + color.mV[VBLUE], 255), + llmin((S32)mV[VALPHA] + color.mV[VALPHA], 255)); } inline LLColor4U LLColor4U::multAll(const F32 k) { // Round to nearest - return LLColor4U( - (U8)ll_round(mV[VRED] * k), - (U8)ll_round(mV[VGREEN] * k), - (U8)ll_round(mV[VBLUE] * k), - (U8)ll_round(mV[VALPHA] * k)); -} -/* -inline LLColor4U operator*(const LLColor4U &a, U8 k) -{ - // only affects rgb (not a!) - return LLColor4U( - a.mV[VRED] * k, - a.mV[VGREEN] * k, - a.mV[VBLUE] * k, - a.mV[VALPHA]); + return LLColor4U((U8)ll_round(mV[VRED] * k), (U8)ll_round(mV[VGREEN] * k), (U8)ll_round(mV[VBLUE] * k), (U8)ll_round(mV[VALPHA] * k)); } -inline LLColor4U operator*(U8 k, const LLColor4U &a) +inline bool operator==(const LLColor4U& a, const LLColor4U& b) { - // only affects rgb (not a!) - return LLColor4U( - a.mV[VRED] * k, - a.mV[VGREEN] * k, - a.mV[VBLUE] * k, - a.mV[VALPHA]); + return ((a.mV[VRED] == b.mV[VRED]) && (a.mV[VGREEN] == b.mV[VGREEN]) && (a.mV[VBLUE] == b.mV[VBLUE]) && (a.mV[VALPHA] == b.mV[VALPHA])); } -inline LLColor4U operator%(U8 k, const LLColor4U &a) +inline bool operator!=(const LLColor4U& a, const LLColor4U& b) { - // only affects alpha (not rgb!) - return LLColor4U( - a.mV[VRED], - a.mV[VGREEN], - a.mV[VBLUE], - a.mV[VALPHA] * k ); + return ((a.mV[VRED] != b.mV[VRED]) || (a.mV[VGREEN] != b.mV[VGREEN]) || (a.mV[VBLUE] != b.mV[VBLUE]) || (a.mV[VALPHA] != b.mV[VALPHA])); } -inline LLColor4U operator%(const LLColor4U &a, U8 k) -{ - // only affects alpha (not rgb!) - return LLColor4U( - a.mV[VRED], - a.mV[VGREEN], - a.mV[VBLUE], - a.mV[VALPHA] * k ); -} -*/ - -inline bool operator==(const LLColor4U &a, const LLColor4U &b) -{ - return ( (a.mV[VRED] == b.mV[VRED]) - &&(a.mV[VGREEN] == b.mV[VGREEN]) - &&(a.mV[VBLUE] == b.mV[VBLUE]) - &&(a.mV[VALPHA] == b.mV[VALPHA])); -} - -inline bool operator!=(const LLColor4U &a, const LLColor4U &b) -{ - return ( (a.mV[VRED] != b.mV[VRED]) - ||(a.mV[VGREEN] != b.mV[VGREEN]) - ||(a.mV[VBLUE] != b.mV[VBLUE]) - ||(a.mV[VALPHA] != b.mV[VALPHA])); -} - -inline const LLColor4U& operator+=(LLColor4U &a, const LLColor4U &b) +inline const LLColor4U& operator+=(LLColor4U& a, const LLColor4U& b) { a.mV[VRED] += b.mV[VRED]; a.mV[VGREEN] += b.mV[VGREEN]; @@ -420,7 +337,7 @@ inline const LLColor4U& operator+=(LLColor4U &a, const LLColor4U &b) return a; } -inline const LLColor4U& operator-=(LLColor4U &a, const LLColor4U &b) +inline const LLColor4U& operator-=(LLColor4U& a, const LLColor4U& b) { a.mV[VRED] -= b.mV[VRED]; a.mV[VGREEN] -= b.mV[VGREEN]; @@ -429,7 +346,7 @@ inline const LLColor4U& operator-=(LLColor4U &a, const LLColor4U &b) return a; } -inline const LLColor4U& operator*=(LLColor4U &a, U8 k) +inline const LLColor4U& operator*=(LLColor4U& a, U8 k) { // only affects rgb (not a!) a.mV[VRED] *= k; @@ -438,20 +355,20 @@ inline const LLColor4U& operator*=(LLColor4U &a, U8 k) return a; } -inline const LLColor4U& operator%=(LLColor4U &a, U8 k) +inline const LLColor4U& operator%=(LLColor4U& a, U8 k) { // only affects alpha (not rgb!) a.mV[VALPHA] *= k; return a; } -inline F32 distVec(const LLColor4U &a, const LLColor4U &b) +inline F32 distVec(const LLColor4U& a, const LLColor4U& b) { LLColor4U vec = a - b; return (vec.length()); } -inline F32 distVec_squared(const LLColor4U &a, const LLColor4U &b) +inline F32 distVec_squared(const LLColor4U& a, const LLColor4U& b) { LLColor4U vec = a - b; return (vec.lengthSquared()); @@ -460,13 +377,13 @@ inline F32 distVec_squared(const LLColor4U &a, const LLColor4U &b) void LLColor4U::setVecScaleClamp(const LLColor4& color) { F32 color_scale_factor = 255.f; - F32 max_color = llmax(color.mV[0], color.mV[1], color.mV[2]); + F32 max_color = llmax(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE]); if (max_color > 1.f) { color_scale_factor /= max_color; } - const S32 MAX_COLOR = 255; - S32 r = ll_round(color.mV[0] * color_scale_factor); + constexpr S32 MAX_COLOR = 255; + S32 r = ll_round(color.mV[VRED] * color_scale_factor); if (r > MAX_COLOR) { r = MAX_COLOR; @@ -475,9 +392,9 @@ void LLColor4U::setVecScaleClamp(const LLColor4& color) { r = 0; } - mV[0] = r; + mV[VRED] = r; - S32 g = ll_round(color.mV[1] * color_scale_factor); + S32 g = ll_round(color.mV[VGREEN] * color_scale_factor); if (g > MAX_COLOR) { g = MAX_COLOR; @@ -486,9 +403,9 @@ void LLColor4U::setVecScaleClamp(const LLColor4& color) { g = 0; } - mV[1] = g; + mV[VGREEN] = g; - S32 b = ll_round(color.mV[2] * color_scale_factor); + S32 b = ll_round(color.mV[VBLUE] * color_scale_factor); if (b > MAX_COLOR) { b = MAX_COLOR; @@ -497,10 +414,10 @@ void LLColor4U::setVecScaleClamp(const LLColor4& color) { b = 0; } - mV[2] = b; + mV[VBLUE] = b; // Alpha shouldn't be scaled, just clamped... - S32 a = ll_round(color.mV[3] * MAX_COLOR); + S32 a = ll_round(color.mV[VALPHA] * MAX_COLOR); if (a > MAX_COLOR) { a = MAX_COLOR; @@ -509,44 +426,42 @@ void LLColor4U::setVecScaleClamp(const LLColor4& color) { a = 0; } - mV[3] = a; + mV[VALPHA] = a; } void LLColor4U::setVecScaleClamp(const LLColor3& color) { F32 color_scale_factor = 255.f; - F32 max_color = llmax(color.mV[0], color.mV[1], color.mV[2]); + F32 max_color = llmax(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE]); if (max_color > 1.f) { color_scale_factor /= max_color; } const S32 MAX_COLOR = 255; - S32 r = ll_round(color.mV[0] * color_scale_factor); + S32 r = ll_round(color.mV[VRED] * color_scale_factor); if (r > MAX_COLOR) { r = MAX_COLOR; } - else - if (r < 0) + else if (r < 0) { r = 0; } - mV[0] = r; + mV[VRED] = r; - S32 g = ll_round(color.mV[1] * color_scale_factor); + S32 g = ll_round(color.mV[VGREEN] * color_scale_factor); if (g > MAX_COLOR) { g = MAX_COLOR; } - else - if (g < 0) + else if (g < 0) { g = 0; } - mV[1] = g; + mV[VGREEN] = g; - S32 b = ll_round(color.mV[2] * color_scale_factor); + S32 b = ll_round(color.mV[VBLUE] * color_scale_factor); if (b > MAX_COLOR) { b = MAX_COLOR; @@ -555,31 +470,29 @@ void LLColor4U::setVecScaleClamp(const LLColor3& color) { b = 0; } - mV[2] = b; + mV[VBLUE] = b; - mV[3] = 255; + mV[VALPHA] = 255; } inline U32 LLColor4U::asRGBA() const { // Little endian: values are swapped in memory. The original code access the array like a U32, so we need to swap here - return (mV[3] << 24) | (mV[2] << 16) | (mV[1] << 8) | mV[0]; + return (mV[VALPHA] << 24) | (mV[VBLUE] << 16) | (mV[VGREEN] << 8) | mV[VRED]; } -inline void LLColor4U::fromRGBA( U32 aVal ) +inline void LLColor4U::fromRGBA(U32 aVal) { // Little endian: values are swapped in memory. The original code access the array like a U32, so we need to swap here - mV[ 0 ] = aVal & 0xFF; + mV[0] = aVal & 0xFF; aVal >>= 8; - mV[ 1 ] = aVal & 0xFF; + mV[1] = aVal & 0xFF; aVal >>= 8; - mV[ 2 ] = aVal & 0xFF; + mV[2] = aVal & 0xFF; aVal >>= 8; - mV[ 3 ] = aVal & 0xFF; + mV[3] = aVal & 0xFF; } - #endif - diff --git a/indra/llmath/v4math.cpp b/indra/llmath/v4math.cpp index e87bfc0013..cd475380d6 100644 --- a/indra/llmath/v4math.cpp +++ b/indra/llmath/v4math.cpp @@ -26,7 +26,6 @@ #include "linden_common.h" -//#include "vmath.h" #include "v3math.h" #include "v4math.h" #include "m4math.h" diff --git a/indra/llmath/xform.h b/indra/llmath/xform.h index 7434301670..fa45fffeae 100644 --- a/indra/llmath/xform.h +++ b/indra/llmath/xform.h @@ -115,7 +115,7 @@ public: void clearChanged(U32 bits) { mChanged &= ~bits; } void setScaleChildOffset(bool scale) { mScaleChildOffset = scale; } - bool getScaleChildOffset() { return mScaleChildOffset; } + bool getScaleChildOffset() const { return mScaleChildOffset; } LLXform* getParent() const { return mParent; } LLXform* getRoot() const; diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index 13972ad399..5539ca7b86 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -403,6 +403,7 @@ void LLCoprocedurePool::coprocedureInvokerCoro( CoprocQueuePtr pendingCoprocs, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter) { + std::string prevtask; for (;;) { // It is VERY IMPORTANT that we instantiate a new ptr_t just before @@ -424,10 +425,25 @@ void LLCoprocedurePool::coprocedureInvokerCoro( // destroyed during pop_wait_for(). QueuedCoproc::ptr_t coproc; boost::fibers::channel_op_status status; + // Each time control reaches our custom coroutine scheduler, we check + // how long the previous coroutine ran before yielding, and report + // coroutines longer than a certain cutoff. But these coprocedure pool + // coroutines are generic; the only way we know what work they're + // doing is the task 'status' set by LLCoros::setStatus(). But what if + // the coroutine runs the task to completion and returns to waiting? + // It does no good to report that "waiting" ran long. So each time we + // enter "waiting" status, also report the *previous* task name. + std::string waiting = "waiting", newstatus; + if (prevtask.empty()) { - LLCoros::TempStatus st("waiting for work for 10s"); - status = pendingCoprocs->pop_wait_for(coproc, std::chrono::seconds(10)); + newstatus = waiting; } + else + { + newstatus = stringize("done ", prevtask, "; ", waiting); + } + LLCoros::setStatus(newstatus); + status = pendingCoprocs->pop_wait_for(coproc, std::chrono::seconds(10)); if (status == boost::fibers::channel_op_status::closed) { break; @@ -436,6 +452,7 @@ void LLCoprocedurePool::coprocedureInvokerCoro( if(status == boost::fibers::channel_op_status::timeout) { LL_DEBUGS_ONCE("CoProcMgr") << "pool '" << mPoolName << "' waiting." << LL_ENDL; + prevtask.clear(); continue; } // we actually popped an item @@ -446,6 +463,9 @@ void LLCoprocedurePool::coprocedureInvokerCoro( try { + // set "status" of pool coroutine to the name of the coproc task + prevtask = coproc->mName; + LLCoros::setStatus(prevtask); coproc->mProc(httpAdapter, coproc->mId); } catch (const LLCoros::Stop &e) diff --git a/indra/llmessage/llexperiencecache.cpp b/indra/llmessage/llexperiencecache.cpp index b5d0c93376..ea9475ed69 100644 --- a/indra/llmessage/llexperiencecache.cpp +++ b/indra/llmessage/llexperiencecache.cpp @@ -110,9 +110,8 @@ void LLExperienceCache::initSingleton() LLCoprocedureManager::instance().initializePool("ExpCache"); - LLCoros::instance().launch("LLExperienceCache::idleCoro", - boost::bind(&LLExperienceCache::idleCoro, this)); - + const F32 SECS_BETWEEN_REQUESTS = 0.5f; + mExpirationTimerHandle = LL::Timers::instance().scheduleEvery([this]() { expirationTimer(); return false; }, SECS_BETWEEN_REQUESTS); } void LLExperienceCache::cleanup() @@ -125,6 +124,8 @@ void LLExperienceCache::cleanup() cache_stream << (*this); } sShutdown = true; + + LL::Timers::instance().cancel(mExpirationTimerHandle); } //------------------------------------------------------------------------- @@ -392,30 +393,20 @@ void LLExperienceCache::setCapabilityQuery(LLExperienceCache::CapabilityQuery_t } -void LLExperienceCache::idleCoro() +void LLExperienceCache::expirationTimer() { - const F32 SECS_BETWEEN_REQUESTS = 0.5f; + LL_PROFILE_ZONE_SCOPED; const F32 ERASE_EXPIRED_TIMEOUT = 60.f; // seconds - LL_INFOS("ExperienceCache") << "Launching Experience cache idle coro." << LL_ENDL; - do + if (mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) { - if (mEraseExpiredTimer.checkExpirationAndReset(ERASE_EXPIRED_TIMEOUT)) - { - eraseExpired(); - } - - if (!mRequestQueue.empty()) - { - requestExperiences(); - } - - llcoro::suspendUntilTimeout(SECS_BETWEEN_REQUESTS); - - } while (!sShutdown); + eraseExpired(); + } - // The coroutine system will likely be shut down by the time we get to this point - // (or at least no further cycling will occur on it since the user has decided to quit.) + if (!mRequestQueue.empty()) + { + requestExperiences(); + } } void LLExperienceCache::erase(const LLUUID& key) diff --git a/indra/llmessage/llexperiencecache.h b/indra/llmessage/llexperiencecache.h index 81e904107f..b10c24efe4 100644 --- a/indra/llmessage/llexperiencecache.h +++ b/indra/llmessage/llexperiencecache.h @@ -30,10 +30,13 @@ #define LL_LLEXPERIENCECACHE_H #include "linden_common.h" -#include "llsingleton.h" + +#include "llcallbacklist.h" // LL::Timers::handle_t +#include "llcorehttputil.h" #include "llframetimer.h" +#include "llsingleton.h" #include "llsd.h" -#include "llcorehttputil.h" + #include <boost/signals2.hpp> #include <boost/function.hpp> @@ -144,7 +147,9 @@ private: std::string mCacheFileName; static bool sShutdown; // control for coroutines, they exist out of LLExperienceCache's scope, so they need a static control - void idleCoro(); + LL::Timers::handle_t mExpirationTimerHandle; + void expirationTimer(); + void eraseExpired(); void requestExperiencesCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, std::string, RequestQueue_t); void requestExperiences(); diff --git a/indra/llmessage/llpartdata.cpp b/indra/llmessage/llpartdata.cpp index d4cf95c1e3..b626637862 100644 --- a/indra/llmessage/llpartdata.cpp +++ b/indra/llmessage/llpartdata.cpp @@ -331,7 +331,7 @@ bool LLPartSysData::isNullPS(const S32 block_num) S32 size; // Check size of block - size = gMessageSystem->getSize("ObjectData", block_num, "PSBlock"); + size = gMessageSystem->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_PSBlock); if (!size) { @@ -344,7 +344,7 @@ bool LLPartSysData::isNullPS(const S32 block_num) return true; } - gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, size, block_num, PS_MAX_DATA_BLOCK_SIZE); + gMessageSystem->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_PSBlock, ps_data_block, size, block_num, PS_MAX_DATA_BLOCK_SIZE); LLDataPackerBinaryBuffer dp(ps_data_block, size); if (size > PS_LEGACY_DATA_BLOCK_SIZE) @@ -373,7 +373,7 @@ bool LLPartSysData::unpackBlock(const S32 block_num) U8 ps_data_block[PS_MAX_DATA_BLOCK_SIZE]; // Check size of block - S32 size = gMessageSystem->getSize("ObjectData", block_num, "PSBlock"); + S32 size = gMessageSystem->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_PSBlock); if (size > PS_MAX_DATA_BLOCK_SIZE) { @@ -382,7 +382,7 @@ bool LLPartSysData::unpackBlock(const S32 block_num) } // Get from message - gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, size, block_num, PS_MAX_DATA_BLOCK_SIZE); + gMessageSystem->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_PSBlock, ps_data_block, size, block_num, PS_MAX_DATA_BLOCK_SIZE); LLDataPackerBinaryBuffer dp(ps_data_block, size); diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp index d3b80d684f..7ab25908e2 100644 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -1404,3 +1404,4 @@ char const* const _PREHASH_ExperienceID = LLMessageStringTable::getInstance()->g char const* const _PREHASH_LargeGenericMessage = LLMessageStringTable::getInstance()->getString("LargeGenericMessage"); char const* const _PREHASH_GameControlInput = LLMessageStringTable::getInstance()->getString("GameControlInput"); char const* const _PREHASH_AxisData = LLMessageStringTable::getInstance()->getString("AxisData"); +char const* const _PREHASH_MetaData = LLMessageStringTable::getInstance()->getString("MetaData"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index 5449eaf2a5..88dee7f961 100644 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -1405,5 +1405,6 @@ extern char const* const _PREHASH_ExperienceID; extern char const* const _PREHASH_LargeGenericMessage; extern char const* const _PREHASH_GameControlInput; extern char const* const _PREHASH_AxisData; +extern char const* const _PREHASH_MetaData; #endif diff --git a/indra/llmessage/patch_code.cpp b/indra/llmessage/patch_code.cpp index 489b6ce6a6..9f9f4c852a 100644 --- a/indra/llmessage/patch_code.cpp +++ b/indra/llmessage/patch_code.cpp @@ -27,7 +27,6 @@ #include "linden_common.h" #include "llmath.h" -//#include "vmath.h" #include "v3math.h" #include "patch_dct.h" #include "patch_code.h" diff --git a/indra/llmessage/patch_dct.cpp b/indra/llmessage/patch_dct.cpp index 728fe84537..e74e5fd459 100644 --- a/indra/llmessage/patch_dct.cpp +++ b/indra/llmessage/patch_dct.cpp @@ -27,7 +27,6 @@ #include "linden_common.h" #include "llmath.h" -//#include "vmath.h" #include "v3math.h" #include "patch_dct.h" diff --git a/indra/llmessage/patch_idct.cpp b/indra/llmessage/patch_idct.cpp index 5483cf98c0..4bcc439917 100644 --- a/indra/llmessage/patch_idct.cpp +++ b/indra/llmessage/patch_idct.cpp @@ -27,7 +27,6 @@ #include "linden_common.h" #include "llmath.h" -//#include "vmath.h" #include "v3math.h" #include "patch_dct.h" diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index 7fa4230237..b634600de0 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -1073,7 +1073,9 @@ bool LLDAELoader::OpenFile(const std::string& filename) LLModel* mdl = *i; if(mdl->getStatus() != LLModel::NO_ERRORS) { - setLoadState(ERROR_MODEL + mdl->getStatus()) ; + // setLoadState() values >= ERROR_MODEL are reserved to + // report errors with the model itself. + setLoadState(ERROR_MODEL + eLoadState(mdl->getStatus())) ; return false; //abort } diff --git a/indra/llprimitive/llgltfloader.cpp b/indra/llprimitive/llgltfloader.cpp index 480012699a..48249aa5c4 100644 --- a/indra/llprimitive/llgltfloader.cpp +++ b/indra/llprimitive/llgltfloader.cpp @@ -155,7 +155,7 @@ bool LLGLTFLoader::parseMeshes() } else { - setLoadState(ERROR_MODEL + pModel->getStatus()); + setLoadState(ERROR_MODEL + eLoadState(pModel->getStatus())); delete(pModel); return false; } diff --git a/indra/llrender/llfontvertexbuffer.cpp b/indra/llrender/llfontvertexbuffer.cpp index 963aab7121..5bd1ca5eed 100644 --- a/indra/llrender/llfontvertexbuffer.cpp +++ b/indra/llrender/llfontvertexbuffer.cpp @@ -144,6 +144,8 @@ S32 LLFontVertexBuffer::render( || mLastShadow != shadow // ex: buttons change shadow state || mLastScaleX != LLFontGL::sScaleX || mLastScaleY != LLFontGL::sScaleY + || mLastVertDPI != LLFontGL::sVertDPI + || mLastHorizDPI != LLFontGL::sHorizDPI || mLastOrigin != LLFontGL::sCurOrigin) { genBuffers(fontp, text, begin_offset, x, y, color, halign, valign, @@ -196,6 +198,8 @@ void LLFontVertexBuffer::genBuffers( mLastScaleX = LLFontGL::sScaleX; mLastScaleY = LLFontGL::sScaleY; + mLastVertDPI = LLFontGL::sVertDPI; + mLastHorizDPI = LLFontGL::sHorizDPI; mLastOrigin = LLFontGL::sCurOrigin; if (right_x) diff --git a/indra/llrender/llfontvertexbuffer.h b/indra/llrender/llfontvertexbuffer.h index 59cb536b74..af195dfff9 100644 --- a/indra/llrender/llfontvertexbuffer.h +++ b/indra/llrender/llfontvertexbuffer.h @@ -115,6 +115,8 @@ private: // LLFontGL's statics F32 mLastScaleX = 1.f; F32 mLastScaleY = 1.f; + F32 mLastVertDPI = 0.f; + F32 mLastHorizDPI = 0.f; LLCoordGL mLastOrigin; static bool sEnableBufferCollection; diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index b4fe711859..7bc9124ac1 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -65,6 +65,7 @@ bool gDebugSession = false; bool gDebugGLSession = false; +bool gDebugTextureLabelLocalFilesSession = false; bool gClothRipple = false; bool gHeadlessClient = false; bool gNonInteractive = false; @@ -1432,7 +1433,10 @@ void LLGLManager::initExtensions() mHasDebugOutput = mGLVersion >= 4.29f; #if LL_WINDOWS || LL_LINUX - mHasNVXGpuMemoryInfo = ExtensionExists("GL_NVX_gpu_memory_info", gGLHExts.mSysExts); + if( gGLHExts.mSysExts ) + mHasNVXGpuMemoryInfo = ExtensionExists("GL_NVX_gpu_memory_info", gGLHExts.mSysExts); + else + LL_WARNS() << "gGLHExts.mSysExts is not set.?" << LL_ENDL; #endif // Misc @@ -2746,7 +2750,7 @@ void LLGLUserClipPlane::setPlane(F32 a, F32 b, F32 c, F32 d) if(cplane[2] < 0) cplane *= -1; - glm::mat4 suffix; + glm::mat4 suffix = glm::identity<glm::mat4>(); suffix = glm::row(suffix, 2, cplane); glm::mat4 newP = suffix * P; gGL.matrixMode(LLRender::MM_PROJECTION); diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 08cf662526..0e26961588 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -48,6 +48,7 @@ extern bool gDebugGL; extern bool gDebugSession; extern bool gDebugGLSession; +extern bool gDebugTextureLabelLocalFilesSession; extern llofstream gFailLog; #define LL_GL_ERRS LL_ERRS("RenderState") diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 6ba5463acd..bbbce1965e 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -1138,6 +1138,28 @@ S32 LLGLSLShader::bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextu return uniform; } +// For LLImageGL-wrapped textures created via GL elsewhere with our API only. Use with caution. +S32 LLGLSLShader::bindTextureImageGL(S32 uniform, LLImageGL* texture, LLTexUnit::eTextureType mode) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; + + if (uniform < 0 || uniform >= (S32)mTexture.size()) + { + LL_WARNS_ONCE("Shader") << "Uniform index out of bounds. Size: " << (S32)mUniform.size() << " index: " << uniform << LL_ENDL; + llassert(false); + return -1; + } + + uniform = mTexture[uniform]; + + if (uniform > -1) + { + gGL.getTexUnit(uniform)->bind(texture); + } + + return uniform; +} + S32 LLGLSLShader::bindTexture(S32 uniform, LLRenderTarget* texture, bool depth, LLTexUnit::eTextureFilterOptions mode, U32 index) { LL_PROFILE_ZONE_SCOPED_CATEGORY_SHADER; diff --git a/indra/llrender/llglslshader.h b/indra/llrender/llglslshader.h index 2d669c70a9..c24daaf686 100644 --- a/indra/llrender/llglslshader.h +++ b/indra/llrender/llglslshader.h @@ -264,6 +264,8 @@ public: // You can reuse the return value to unbind a texture when required. S32 bindTexture(const std::string& uniform, LLTexture* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); S32 bindTexture(S32 uniform, LLTexture* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); + // For LLImageGL-wrapped textures created via GL elsewhere with our API only. Use with caution. + S32 bindTextureImageGL(S32 uniform, LLImageGL* texture, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); S32 bindTexture(const std::string& uniform, LLRenderTarget* texture, bool depth = false, LLTexUnit::eTextureFilterOptions mode = LLTexUnit::TFO_BILINEAR); S32 bindTexture(S32 uniform, LLRenderTarget* texture, bool depth = false, LLTexUnit::eTextureFilterOptions mode = LLTexUnit::TFO_BILINEAR, U32 index = 0); S32 unbindTexture(const std::string& uniform, LLTexUnit::eTextureType mode = LLTexUnit::TT_TEXTURE); diff --git a/indra/llrender/llgltexture.cpp b/indra/llrender/llgltexture.cpp index 4dcca5a726..c2298d5377 100644 --- a/indra/llrender/llgltexture.cpp +++ b/indra/llrender/llgltexture.cpp @@ -168,6 +168,86 @@ bool LLGLTexture::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, return ret ; } +void LLGLTexture::getGLObjectLabel(std::string& label, bool& error) const +{ + // GL_VERSION_4_3 + if (gGLManager.mGLVersion < 4.29f) + { + error = true; + label.clear(); + return; + } + if (!mGLTexturep) + { + error = true; + label.clear(); + return; + } + LLGLuint texname = mGLTexturep->getTexName(); + if (!texname) + { + error = true; + label.clear(); + return; + } + +#if LL_DARWIN + // apple doesn't support GL after 4.1 so should have hit the above early out, but make the compiler happy here + error = true; + label.clear(); + return; +#else + static GLsizei max_length = 0; + if (max_length == 0) { glGetIntegerv(GL_MAX_LABEL_LENGTH, &max_length); } + static char * clabel = new char[max_length+1]; + GLsizei length; + glGetObjectLabel(GL_TEXTURE, texname, max_length+1, &length, clabel); + error = false; + label.assign(clabel, length); +#endif +} + +std::string LLGLTexture::setGLObjectLabel(const std::string& prefix, bool append_texname) const +{ +#ifndef LL_DARWIN // apple doesn't support GL > 4.1 + if (gGLManager.mGLVersion < 4.29f) { return ""; } // GL_VERSION_4_3 + llassert(mGLTexturep); + if (mGLTexturep) + { + LLGLuint texname = mGLTexturep->getTexName(); + llassert(texname); + if (texname) + { + static GLsizei max_length = 0; + if (max_length == 0) { glGetIntegerv(GL_MAX_LABEL_LENGTH, &max_length); } + + if (append_texname) + { + std::string label_with_texname = prefix + "_" + std::to_string(texname); + label_with_texname.resize(std::min(size_t(max_length), label_with_texname.size())); + glObjectLabel(GL_TEXTURE, texname, (GLsizei)label_with_texname.size(), label_with_texname.c_str()); + return label_with_texname; + } + else + { + if (prefix.size() <= max_length) + { + glObjectLabel(GL_TEXTURE, texname, (GLsizei)prefix.size(), prefix.c_str()); + return prefix; + } + else + { + const std::string label(prefix.c_str(), max_length); + glObjectLabel(GL_TEXTURE, texname, (GLsizei)label.size(), label.c_str()); + return label; + } + } + } + } +#endif + return ""; +} + void LLGLTexture::setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, bool swap_bytes) { llassert(mGLTexturep.notNull()) ; diff --git a/indra/llrender/llgltexture.h b/indra/llrender/llgltexture.h index 122d2a7f9c..22f2ed5131 100644 --- a/indra/llrender/llgltexture.h +++ b/indra/llrender/llgltexture.h @@ -118,6 +118,9 @@ public: LLGLuint getTexName() const ; bool createGLTexture() ; + void getGLObjectLabel(std::string& label, bool& error) const; + std::string setGLObjectLabel(const std::string& prefix, bool append_texname = false) const; + // Create a GL Texture from an image raw // discard_level - mip level, 0 for highest resultion mip // imageraw - the image to copy from diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 67b4ada62f..17c6247670 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -51,6 +51,7 @@ extern LL_COMMON_API bool on_main_thread(); //---------------------------------------------------------------------------- const F32 MIN_TEXTURE_LIFETIME = 10.f; +const F32 CONVERSION_SCRATCH_BUFFER_GL_VERSION = 3.29f; //which power of 2 is i? //assumes i is a power of 2 > 0 @@ -160,6 +161,7 @@ S32 LLImageGL::sMaxCategories = 1 ; bool LLImageGL::sSkipAnalyzeAlpha; U32 LLImageGL::sScratchPBO = 0; U32 LLImageGL::sScratchPBOSize = 0; +U32* LLImageGL::sManualScratch = nullptr; //------------------------ @@ -262,6 +264,22 @@ void LLImageGL::initClass(LLWindow* window, S32 num_catagories, bool skip_analyz } } +void LLImageGL::allocateConversionBuffer() +{ + if (gGLManager.mGLVersion < CONVERSION_SCRATCH_BUFFER_GL_VERSION) + { + try + { + sManualScratch = new U32[MAX_IMAGE_AREA]; + } + catch (std::bad_alloc&) + { + LLError::LLUserWarningMsg::showOutOfMemory(); + LL_ERRS() << "Failed to allocate sManualScratch" << LL_ENDL; + } + } +} + //static void LLImageGL::cleanupClass() { @@ -273,6 +291,8 @@ void LLImageGL::cleanupClass() sScratchPBO = 0; sScratchPBOSize = 0; } + + delete[] sManualScratch; } @@ -1287,11 +1307,10 @@ void LLImageGL::deleteTextures(S32 numTextures, const U32 *textures) void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 width, S32 height, U32 pixformat, U32 pixtype, const void* pixels, bool allow_compression) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - std::unique_ptr<U32[]> scratch; if (LLRender::sGLCoreProfile) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - if (gGLManager.mGLVersion >= 3.29f) + if (gGLManager.mGLVersion >= CONVERSION_SCRATCH_BUFFER_GL_VERSION) { if (pixformat == GL_ALPHA) { //GL_ALPHA is deprecated, convert to RGBA @@ -1323,23 +1342,15 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt { //GL_ALPHA is deprecated, convert to RGBA if (pixels != nullptr) { - scratch.reset(new(std::nothrow) U32[width * height]); - if (!scratch) - { - LLError::LLUserWarningMsg::showOutOfMemory(); - LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) - << " bytes for a manual image W" << width << " H" << height << LL_ENDL; - } - U32 pixel_count = (U32)(width * height); for (U32 i = 0; i < pixel_count; i++) { - U8* pix = (U8*)&scratch[i]; + U8* pix = (U8*)&sManualScratch[i]; pix[0] = pix[1] = pix[2] = 0; pix[3] = ((U8*)pixels)[i]; } - pixels = scratch.get(); + pixels = sManualScratch; } pixformat = GL_RGBA; @@ -1350,26 +1361,18 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt { //GL_LUMINANCE_ALPHA is deprecated, convert to RGBA if (pixels != nullptr) { - scratch.reset(new(std::nothrow) U32[width * height]); - if (!scratch) - { - LLError::LLUserWarningMsg::showOutOfMemory(); - LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) - << " bytes for a manual image W" << width << " H" << height << LL_ENDL; - } - U32 pixel_count = (U32)(width * height); for (U32 i = 0; i < pixel_count; i++) { U8 lum = ((U8*)pixels)[i * 2 + 0]; U8 alpha = ((U8*)pixels)[i * 2 + 1]; - U8* pix = (U8*)&scratch[i]; + U8* pix = (U8*)&sManualScratch[i]; pix[0] = pix[1] = pix[2] = lum; pix[3] = alpha; } - pixels = scratch.get(); + pixels = sManualScratch; } pixformat = GL_RGBA; @@ -1380,25 +1383,17 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt { //GL_LUMINANCE_ALPHA is deprecated, convert to RGB if (pixels != nullptr) { - scratch.reset(new(std::nothrow) U32[width * height]); - if (!scratch) - { - LLError::LLUserWarningMsg::showOutOfMemory(); - LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) - << " bytes for a manual image W" << width << " H" << height << LL_ENDL; - } - U32 pixel_count = (U32)(width * height); for (U32 i = 0; i < pixel_count; i++) { U8 lum = ((U8*)pixels)[i]; - U8* pix = (U8*)&scratch[i]; + U8* pix = (U8*)&sManualScratch[i]; pix[0] = pix[1] = pix[2] = lum; pix[3] = 255; } - pixels = scratch.get(); + pixels = sManualScratch; } pixformat = GL_RGBA; intformat = GL_RGB8; @@ -1777,7 +1772,7 @@ void LLImageGL::syncToMainThread(LLGLuint new_tex_name) ref(); LL::WorkQueue::postMaybe( mMainQueue, - [=]() + [=, this]() { LL_PROFILE_ZONE_NAMED("cglt - delete callback"); syncTexName(new_tex_name); diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index a8b94bd5b0..6b4492c09e 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -298,6 +298,7 @@ public: public: static void initClass(LLWindow* window, S32 num_catagories, bool skip_analyze_alpha = false, bool thread_texture_loads = false, bool thread_media_updates = false); + static void allocateConversionBuffer(); static void cleanupClass() ; private: @@ -305,6 +306,7 @@ private: static bool sSkipAnalyzeAlpha; static U32 sScratchPBO; static U32 sScratchPBOSize; + static U32* sManualScratch; //the flag to allow to call readBackRaw(...). //can be removed if we do not use that function at all. diff --git a/indra/llrender/llrender2dutils.cpp b/indra/llrender/llrender2dutils.cpp index 971241ed05..bec6cb32c4 100644 --- a/indra/llrender/llrender2dutils.cpp +++ b/indra/llrender/llrender2dutils.cpp @@ -1693,10 +1693,10 @@ void gl_segmented_rect_3d_tex(const LLRectf& clip_rect, const LLRectf& center_uv gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV); gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mTop); - gGL.vertex3fv((center_draw_rect.mTop* height_vec).mV); + gGL.vertex3fv((center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop); - gGL.vertex3fv((center_draw_rect.mLeft* width_vec + center_draw_rect.mTop * height_vec).mV); + gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV); gGL.texCoord2f(clip_rect.mLeft, clip_rect.mTop); gGL.vertex3fv((height_vec).mV); diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 6097b09d96..796805e2a5 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1190,8 +1190,9 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("gltf_material_id"); // (GLTF) mReservedUniforms.push_back("terrain_texture_transforms"); // (GLTF) + mReservedUniforms.push_back("terrain_stamp_scale"); - llassert(mReservedUniforms.size() == LLShaderMgr::TERRAIN_TEXTURE_TRANSFORMS +1); + llassert(mReservedUniforms.size() == LLShaderMgr::TERRAIN_STAMP_SCALE +1); mReservedUniforms.push_back("viewport"); diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 1bae0cd8a0..ff07ce454b 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -67,6 +67,7 @@ public: GLTF_MATERIAL_ID, // "gltf_material_id" (GLTF) TERRAIN_TEXTURE_TRANSFORMS, // "terrain_texture_transforms" (GLTF) + TERRAIN_STAMP_SCALE, // "terrain_stamp_scale" VIEWPORT, // "viewport" LIGHT_POSITION, // "light_position" diff --git a/indra/llui/llemojihelper.cpp b/indra/llui/llemojihelper.cpp index b9441a9c91..b2c59ce775 100644 --- a/indra/llui/llemojihelper.cpp +++ b/indra/llui/llemojihelper.cpp @@ -99,6 +99,7 @@ void LLEmojiHelper::showHelper(LLUICtrl* hostctrl_p, S32 local_x, S32 local_y, c LLFloater* pHelperFloater = LLFloaterReg::getInstance(DEFAULT_EMOJI_HELPER_FLOATER); mHelperHandle = pHelperFloater->getHandle(); mHelperCommitConn = pHelperFloater->setCommitCallback(std::bind([&](const LLSD& sdValue) { onCommitEmoji(utf8str_to_wstring(sdValue.asStringRef())[0]); }, std::placeholders::_2)); + mHelperCloseConn = pHelperFloater->setCloseCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCloseHelper(ctrl, param); }); } setHostCtrl(hostctrl_p); mEmojiCommitCb = cb; @@ -148,6 +149,16 @@ void LLEmojiHelper::onCommitEmoji(llwchar emoji) } } +void LLEmojiHelper::onCloseHelper(LLUICtrl* ctrl, const LLSD& param) +{ + mCloseSignal(ctrl, param); +} + +boost::signals2::connection LLEmojiHelper::setCloseCallback(const commit_signal_t::slot_type& cb) +{ + return mCloseSignal.connect(cb); +} + void LLEmojiHelper::setHostCtrl(LLUICtrl* hostctrl_p) { const LLUICtrl* pCurHostCtrl = mHostHandle.get(); diff --git a/indra/llui/llemojihelper.h b/indra/llui/llemojihelper.h index 2834b06061..26840eef94 100644 --- a/indra/llui/llemojihelper.h +++ b/indra/llui/llemojihelper.h @@ -51,16 +51,23 @@ public: // Eventing bool handleKey(const LLUICtrl* ctrl_p, KEY key, MASK mask); void onCommitEmoji(llwchar emoji); + void onCloseHelper(LLUICtrl* ctrl, const LLSD& param); + + typedef boost::signals2::signal<void(LLUICtrl* ctrl, const LLSD& param)> commit_signal_t; + boost::signals2::connection setCloseCallback(const commit_signal_t::slot_type& cb); protected: LLUICtrl* getHostCtrl() const { return mHostHandle.get(); } void setHostCtrl(LLUICtrl* hostctrl_p); private: + commit_signal_t mCloseSignal; + LLHandle<LLUICtrl> mHostHandle; LLHandle<LLFloater> mHelperHandle; boost::signals2::connection mHostCtrlFocusLostConn; boost::signals2::connection mHelperCommitConn; + boost::signals2::connection mHelperCloseConn; std::function<void(llwchar)> mEmojiCommitCb; bool mIsHideDisabled; }; diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index fd07b2ec5d..92059f0753 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -247,6 +247,7 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) mTitle(p.title), mShortTitle(p.short_title), mSingleInstance(p.single_instance), + mIsReuseInitialized(p.reuse_instance.isProvided()), mReuseInstance(p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance), // reuse single-instance floaters by default mKey(key), mCanTearOff(p.can_tear_off), @@ -3350,6 +3351,7 @@ void LLFloater::initFromParams(const LLFloater::Params& p) mHeaderHeight = p.header_height; mLegacyHeaderHeight = p.legacy_header_height; mSingleInstance = p.single_instance; + mIsReuseInitialized = p.reuse_instance.isProvided(); mReuseInstance = p.reuse_instance.isProvided() ? p.reuse_instance : p.single_instance; mDefaultRelativeX = p.rel_x; diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 5bdbcdfcf8..9e1594bdd2 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -377,6 +377,10 @@ public: void enableResizeCtrls(bool enable, bool width = true, bool height = true); bool isPositioning(LLFloaterEnums::EOpenPositioning p) const { return (p == mPositioning); } + + void setAutoFocus(bool focus) { mAutoFocus = focus; } // whether to automatically take focus when opened + bool getAutoFocus() const { return mAutoFocus; } + protected: void applyControlsAndPosition(LLFloater* other); @@ -401,8 +405,6 @@ protected: void setExpandedRect(const LLRect& rect) { mExpandedRect = rect; } // size when not minimized const LLRect& getExpandedRect() const { return mExpandedRect; } - void setAutoFocus(bool focus) { mAutoFocus = focus; } // whether to automatically take focus when opened - bool getAutoFocus() const { return mAutoFocus; } LLDragHandle* getDragHandle() const { return mDragHandle; } void destroy(); // Don't call this directly. You probably want to call closeFloater() diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 66b274c33f..c0abba4358 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -122,6 +122,7 @@ LLLineEditor::Params::Params() LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) : LLUICtrl(p), + mDefaultText(p.default_text), mMaxLengthBytes(p.max_length.bytes), mMaxLengthChars(p.max_length.chars), mCursorPos( 0 ), diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index 7533f76f1d..65f167bc6b 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -202,6 +202,7 @@ public: void setLabel(const LLStringExplicit &new_label) { mLabel = new_label; } const std::string& getLabel() { return mLabel.getString(); } + void setDefaultText() { setText(mDefaultText); } void setText(const LLStringExplicit &new_text); const std::string& getText() const override { return mText.getString(); } @@ -347,6 +348,7 @@ protected: LLFontVertexBuffer mFontBufferSelection; LLFontVertexBuffer mFontBufferPostSelection; LLFontVertexBuffer mFontBufferLabel; + std::string mDefaultText; S32 mMaxLengthBytes; // Max length of the UTF8 string in bytes S32 mMaxLengthChars; // Maximum number of characters in the string S32 mCursorPos; // I-beam is just after the mCursorPos-th character. diff --git a/indra/llui/llluafloater.cpp b/indra/llui/llluafloater.cpp index ccdadc6ae0..91c0cfeec9 100644 --- a/indra/llui/llluafloater.cpp +++ b/indra/llui/llluafloater.cpp @@ -301,11 +301,11 @@ void LLLuaFloater::postEvent(LLSD data, const std::string &event_name) void LLLuaFloater::showLuaFloater(const LLSD &data) { fsyspath fs_path(data["xml_path"].asString()); - std::string path = fs_path.lexically_normal().u8string(); + fsyspath path = fs_path.lexically_normal(); if (!fs_path.is_absolute()) { std::string lib_path = gDirUtilp->getExpandedFilename(LL_PATH_SCRIPTS, "lua"); - path = (fsyspath(lib_path) / path).u8string(); + path = fsyspath(fsyspath(lib_path) / path); } LLLuaFloater *floater = new LLLuaFloater(data); diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index ef0762fc17..eca13cbb3c 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -94,6 +94,7 @@ #include "llinitparam.h" #include "llinstancetracker.h" #include "llmortician.h" +#include "llmutex.h" #include "llnotificationptr.h" #include "llpointer.h" #include "llrefcount.h" diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index ddd1d81cb4..3ed328e37f 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -423,6 +423,19 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const return ret; } +std::vector<LLSD> LLScrollListCtrl::getAllSelectedValues() const +{ + std::vector<LLSD> ret; + for (LLScrollListItem* item : mItemList) + { + if (item->getSelected()) + { + ret.push_back(item->getValue()); + } + } + return ret; +} + S32 LLScrollListCtrl::getNumSelected() const { S32 numSelected = 0; @@ -1105,38 +1118,32 @@ S32 LLScrollListCtrl::getItemIndex( const LLUUID& target_id ) const void LLScrollListCtrl::selectPrevItem( bool extend_selection) { + updateSort(); + LLScrollListItem* prev_item = NULL; - if (!getFirstSelected()) - { - // select last item - selectNthItem(getItemCount() - 1); - } - else + for (LLScrollListItem* item : mItemList) { - updateSort(); - - item_list::iterator iter; - for (LLScrollListItem* cur_item : mItemList) + if (item->getSelected()) { - if (cur_item->getSelected()) - { - if (prev_item) - { - selectItem(prev_item, cur_item->getSelectedCell(), !extend_selection); - } - else - { - reportInvalidInput(); - } - break; - } + break; + } - // don't allow navigation to disabled elements - prev_item = cur_item->getEnabled() ? cur_item : prev_item; + // don't allow navigation to disabled elements + if (item->getEnabled()) + { + prev_item = item; } } + if (!prev_item) + { + reportInvalidInput(); + return; + } + + selectItem(prev_item, prev_item->getSelectedCell(), !extend_selection); + if ((mCommitOnSelectionChange || mCommitOnKeyboardMovement)) { commitIfChanged(); @@ -1147,36 +1154,41 @@ void LLScrollListCtrl::selectPrevItem( bool extend_selection) void LLScrollListCtrl::selectNextItem( bool extend_selection) { + updateSort(); + + LLScrollListItem* current_item = NULL; LLScrollListItem* next_item = NULL; - if (!getFirstSelected()) - { - selectFirstItem(); - } - else + for (LLScrollListItem* item : mItemList) { - updateSort(); - - for (LLScrollListItem* cur_item : mItemList) + if (current_item) { - if (cur_item->getSelected()) + if (item->getEnabled()) { - if (next_item) - { - selectItem(next_item, cur_item->getSelectedCell(), !extend_selection); - } - else - { - reportInvalidInput(); - } + next_item = item; break; } - - // don't allow navigation to disabled items - next_item = cur_item->getEnabled() ? cur_item : next_item; + } + else if (item->getSelected()) + { + current_item = item; + next_item = NULL; + continue; + } + else if (!next_item && item->getEnabled()) + { + next_item = item; } } + if (!next_item) + { + reportInvalidInput(); + return; + } + + selectItem(next_item, next_item->getSelectedCell(), !extend_selection); + if (mCommitOnKeyboardMovement) { onCommit(); @@ -1511,7 +1523,7 @@ bool LLScrollListCtrl::setSelectedByValue(const LLSD& value, bool selected) { if (selected) { - selectItem(item, -1); + selectItem(item, -1, !mAllowMultipleSelection); } else { diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index bfae08ab5b..badaf31657 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -284,6 +284,7 @@ public: LLScrollListItem* getFirstSelected() const; virtual S32 getFirstSelectedIndex() const; std::vector<LLScrollListItem*> getAllSelected() const; + std::vector<LLSD> getAllSelectedValues() const; S32 getNumSelected() const; LLScrollListItem* getLastSelectedItem() const { return mLastSelected; } diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index ecac800def..81959f1542 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -1211,6 +1211,14 @@ void LLTextEditor::showEmojiHelper() LLEmojiHelper::instance().showHelper(this, cursorRect.mLeft, cursorRect.mTop, LLStringUtil::null, cb); } +void LLTextEditor::hideEmojiHelper() +{ + if (mShowEmojiHelper) + { + LLEmojiHelper::instance().hideHelper(this); + } +} + void LLTextEditor::tryToShowEmojiHelper() { if (mReadOnly || !mShowEmojiHelper) @@ -1594,8 +1602,14 @@ void LLTextEditor::cleanStringForPaste(LLWString & clean_string) } } -void LLTextEditor::pasteTextWithLinebreaksImpl(const LLWString & clean_string) +void LLTextEditor::pasteTextWithLinebreaksImpl(const LLWString & clean_string, bool reset_cursor) { + if (reset_cursor) + { + deselect(); + setCursorPos(getLength()); + } + std::basic_string<llwchar>::size_type start = 0; std::basic_string<llwchar>::size_type pos = clean_string.find('\n',start); diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index cedb79bf62..0df2f6b38a 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -207,6 +207,7 @@ public: bool getShowContextMenu() const { return mShowContextMenu; } void showEmojiHelper(); + void hideEmojiHelper(); void setShowEmojiHelper(bool show); bool getShowEmojiHelper() const { return mShowEmojiHelper; } @@ -307,14 +308,13 @@ private: public: template <typename STRINGTYPE> - void pasteTextWithLinebreaks(const STRINGTYPE& clean_string) + void pasteTextWithLinebreaks(const STRINGTYPE& clean_string, bool reset_cursor = false) { - pasteTextWithLinebreaksImpl(ll_convert(clean_string)); + pasteTextWithLinebreaksImpl(ll_convert(clean_string), reset_cursor); } - void pasteTextWithLinebreaksImpl(const LLWString& clean_string); + void pasteTextWithLinebreaksImpl(const LLWString& clean_string, bool reset_cursor = false); private: - void pasteTextWithLinebreaksInternal(const LLWString & clean_string); void onKeyStroke(); // Concrete TextCmd sub-classes used by the LLTextEditor base class diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 0daa767766..e02bf5919b 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -45,6 +45,10 @@ static int16_t PLAYOUT_DEVICE_BAD = -2; static int16_t RECORD_DEVICE_DEFAULT = -1; static int16_t RECORD_DEVICE_BAD = -2; +static const std::string DEFAULT_DEVICE_NAME = "Default"; +static const std::string NO_DEVICE_NAME = "No Device"; +static const std::string NO_DEVICE_GUID; + LLAudioDeviceObserver::LLAudioDeviceObserver() : mSumVector {0}, mMicrophoneEnergy(0.0) {} float LLAudioDeviceObserver::getMicrophoneEnergy() { return mMicrophoneEnergy; } @@ -242,10 +246,10 @@ void LLWebRTCImpl::init() apm_config.gain_controller1.enabled = false; apm_config.gain_controller1.mode = webrtc::AudioProcessing::Config::GainController1::kAdaptiveAnalog; apm_config.gain_controller2.enabled = false; - apm_config.high_pass_filter.enabled = true; + apm_config.high_pass_filter.enabled = false; apm_config.noise_suppression.enabled = true; apm_config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kVeryHigh; - apm_config.transient_suppression.enabled = true; + apm_config.transient_suppression.enabled = false; apm_config.pipeline.multi_channel_render = true; apm_config.pipeline.multi_channel_capture = false; @@ -358,14 +362,13 @@ void LLWebRTCImpl::setAudioConfig(LLWebRTCDeviceInterface::AudioConfig config) { webrtc::AudioProcessing::Config apm_config; apm_config.echo_canceller.enabled = config.mEchoCancellation; - apm_config.echo_canceller.mobile_mode = false; + apm_config.echo_canceller.mobile_mode = false; // don't use mobile hardware echo cancellation. apm_config.gain_controller1.enabled = config.mAGC; apm_config.gain_controller1.mode = webrtc::AudioProcessing::Config::GainController1::kAdaptiveAnalog; - apm_config.gain_controller2.enabled = false; - apm_config.high_pass_filter.enabled = true; - apm_config.transient_suppression.enabled = true; - apm_config.pipeline.multi_channel_render = true; - apm_config.pipeline.multi_channel_capture = true; + apm_config.gain_controller2.enabled = false; // use the main gain controller. + apm_config.high_pass_filter.enabled = false; // don't filter, to improve quality for music and other pure sources. + apm_config.transient_suppression.enabled = false; // transient suppression may increase latency. + apm_config.pipeline.multi_channel_render = true; // stereo apm_config.pipeline.multi_channel_capture = true; switch (config.mNoiseSuppressionLevel) @@ -438,7 +441,7 @@ void ll_set_device_module_capture_device(rtc::scoped_refptr<webrtc::AudioDeviceM void LLWebRTCImpl::setCaptureDevice(const std::string &id) { int16_t recordingDevice = RECORD_DEVICE_DEFAULT; - if (id != "Default") + if (id != DEFAULT_DEVICE_NAME) { for (int16_t i = 0; i < mRecordingDeviceList.size(); i++) { @@ -502,7 +505,7 @@ void ll_set_device_module_render_device(rtc::scoped_refptr<webrtc::AudioDeviceMo void LLWebRTCImpl::setRenderDevice(const std::string &id) { int16_t playoutDevice = PLAYOUT_DEVICE_DEFAULT; - if (id != "Default") + if (id != DEFAULT_DEVICE_NAME) { for (int16_t i = 0; i < mPlayoutDeviceList.size(); i++) { @@ -546,6 +549,16 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id) } } +bool LLWebRTCImpl::isCaptureNoDevice() +{ + return mRecordingDevice == mRecordingNoDevice; +} + +bool LLWebRTCImpl::isRenderNoDevice() +{ + return mPlayoutDevice == mPlayoutNoDevice; +} + // updateDevices needs to happen on the worker thread. void LLWebRTCImpl::updateDevices() { @@ -566,6 +579,11 @@ void LLWebRTCImpl::updateDevices() mTuningDeviceModule->PlayoutDeviceName(index, name, guid); mPlayoutDeviceList.emplace_back(name, guid); } + mPlayoutNoDevice = (int32_t)mPlayoutDeviceList.size(); + if (mPlayoutNoDevice) + { + mPlayoutDeviceList.emplace_back(NO_DEVICE_NAME, NO_DEVICE_GUID); + } int16_t captureDeviceCount = mTuningDeviceModule->RecordingDevices(); @@ -584,6 +602,11 @@ void LLWebRTCImpl::updateDevices() mTuningDeviceModule->RecordingDeviceName(index, name, guid); mRecordingDeviceList.emplace_back(name, guid); } + mRecordingNoDevice = (int32_t)mRecordingDeviceList.size(); + if (mRecordingNoDevice) + { + mRecordingDeviceList.emplace_back(NO_DEVICE_NAME, NO_DEVICE_GUID); + } for (auto &observer : mVoiceDevicesObserverList) { @@ -726,7 +749,7 @@ void LLWebRTCPeerConnectionImpl::init(LLWebRTCImpl * webrtc_impl) } void LLWebRTCPeerConnectionImpl::terminate() { - mWebRTCImpl->SignalingBlockingCall( + mWebRTCImpl->PostSignalingTask( [this]() { if (mPeerConnection) @@ -851,6 +874,13 @@ bool LLWebRTCPeerConnectionImpl::initializeConnection(const LLWebRTCPeerConnecti codecparam.parameters["stereo"] = "1"; codecparam.parameters["sprop-stereo"] = "1"; params.codecs.push_back(codecparam); + + // Fixed bitrates result in lower CPU cost + for (auto&& encoding : params.encodings) + { + encoding.max_bitrate_bps = 64000; + encoding.min_bitrate_bps = 64000; + } sender->SetParameters(params); } @@ -933,20 +963,20 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp) void LLWebRTCPeerConnectionImpl::setMute(bool mute) { mMute = mute; + mute |= mWebRTCImpl->isCaptureNoDevice(); mWebRTCImpl->PostSignalingTask( - [this]() + [this, mute]() { if (mPeerConnection) { auto senders = mPeerConnection->GetSenders(); - RTC_LOG(LS_INFO) << __FUNCTION__ << (mMute ? "disabling" : "enabling") << " streams count " << senders.size(); + RTC_LOG(LS_INFO) << __FUNCTION__ << (mute ? "disabling" : "enabling") << " streams count " << senders.size(); for (auto &sender : senders) { - auto track = sender->track(); - if (track) + if (auto track = sender->track()) { - track->set_enabled(!mMute); + track->set_enabled(!mute); } } } @@ -960,6 +990,11 @@ void LLWebRTCPeerConnectionImpl::resetMute() void LLWebRTCPeerConnectionImpl::setReceiveVolume(float volume) { + if (mWebRTCImpl->isRenderNoDevice()) + { + volume = 0; + } + mWebRTCImpl->PostSignalingTask( [this, volume]() { @@ -1202,7 +1237,7 @@ void LLWebRTCPeerConnectionImpl::OnSuccess(webrtc::SessionDescriptionInterface * else if (sdp_line.find("a=fmtp:" + opus_payload) == 0) { sdp_mangled_stream << sdp_line << "a=fmtp:" << opus_payload - << " minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1;maxplaybackrate=48000;sprop-maxplaybackrate=48000;sprop-maxcapturerate=48000\n"; + << " minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1;maxplaybackrate=48000;sprop-maxplaybackrate=48000;sprop-maxcapturerate=48000;complexity=4\n"; } else { diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index c6fdb909dd..2deaba9e58 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -151,6 +151,9 @@ class LLWebRTCDeviceInterface virtual void setCaptureDevice(const std::string& id) = 0; virtual void setRenderDevice(const std::string& id) = 0; + virtual bool isCaptureNoDevice() = 0; + virtual bool isRenderNoDevice() = 0; + // Device observers for device change callbacks. virtual void setDevicesObserver(LLWebRTCDevicesObserver *observer) = 0; virtual void unsetDevicesObserver(LLWebRTCDevicesObserver *observer) = 0; diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index b93a1fdb01..27b7eae8e7 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -210,6 +210,9 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS void setCaptureDevice(const std::string& id) override; void setRenderDevice(const std::string& id) override; + bool isCaptureNoDevice() override; + bool isRenderNoDevice() override; + void setTuningMode(bool enable) override; float getTuningAudioLevel() override; float getPeerConnectionAudioLevel() override; @@ -306,9 +309,11 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS // accessors in native webrtc for devices aren't apparently implemented yet. bool mTuningMode; int32_t mRecordingDevice; + int32_t mRecordingNoDevice; LLWebRTCVoiceDeviceList mRecordingDeviceList; int32_t mPlayoutDevice; + int32_t mPlayoutNoDevice; LLWebRTCVoiceDeviceList mPlayoutDeviceList; bool mMute; diff --git a/indra/llwindow/llgamecontrol.cpp b/indra/llwindow/llgamecontrol.cpp index 9d3c854ca2..67847600e7 100644 --- a/indra/llwindow/llgamecontrol.cpp +++ b/indra/llwindow/llgamecontrol.cpp @@ -1530,6 +1530,9 @@ void LLGameControl::init(const std::string& gamecontrollerdb_path, llassert(saveObject); llassert(updateUI); +#ifndef LL_DARWIN + // SDL2 is temporarily disabled on Mac, so this needs to be a no-op on that platform + int result = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_SENSOR); if (result < 0) { @@ -1555,6 +1558,7 @@ void LLGameControl::init(const std::string& gamecontrollerdb_path, LL_INFOS("SDL2") << "Total " << count << " mappings added from " << gamecontrollerdb_path << LL_ENDL; } } +#endif // LL_DARWIN g_gameControl = LLGameControl::getInstance(); @@ -1614,6 +1618,9 @@ void LLGameControl::clearAllStates() // static void LLGameControl::processEvents(bool app_has_focus) { +#ifndef LL_DARWIN + // SDL2 is temporarily disabled on Mac, so this needs to be a no-op on that platform + // This method used by non-linux platforms which only use SDL for GameController input SDL_Event event; if (!app_has_focus) @@ -1631,6 +1638,7 @@ void LLGameControl::processEvents(bool app_has_focus) { handleEvent(event, app_has_focus); } +#endif // LL_DARWIN } void LLGameControl::handleEvent(const SDL_Event& event, bool app_has_focus) diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index 93ac58ca6f..066d29c624 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -416,7 +416,11 @@ LLWindow* LLWindowManager::createWindow( if (use_gl) { +#ifndef LL_DARWIN + // SDL2 is temporarily disabled on Mac init_sdl(); +#endif + #if LL_WINDOWS new_window = new LLWindowWin32(callbacks, title, name, x, y, width, height, flags, diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 1883c6c9c1..b90e85d911 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -1037,7 +1037,7 @@ F32 LLWindowMacOSX::getGamma() const &greenGamma, &blueMin, &blueMax, - &blueGamma) == noErr) + &blueGamma) == static_cast<CGError>(noErr)) { // So many choices... // Let's just return the green channel gamma for now. @@ -1088,7 +1088,7 @@ bool LLWindowMacOSX::setGamma(const F32 gamma) &greenGamma, &blueMin, &blueMax, - &blueGamma) != noErr) + &blueGamma) != static_cast<CGError>(noErr)) { return false; } @@ -1103,7 +1103,7 @@ bool LLWindowMacOSX::setGamma(const F32 gamma) gamma, blueMin, blueMax, - gamma) != noErr) + gamma) != static_cast<CGError>(noErr)) { return false; } @@ -1155,7 +1155,7 @@ bool LLWindowMacOSX::setCursorPosition(const LLCoordWindow position) newPosition.y = screen_pos.mY; CGSetLocalEventsSuppressionInterval(0.0); - if(CGWarpMouseCursorPosition(newPosition) == noErr) + if(CGWarpMouseCursorPosition(newPosition) == static_cast<CGError>(noErr)) { result = true; } diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index ea95a5aa2e..68704e65bb 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -50,10 +50,10 @@ extern "C" { #if LL_LINUX // not necessarily available on random SDL platforms, so #if LL_LINUX // for execv(), waitpid(), fork() -# include <unistd.h> -# include <sys/types.h> -# include <sys/wait.h> -# include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <stdio.h> #endif // LL_LINUX extern bool gDebugWindowProc; @@ -64,52 +64,172 @@ const S32 MAX_NUM_RESOLUTIONS = 200; // LLWindowSDL // -#if LL_X11 -# include <X11/Xutil.h> -#endif //LL_X11 +#include <X11/Xutil.h> // TOFU HACK -- (*exactly* the same hack as LLWindowMacOSX for a similar // set of reasons): Stash a pointer to the LLWindowSDL object here and // maintain in the constructor and destructor. This assumes that there will // be only one object of this class at any time. Currently this is true. -static LLWindowSDL *gWindowImplementation = NULL; - +static LLWindowSDL *gWindowImplementation = nullptr; void maybe_lock_display(void) { - if (gWindowImplementation && gWindowImplementation->Lock_Display) { + if (gWindowImplementation && gWindowImplementation->Lock_Display) gWindowImplementation->Lock_Display(); - } } - void maybe_unlock_display(void) { - if (gWindowImplementation && gWindowImplementation->Unlock_Display) { + if (gWindowImplementation && gWindowImplementation->Unlock_Display) gWindowImplementation->Unlock_Display(); - } } -#if LL_X11 -// static Window LLWindowSDL::get_SDL_XWindowID(void) { - if (gWindowImplementation) { - return gWindowImplementation->mSDL_XWindowID; - } + if (gWindowImplementation) + return gWindowImplementation->mX11Data.mXWindowID; return None; } -//static Display* LLWindowSDL::get_SDL_Display(void) { - if (gWindowImplementation) { - return gWindowImplementation->mSDL_Display; + if (gWindowImplementation) + return gWindowImplementation->mX11Data.mDisplay; + return nullptr; +} + +/* + * In wayland a window does not have a state of "minimized" or gets messages that it got minimized [1] + * There's two ways to approach this challenge: + * 1) Ignore it, this would mean even if minimized/not visible the viewer will always fun at full fps + * 2) Try to detect something like "minimized", the best way I found so far is to as for frame_done events. Those will + * only happen if parts of the viewer are visible. As such it is not the same a "minimized" but rather "this window + * is not fully hidden behind another window or minimized". That's (I guess) still better than nothing and running + * full tilt even if hidden. + * + * [1] As of 2024-09, maybe in the future we get nice things +*/ +#ifdef LL_WAYLAND +#include <wayland-client-protocol.h> +#include <dlfcn.h> + +uint32_t (*ll_wl_proxy_get_version)(struct wl_proxy *proxy); +void (*ll_wl_proxy_destroy)(struct wl_proxy *proxy); +int (*ll_wl_proxy_add_listener)(struct wl_proxy *proxy, void (**implementation)(void), void *data); + +wl_interface *ll_wl_callback_interface; + +int ll_wl_callback_add_listener(struct wl_callback *wl_callback, + const struct wl_callback_listener *listener, void *data) +{ + return ll_wl_proxy_add_listener((struct wl_proxy *) wl_callback, + (void (**)(void)) listener, data); +} + +struct wl_proxy* (*ll_wl_proxy_marshal_flags)(struct wl_proxy *proxy, uint32_t opcode, + const struct wl_interface *interface, + uint32_t version, + uint32_t flags, ...); + + +bool loadWaylandClient() +{ + auto *pSO = dlopen( "libwayland-client.so.0", RTLD_NOW); + if( !pSO ) + return false; + + + ll_wl_callback_interface = (wl_interface*)dlsym(pSO, "wl_callback_interface"); + *(void**)&ll_wl_proxy_marshal_flags = dlsym(pSO, "wl_proxy_marshal_flags"); + *(void**)&ll_wl_proxy_get_version = dlsym(pSO, "wl_proxy_get_version"); + *(void**)&ll_wl_proxy_destroy = dlsym(pSO, "wl_proxy_destroy"); + *(void**)&ll_wl_proxy_add_listener = dlsym(pSO, "wl_proxy_add_listener"); + + return ll_wl_callback_interface != nullptr && ll_wl_proxy_marshal_flags != nullptr && + ll_wl_proxy_get_version != nullptr && ll_wl_proxy_destroy != nullptr && + ll_wl_proxy_add_listener != nullptr; +} + +struct wl_callback* ll_wl_surface_frame(struct wl_surface *wl_surface) +{ + auto version = ll_wl_proxy_get_version((struct wl_proxy *) wl_surface); + + auto callback = ll_wl_proxy_marshal_flags((struct wl_proxy *) wl_surface, + WL_SURFACE_FRAME, ll_wl_callback_interface, version, + 0, nullptr); + + return (struct wl_callback *) callback; +} + +void ll_wl_callback_destroy(struct wl_callback *wl_callback) +{ + ll_wl_proxy_destroy((struct wl_proxy *) wl_callback); +} + +void LLWindowSDL::waylandFrameDoneCB(void *data, struct wl_callback *cb, uint32_t time) +{ + static uint32_t frame_count {0}; + static F64SecondsImplicit lastInfo{0}; + + ++frame_count; + + ll_wl_callback_destroy(cb); + auto pThis = reinterpret_cast<LLWindowSDL*>(data); + pThis->mWaylandData.mLastFrameEvent = LLTimer::getTotalTime(); + + auto now = LLTimer::getElapsedSeconds(); + if( lastInfo > 0) + { + auto diff = now.value() - lastInfo.value(); + constexpr double FPS_INFO_INTERVAL = 60.f * 5.f; + if( diff >= FPS_INFO_INTERVAL) + { + double fFPS = frame_count; + fFPS /= diff; + LL_INFOS() << "Wayland: FPS " << fFPS << " fps, " << frame_count << " #frames time " << (diff) << LL_ENDL; + frame_count = 0; + lastInfo = now; + } } - return NULL; + else + lastInfo = now; + + pThis->setupWaylandFrameCallback(); // ask for a new frame } -#endif // LL_X11 + +bool LLWindowSDL::isWaylandWindowNotDrawing() const +{ + if(!mWaylandLoaded || Wayland != mServerProtocol || mWaylandData.mLastFrameEvent == 0) + return false; + + auto currentTime = LLTimer::getTotalTime(); + if( (currentTime - mWaylandData.mLastFrameEvent) > 250000 ) + return true; + + return false; +} + +void LLWindowSDL::setupWaylandFrameCallback() +{ + if(!mWaylandLoaded) return; + + static wl_callback_listener frame_listener { nullptr }; + frame_listener.done = &LLWindowSDL::waylandFrameDoneCB; + + auto cb = ll_wl_surface_frame(mWaylandData.mSurface); + ll_wl_callback_add_listener(cb, &frame_listener, this); +} +#else +bool LLWindowSDL::isWaylandWindowNotDrawing() +{ + return false; +} +void LLWindowSDL::setupWaylandFrameCallback() +{ + LL_WARNS() << "Viewer is running under Wayland, but was not compiled with full wayland support!" << LL_ENDL; +} +#endif LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, @@ -118,29 +238,12 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, bool enable_vsync, bool use_gl, bool ignore_pixel_depth, U32 fsaa_samples) : LLWindow(callbacks, fullscreen, flags), - Lock_Display(NULL), - Unlock_Display(NULL), mGamma(1.0f) + Lock_Display(nullptr), + Unlock_Display(nullptr), mGamma(1.0f) { // Initialize the keyboard gKeyboard = new LLKeyboardSDL(); gKeyboard->setCallbacks(callbacks); - // Note that we can't set up key-repeat until after SDL has init'd video - - // Ignore use_gl for now, only used for drones on PC - mWindow = NULL; - mContext = {}; - mNeedsResize = false; - mOverrideAspectRatio = 0.f; - mGrabbyKeyFlags = 0; - mReallyCapturedCount = 0; - mHaveInputFocus = -1; - mIsMinimized = -1; - mFSAASamples = fsaa_samples; - -#if LL_X11 - mSDL_XWindowID = None; - mSDL_Display = nullptr; -#endif // LL_X11 // Assume 4:3 aspect ratio until we know better mOriginalAspectRatio = 1024.0 / 768.0; @@ -167,8 +270,6 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, mFlashing = false; - mKeyVirtualKey = 0; - mKeyModifiers = KMOD_NONE; } static SDL_Surface *Load_BMP_Resource(const char *basename) @@ -197,7 +298,7 @@ void LLWindowSDL::tryFindFullscreenSize( int &width, int &height ) LL_INFOS() << "createContext: setting up fullscreen " << width << "x" << height << LL_ENDL; // If the requested width or height is 0, find the best default for the monitor. - if((width == 0) || (height == 0)) + if(width == 0 || height == 0) { // Scan through the list of modes, looking for one which has: // height between 700 and 800 @@ -205,16 +306,15 @@ void LLWindowSDL::tryFindFullscreenSize( int &width, int &height ) S32 resolutionCount = 0; LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount); - if(resolutionList != NULL) + if(resolutionList != nullptr) { F32 closestAspect = 0; U32 closestHeight = 0; U32 closestWidth = 0; - int i; LL_INFOS() << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << LL_ENDL; - for(i=0; i < resolutionCount; i++) + for(S32 i=0; i < resolutionCount; i++) { F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight; @@ -237,7 +337,7 @@ void LLWindowSDL::tryFindFullscreenSize( int &width, int &height ) } } - if((width == 0) || (height == 0)) + if(width == 0 || height == 0) { // Mode search failed for some reason. Use the old-school default. width = 1024; @@ -247,16 +347,10 @@ void LLWindowSDL::tryFindFullscreenSize( int &width, int &height ) bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool enable_vsync) { - if (width == 0) - width = 1024; - if (height == 0) - width = 768; - LL_INFOS() << "createContext, fullscreen=" << fullscreen << - " size=" << width << "x" << height << LL_ENDL; + LL_INFOS() << "createContext, fullscreen=" << fullscreen << " size=" << width << "x" << height << LL_ENDL; // captures don't survive contexts mGrabbyKeyFlags = 0; - mReallyCapturedCount = 0; if (width == 0) width = 1024; @@ -269,15 +363,6 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b mFullscreen = fullscreen; - int sdlflags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; - - if( mFullscreen ) - { - sdlflags |= SDL_WINDOW_FULLSCREEN; - tryFindFullscreenSize( width, height ); - } - - mSDLFlags = sdlflags; // Setup default backing colors GLint redBits{8}, greenBits{8}, blueBits{8}, alphaBits{8}; @@ -300,13 +385,19 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, context_flags); SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - // Create the window - mWindow = SDL_CreateWindow(mWindowTitle.c_str(), x, y, width, height, mSDLFlags); + int sdlflags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; + + if( mFullscreen ) + { + sdlflags |= SDL_WINDOW_FULLSCREEN; + tryFindFullscreenSize( width, height ); + } + + mWindow = SDL_CreateWindow( mWindowTitle.c_str(), x, y, width, height, sdlflags ); if (mWindow == nullptr) { LL_WARNS() << "Window creation failure. SDL: " << SDL_GetError() << LL_ENDL; setupFailure("Window creation error", "Error", OSMB_OK); - return false; } // Create the context @@ -314,15 +405,15 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b if(!mContext) { LL_WARNS() << "Cannot create GL context " << SDL_GetError() << LL_ENDL; + close(); setupFailure("GL Context creation error", "Error", OSMB_OK); - return false; } if (SDL_GL_MakeCurrent(mWindow, mContext) != 0) { LL_WARNS() << "Failed to make context current. SDL: " << SDL_GetError() << LL_ENDL; + close(); setupFailure("GL Context failed to set current failure", "Error", OSMB_OK); - return false; } mSurface = SDL_GetWindowSurface(mWindow); @@ -345,7 +436,7 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b else { LL_WARNS() << "createContext: fullscreen creation failure. SDL: " << SDL_GetError() << LL_ENDL; - // No fullscreen support + mFullscreen = false; mFullscreenWidth = -1; mFullscreenHeight = -1; @@ -353,17 +444,7 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b mFullscreenRefresh = -1; std::string error = llformat("Unable to run fullscreen at %d x %d.\nRunning in window.", width, height); - OSMessageBox(error, "Error", OSMB_OK); - return false; - } - } - else - { - if (!mWindow) - { - LL_WARNS() << "createContext: window creation failure. SDL: " << SDL_GetError() << LL_ENDL; - setupFailure("Window creation error", "Error", OSMB_OK); - return false; + setupFailure( error, "Error", OSMB_OK ); } } @@ -383,9 +464,6 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b LL_INFOS() << " Stencil Bits " << S32(stencilBits) << LL_ENDL; GLint colorBits = redBits + greenBits + blueBits + alphaBits; - // fixme: actually, it's REALLY important for picking that we get at - // least 8 bits each of red,green,blue. Alpha we can be a bit more - // relaxed about if we have to. if (colorBits < 32) { close(); @@ -397,7 +475,6 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b "will automatically adjust the screen each time it runs.", "Error", OSMB_OK); - return false; } LL_PROFILER_GPU_CONTEXT; @@ -411,33 +488,53 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b { SDL_SetWindowIcon(mWindow, bmpsurface); SDL_FreeSurface(bmpsurface); - bmpsurface = NULL; + bmpsurface = nullptr; } -#if LL_X11 /* Grab the window manager specific information */ SDL_SysWMinfo info; SDL_VERSION(&info.version); if ( SDL_GetWindowWMInfo(mWindow, &info) ) { /* Save the information for later use */ - if ( info.subsystem == SDL_SYSWM_X11 ) + if (info.subsystem == SDL_SYSWM_X11) { - mSDL_Display = info.info.x11.display; - mSDL_XWindowID = info.info.x11.window; + mX11Data.mDisplay = info.info.x11.display; + mX11Data.mXWindowID = info.info.x11.window; + mServerProtocol = X11; + LL_INFOS() << "Running under X11" << LL_ENDL; + } + else if (info.subsystem == SDL_SYSWM_WAYLAND) + { +#ifdef LL_WAYLAND + mWaylandLoaded = loadWaylandClient(); + if(!mWaylandLoaded) + { + LL_WARNS() << "Failed to load wayland-client.so or grab required functions" << LL_ENDL; + } +#endif + + mWaylandData.mSurface = info.info.wl.surface; + mServerProtocol = Wayland; + + setupWaylandFrameCallback(); + + // If set (XWayland) remove DISPLAY, this will prompt dullahan to also use Wayland + if( getenv("DISPLAY") ) + unsetenv("DISPLAY"); + + LL_INFOS() << "Running under Wayland" << LL_ENDL; + LL_WARNS() << "Be aware that with at least SDL2 the window will not receive minimizing events, thus minimized state can only be estimated." << LL_ENDL; } else { - LL_WARNS() << "We're not running under X11? Wild." - << LL_ENDL; + LL_WARNS() << "Unsupported windowing system" << LL_ENDL; } } else { - LL_WARNS() << "We're not running under any known WM. Wild." - << LL_ENDL; + LL_WARNS() << "We're not running under any known WM. Wild." << LL_ENDL; } -#endif // LL_X11 SDL_StartTextInput(); //make sure multisampling is disabled by default @@ -523,12 +620,11 @@ void LLWindowSDL::destroyContext() LL_INFOS() << "shutdownGL begins" << LL_ENDL; gGLManager.shutdownGL(); -#if LL_X11 - mSDL_Display = NULL; - mSDL_XWindowID = None; - Lock_Display = NULL; - Unlock_Display = NULL; -#endif // LL_X11 + mX11Data.mDisplay = nullptr; + mX11Data.mXWindowID = None; + Lock_Display = nullptr; + Unlock_Display = nullptr; + mServerProtocol = Unknown; LL_INFOS() << "Destroying SDL cursors" << LL_ENDL; quitCursors(); @@ -564,12 +660,9 @@ LLWindowSDL::~LLWindowSDL() { destroyContext(); - if(mSupportedResolutions != NULL) - { - delete []mSupportedResolutions; - } + delete []mSupportedResolutions; - gWindowImplementation = NULL; + gWindowImplementation = nullptr; } @@ -589,7 +682,6 @@ void LLWindowSDL::hide() } } -//virtual void LLWindowSDL::minimize() { if (mWindow) @@ -598,7 +690,6 @@ void LLWindowSDL::minimize() } } -//virtual void LLWindowSDL::restore() { if (mWindow) @@ -611,12 +702,6 @@ void LLWindowSDL::restore() // Usually called from LLWindowManager::destroyWindow() void LLWindowSDL::close() { - // Is window is already closed? - // if (!mWindow) - // { - // return; - // } - // Make sure cursor is visible and we haven't mangled the clipping state. setMouseClipping(false); showCursor(); @@ -626,7 +711,7 @@ void LLWindowSDL::close() bool LLWindowSDL::isValid() { - return (mWindow != NULL); + return mWindow != nullptr; } bool LLWindowSDL::getVisible() const @@ -645,6 +730,9 @@ bool LLWindowSDL::getVisible() const bool LLWindowSDL::getMinimized() const { + if( isWaylandWindowNotDrawing() ) + return true; + bool result = false; if (mWindow) { @@ -698,10 +786,10 @@ bool LLWindowSDL::getSize(LLCoordScreen *size) const { size->mX = mSurface->w; size->mY = mSurface->h; - return (true); + return true; } - return (false); + return false; } bool LLWindowSDL::getSize(LLCoordWindow *size) const @@ -710,10 +798,10 @@ bool LLWindowSDL::getSize(LLCoordWindow *size) const { size->mX = mSurface->w; size->mY = mSurface->h; - return (true); + return true; } - return (false); + return false; } bool LLWindowSDL::setPosition(const LLCoordScreen position) @@ -737,7 +825,6 @@ template< typename T > bool setSizeImpl( const T& newSize, SDL_Window *pWin ) if( nFlags & SDL_WINDOW_MAXIMIZED ) SDL_RestoreWindow( pWin ); - SDL_SetWindowSize( pWin, newSize.mX, newSize.mY ); SDL_Event event; event.type = SDL_WINDOWEVENT; @@ -803,7 +890,8 @@ bool LLWindowSDL::setGamma(const F32 gamma) Uint16 ramp[256]; mGamma = gamma; - if (mGamma == 0) mGamma = 0.1f; + if (mGamma == 0) + mGamma = 0.1f; mGamma = 1.f / mGamma; SDL_CalculateGammaRamp(mGamma, ramp); @@ -820,7 +908,8 @@ bool LLWindowSDL::isCursorHidden() // Constrains the mouse to the window. void LLWindowSDL::setMouseClipping(bool b) { - //SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF); + if( mWindow ) + SDL_SetWindowGrab( mWindow, b?SDL_TRUE:SDL_FALSE ); } // virtual @@ -840,17 +929,11 @@ bool LLWindowSDL::setCursorPosition(const LLCoordWindow position) LLCoordScreen screen_pos; if (!convertCoords(position, &screen_pos)) - { return false; - } - - //LL_INFOS() << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << LL_ENDL; // do the actual forced cursor move. SDL_WarpMouseInWindow(mWindow, screen_pos.mX, screen_pos.mY); - //LL_INFOS() << llformat("llcw %d,%d -> scr %d,%d", position.mX, position.mY, screen_pos.mX, screen_pos.mY) << LL_ENDL; - return result; } @@ -868,7 +951,6 @@ bool LLWindowSDL::getCursorPosition(LLCoordWindow *position) return convertCoords(screen_pos, position); } - F32 LLWindowSDL::getNativeAspectRatio() { // MBW -- there are a couple of bad assumptions here. One is that the display list won't include @@ -889,9 +971,7 @@ F32 LLWindowSDL::getNativeAspectRatio() // switching, and stashes it in mOriginalAspectRatio. Here, we just return it. if (mOverrideAspectRatio > 0.f) - { return mOverrideAspectRatio; - } return mOriginalAspectRatio; } @@ -903,9 +983,7 @@ F32 LLWindowSDL::getPixelAspectRatio() { LLCoordScreen screen_size; if (getSize(&screen_size)) - { pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX; - } } return pixel_aspect; @@ -916,61 +994,34 @@ F32 LLWindowSDL::getPixelAspectRatio() // dialogs are still usable in fullscreen. void LLWindowSDL::beforeDialog() { - bool running_x11 = false; -#if LL_X11 - running_x11 = (mSDL_XWindowID != None); -#endif //LL_X11 - LL_INFOS() << "LLWindowSDL::beforeDialog()" << LL_ENDL; if (SDLReallyCaptureInput(false)) // must ungrab input so popup works! { - if (mFullscreen) - { - // need to temporarily go non-fullscreen; bless SDL - // for providing a SDL_WM_ToggleFullScreen() - though - // it only works in X11 - if (running_x11 && mWindow) - { - SDL_SetWindowFullscreen( mWindow, 0 ); - } - } + if (mFullscreen && mWindow ) + SDL_SetWindowFullscreen( mWindow, 0 ); } -#if LL_X11 - if (mSDL_Display) + if (mServerProtocol == X11 && mX11Data.mDisplay) { // Everything that we/SDL asked for should happen before we // potentially hand control over to GTK. maybe_lock_display(); - XSync(mSDL_Display, False); + XSync(mX11Data.mDisplay, False); maybe_unlock_display(); } -#endif // LL_X11 maybe_lock_display(); } void LLWindowSDL::afterDialog() { - bool running_x11 = false; -#if LL_X11 - running_x11 = (mSDL_XWindowID != None); -#endif //LL_X11 - LL_INFOS() << "LLWindowSDL::afterDialog()" << LL_ENDL; maybe_unlock_display(); - if (mFullscreen) - { - // need to restore fullscreen mode after dialog - only works - // in X11 - if (running_x11 && mWindow) - { - SDL_SetWindowFullscreen( mWindow, 0 ); - } - } + if (mFullscreen && mWindow ) + SDL_SetWindowFullscreen( mWindow, 0 ); } void LLWindowSDL::flashIcon(F32 seconds) @@ -987,6 +1038,15 @@ void LLWindowSDL::flashIcon(F32 seconds) mFlashing = true; } +void LLWindowSDL::maybeStopFlashIcon() +{ + if (mFlashing && mFlashTimer.hasExpired()) + { + mFlashing = false; + SDL_FlashWindow( mWindow, SDL_FLASH_CANCEL ); + } +} + bool LLWindowSDL::isClipboardTextAvailable() { return SDL_HasClipboardText() == SDL_TRUE; @@ -1054,9 +1114,7 @@ LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_reso { SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 }; if (SDL_GetDisplayMode( 0 , i, &mode) != 0) - { continue; - } int w = mode.w; int h = mode.h; @@ -1109,7 +1167,7 @@ bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to) const // In the fullscreen case, window and screen coordinates are the same. to->mX = from.mX; to->mY = from.mY; - return (true); + return true; } bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to) const @@ -1120,21 +1178,21 @@ bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to) const // In the fullscreen case, window and screen coordinates are the same. to->mX = from.mX; to->mY = from.mY; - return (true); + return true; } bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to) const { LLCoordWindow window_coord; - return(convertCoords(from, &window_coord) && convertCoords(window_coord, to)); + return convertCoords(from, &window_coord) && convertCoords(window_coord, to); } bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to) const { LLCoordWindow window_coord; - return(convertCoords(from, &window_coord) && convertCoords(window_coord, to)); + return convertCoords(from, &window_coord) && convertCoords(window_coord, to); } void LLWindowSDL::setupFailure(const std::string& text, const std::string& caption, U32 type) @@ -1142,55 +1200,17 @@ void LLWindowSDL::setupFailure(const std::string& text, const std::string& capti destroyContext(); OSMessageBox(text, caption, type); + + // This is so catastrophic > bail as fast as possible. Otherwise the viewer can be stuck in a perpetual state of startup pain + std::terminate(); } bool LLWindowSDL::SDLReallyCaptureInput(bool capture) { - // note: this used to be safe to call nestedly, but in the - // end that's not really a wise usage pattern, so don't. - - if (capture) - mReallyCapturedCount = 1; - else - mReallyCapturedCount = 0; - - bool wantGrab; - if (mReallyCapturedCount <= 0) // uncapture - { - wantGrab = false; - } else // capture - { - wantGrab = true; - } - - if (mReallyCapturedCount < 0) // yuck, imbalance. - { - mReallyCapturedCount = 0; - LL_WARNS() << "ReallyCapture count was < 0" << LL_ENDL; - } - - bool newGrab = wantGrab; + if (!mFullscreen && mWindow ) /* only bother if we're windowed anyway */ + SDL_SetWindowGrab( mWindow, capture?SDL_TRUE:SDL_FALSE); - if (!mFullscreen) /* only bother if we're windowed anyway */ - { - int result; - if (wantGrab == true) - { - result = SDL_CaptureMouse(SDL_TRUE); - if (0 == result) - newGrab = true; - else - newGrab = false; - } - else - { - newGrab = false; - result = SDL_CaptureMouse(SDL_FALSE); - } - } - - // return boolean success for whether we ended up in the desired state - return capture == newGrab; + return capture; } U32 LLWindowSDL::SDLCheckGrabbyKeys(U32 keysym, bool gain) @@ -1252,7 +1272,7 @@ void check_vm_bloat() ssize_t res; size_t dummy; - char *ptr = NULL; + char *ptr = nullptr; for (int i=0; i<22; ++i) // parse past the values we don't want { res = getdelim(&ptr, &dummy, ' ', fp); @@ -1262,7 +1282,7 @@ void check_vm_bloat() goto finally; } free(ptr); - ptr = NULL; + ptr = nullptr; } // 23rd space-delimited entry is vsize res = getdelim(&ptr, &dummy, ' ', fp); @@ -1274,7 +1294,7 @@ void check_vm_bloat() } this_vm_size = atoll(ptr); free(ptr); - ptr = NULL; + ptr = nullptr; // 24th space-delimited entry is RSS res = getdelim(&ptr, &dummy, ' ', fp); llassert(ptr); @@ -1285,12 +1305,11 @@ void check_vm_bloat() } this_rss_size = getpagesize() * atoll(ptr); free(ptr); - ptr = NULL; + ptr = nullptr; LL_INFOS() << "VM SIZE IS NOW " << (this_vm_size/(1024*1024)) << " MB, RSS SIZE IS NOW " << (this_rss_size/(1024*1024)) << " MB" << LL_ENDL; - if (llabs(last_vm_size - this_vm_size) > - significant_vm_difference) + if (llabs(last_vm_size - this_vm_size) > significant_vm_difference) { if (this_vm_size > last_vm_size) { @@ -1302,8 +1321,7 @@ void check_vm_bloat() } } - if (llabs(last_rss_size - this_rss_size) > - significant_rss_difference) + if (llabs(last_rss_size - this_rss_size) > significant_rss_difference) { if (this_rss_size > last_rss_size) { @@ -1319,21 +1337,17 @@ void check_vm_bloat() last_vm_size = this_vm_size; finally: - if (NULL != ptr) - { + if (ptr) free(ptr); - ptr = NULL; - } fclose(fp); } -#endif // LL_LINUX +#endif } // virtual void LLWindowSDL::processMiscNativeEvents() { -#if LL_GLIB // Pump until we've nothing left to do or passed 1/15th of a // second pumping for this frame. static LLTimer pump_timer; @@ -1343,13 +1357,10 @@ void LLWindowSDL::processMiscNativeEvents() { g_main_context_iteration(g_main_context_default(), false); } while( g_main_context_pending(g_main_context_default()) && !pump_timer.hasExpired()); -#endif // hack - doesn't belong here - but this is just for debugging if (getenv("LL_DEBUG_BLOAT")) - { check_vm_bloat(); - } } void LLWindowSDL::gatherInput(bool app_has_focus) @@ -1388,7 +1399,7 @@ void LLWindowSDL::gatherInput(bool app_has_focus) { auto string = utf8str_to_utf16str( event.text.text ); mKeyModifiers = gKeyboard->currentMask( false ); - mInputType = "textinput"; + for( auto key: string ) { mKeyVirtualKey = key; @@ -1404,13 +1415,10 @@ void LLWindowSDL::gatherInput(bool app_has_focus) case SDL_KEYDOWN: mKeyVirtualKey = event.key.keysym.sym; mKeyModifiers = event.key.keysym.mod; - mInputType = "keydown"; // treat all possible Enter/Return keys the same if (mKeyVirtualKey == SDLK_RETURN2 || mKeyVirtualKey == SDLK_KP_ENTER) - { mKeyVirtualKey = SDLK_RETURN; - } gKeyboard->handleKeyDown(mKeyVirtualKey, mKeyModifiers ); @@ -1433,13 +1441,10 @@ void LLWindowSDL::gatherInput(bool app_has_focus) case SDL_KEYUP: mKeyVirtualKey = event.key.keysym.sym; mKeyModifiers = event.key.keysym.mod; - mInputType = "keyup"; // treat all possible Enter/Return keys the same if (mKeyVirtualKey == SDLK_RETURN2 || mKeyVirtualKey == SDLK_KP_ENTER) - { mKeyVirtualKey = SDLK_RETURN; - } if (SDLCheckGrabbyKeys(mKeyVirtualKey, false) == 0) SDLReallyCaptureInput(false); // part of the fix for SL-13243 @@ -1461,7 +1466,7 @@ void LLWindowSDL::gatherInput(bool app_has_focus) else mCallbacks->handleMouseDown(this, openGlCoord, mask); } - else if (event.button.button == SDL_BUTTON_RIGHT) // right + else if (event.button.button == SDL_BUTTON_RIGHT) { mCallbacks->handleRightMouseDown(this, openGlCoord, mask); } @@ -1591,12 +1596,12 @@ static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty SDL_SwapLE32(0xFF00U), SDL_SwapLE32(0xFF0000U), SDL_SwapLE32(0xFF000000U)); - SDL_FillRect(cursurface, NULL, SDL_SwapLE32(0x00000000U)); + SDL_FillRect(cursurface, nullptr, SDL_SwapLE32(0x00000000U)); // Blit the cursor pixel data onto a 32-bit RGBA surface so we // only have to cope with processing one type of pixel format. - if (0 == SDL_BlitSurface(bmpsurface, NULL, - cursurface, NULL)) + if (0 == SDL_BlitSurface(bmpsurface, nullptr, + cursurface, nullptr)) { // n.b. we already checked that width is a multiple of 8. const int bitmap_bytes = (cursurface->w * cursurface->h) / 8; @@ -1670,12 +1675,10 @@ void LLWindowSDL::updateCursor() void LLWindowSDL::initCursors() { - int i; // Blank the cursor pointer array for those we may miss. - for (i=0; i<UI_CURSOR_COUNT; ++i) - { + for ( int i=0; i<UI_CURSOR_COUNT; ++i) mSDLCursors[i] = nullptr; - } + // Pre-make an SDL cursor for each of the known cursor types. // We hardcode the hotspots - to avoid that we'd have to write // a .cur file loader. @@ -1728,23 +1731,24 @@ void LLWindowSDL::initCursors() void LLWindowSDL::quitCursors() { - int i; if (mWindow) { - for (i=0; i<UI_CURSOR_COUNT; ++i) + for (int i=0; i<UI_CURSOR_COUNT; ++i) { if (mSDLCursors[i]) { SDL_FreeCursor(mSDLCursors[i]); - mSDLCursors[i] = NULL; + mSDLCursors[i] = nullptr; } } - } else { + } + else + { // SDL doesn't refcount cursors, so if the window has // already been destroyed then the cursors have gone with it. LL_INFOS() << "Skipping quitCursors: mWindow already gone." << LL_ENDL; - for (i=0; i<UI_CURSOR_COUNT; ++i) - mSDLCursors[i] = NULL; + for (int i=0; i<UI_CURSOR_COUNT; ++i) + mSDLCursors[i] = nullptr; } } @@ -1872,7 +1876,7 @@ S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 typ bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b) { - return (false); + return false; } /* @@ -1902,7 +1906,6 @@ LLSD LLWindowSDL::getNativeKeyData() const result["virtual_key"] = (S32)mKeyVirtualKey; result["virtual_key_win"] = (S32)LLKeyboardSDL::mapSDL2toWin( mKeyVirtualKey ); result["modifiers"] = (S32)modifiers; - result["input_type"] = mInputType; return result; } @@ -1937,9 +1940,9 @@ void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async) LL_INFOS() << "spawn_web_browser returning." << LL_ENDL; } -void *LLWindowSDL::getPlatformWindow() +void* LLWindowSDL::getPlatformWindow() { - return NULL; + return (void*)mWindow; } void LLWindowSDL::bringToFront() @@ -1956,6 +1959,8 @@ void LLWindowSDL::bringToFront() //static std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList() { + std::vector<std::string> rtns; +#if LL_LINUX // Use libfontconfig to find us a nice ordered list of fallback fonts // specific to this system. std::string final_fallback("/usr/share/fonts/truetype/kochi/kochi-gothic.ttf"); @@ -1972,9 +1977,9 @@ std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList() // renderable range if for some reason our FreeType actually fails // to use some of the fonts we want it to. const bool elide_unicode_coverage = true; - std::vector<std::string> rtns; - FcFontSet *fs = NULL; - FcPattern *sortpat = NULL; + + FcFontSet *fs = nullptr; + FcPattern *sortpat = nullptr; LL_INFOS() << "Getting system font list from FontConfig..." << LL_ENDL; @@ -1983,7 +1988,7 @@ std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList() // of languages that can be displayed, but ensures that their // preferred language is rendered from a single consistent font where // possible. - FL_Locale *locale = NULL; + FL_Locale *locale = nullptr; FL_Success success = FL_FindLocale(&locale, FL_MESSAGES); if (success != 0) { @@ -2014,8 +2019,7 @@ std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList() { // Sort the list of system fonts from most-to-least-desirable. FcResult result; - fs = FcFontSort(NULL, sortpat, elide_unicode_coverage, - NULL, &result); + fs = FcFontSort(nullptr, sortpat, elide_unicode_coverage, nullptr, &result); FcPatternDestroy(sortpat); } @@ -2028,10 +2032,7 @@ std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList() for (int i=0; i<fs->nfont; ++i) { FcChar8 *filename; - if (FcResultMatch == FcPatternGetString(fs->fonts[i], - FC_FILE, 0, - &filename) - && filename) + if (FcResultMatch == FcPatternGetString(fs->fonts[i], FC_FILE, 0, &filename) && filename) { rtns.push_back(std::string((const char*)filename)); if (rtns.size() >= max_font_count_cutoff) @@ -2042,15 +2043,15 @@ std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList() } LL_DEBUGS() << "Using font list: " << LL_ENDL; - for (std::vector<std::string>::iterator it = rtns.begin(); - it != rtns.end(); - ++it) + for (auto it = rtns.begin(); it != rtns.end(); ++it) { LL_DEBUGS() << " file: " << *it << LL_ENDL; } + LL_INFOS() << "Using " << rtns.size() << "/" << found_font_count << " system fonts." << LL_ENDL; rtns.push_back(final_fallback); +#endif return rtns; } diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index a85b7c11e7..c3b2f32cf7 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -11,7 +11,6 @@ * 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. @@ -36,10 +35,8 @@ #include "SDL2/SDL.h" #include "SDL2/SDL_endian.h" -#if LL_X11 // get X11-specific headers for use in low-level stuff like copy-and-paste support #include "SDL2/SDL_syswm.h" -#endif // AssertMacros.h does bad things. #include "fix_macros.h" @@ -50,8 +47,8 @@ class LLWindowSDL : public LLWindow { public: void show() override; - void hide() override; + void restore() override; void close() override; @@ -62,11 +59,8 @@ public: bool getMaximized() const override; bool maximize() override; - void minimize() override; - void restore() override; - bool getPosition(LLCoordScreen *position) const override; bool getSize(LLCoordScreen *size) const override; @@ -87,49 +81,38 @@ public: bool getCursorPosition(LLCoordWindow *position) override; void showCursor() override; - void hideCursor() override; + bool isCursorHidden() override; void showCursorFromMouseMove() override; - void hideCursorUntilMouseMove() override; - bool isCursorHidden() override; - void updateCursor() override; void captureMouse() override; - void releaseMouse() override; void setMouseClipping(bool b) override; - void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true) override; + void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true) override; bool isClipboardTextAvailable() override; - bool pasteTextFromClipboard(LLWString &dst) override; - bool copyTextToClipboard(const LLWString &src) override; - bool isPrimaryTextAvailable() override; - bool pasteTextFromPrimary(LLWString &dst) override; - bool copyTextToPrimary(const LLWString &src) override; void flashIcon(F32 seconds) override; + void maybeStopFlashIcon(); F32 getGamma() const override; - bool setGamma(const F32 gamma) override; // Set the gamma + bool restoreGamma() override; // Restore original gamma table (before updating gamma) U32 getFSAASamples() const override; - void setFSAASamples(const U32 samples) override; - bool restoreGamma() override; // Restore original gamma table (before updating gamma) - void processMiscNativeEvents() override; void gatherInput(bool app_has_focus) override; @@ -162,7 +145,6 @@ public: void setNativeAspectRatio(F32 ratio) override { mOverrideAspectRatio = ratio; } void beforeDialog() override; - void afterDialog() override; bool dialogColorPicker(F32 *r, F32 *g, F32 *b) override; @@ -179,31 +161,15 @@ public: static std::vector<std::string> getDynamicFallbackFontList(); - // Not great that these are public, but they have to be accessible - // by non-class code and it's better than making them global. -#if LL_X11 - Window mSDL_XWindowID; - Display *mSDL_Display; -#endif - - void (*Lock_Display)(void); - - void (*Unlock_Display)(void); - -#if LL_X11 + void (*Lock_Display)(void) = nullptr; + void (*Unlock_Display)(void) = nullptr; static Window get_SDL_XWindowID(void); - static Display *get_SDL_Display(void); -#endif // LL_X11 - void *createSharedContext() override; - void makeContextCurrent(void *context) override; - void destroySharedContext(void *context) override; - void toggleVSync(bool enable_vsync) override; protected: @@ -219,7 +185,6 @@ protected: LLSD getNativeKeyData() const override; void initCursors(); - void quitCursors(); void moveWindow(const LLCoordScreen &position, const LLCoordScreen &size); @@ -239,7 +204,6 @@ protected: // create or re-create the GL context/window. Called from the constructor and switchContext(). bool createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool enable_vsync); - void destroyContext(); void setupFailure(const std::string &text, const std::string &caption, U32 type); @@ -251,36 +215,52 @@ protected: // // Platform specific variables // - U32 mGrabbyKeyFlags; - int mReallyCapturedCount; + U32 mGrabbyKeyFlags = 0; - SDL_Window *mWindow; + SDL_Window *mWindow = nullptr; SDL_Surface *mSurface; SDL_GLContext mContext; SDL_Cursor *mSDLCursors[UI_CURSOR_COUNT]; std::string mWindowTitle; - double mOriginalAspectRatio; - bool mNeedsResize; // Constructor figured out the window is too big, it needs a resize. + double mOriginalAspectRatio = 1.0f; + bool mNeedsResize = false; // Constructor figured out the window is too big, it needs a resize. LLCoordScreen mNeedsResizeSize; - F32 mOverrideAspectRatio; - F32 mGamma; - U32 mFSAASamples; - - int mSDLFlags; + F32 mOverrideAspectRatio = 0.0f; + F32 mGamma = 0.0f; + U32 mFSAASamples = 0; - int mHaveInputFocus; /* 0=no, 1=yes, else unknown */ - int mIsMinimized; /* 0=no, 1=yes, else unknown */ + int mHaveInputFocus = -1; /* 0=no, 1=yes, else unknown */ friend class LLWindowManager; private: - bool mFlashing; + bool mFlashing = false; LLTimer mFlashTimer; - U32 mKeyVirtualKey; - U32 mKeyModifiers; - std::string mInputType; + U32 mKeyVirtualKey = 0; + U32 mKeyModifiers = KMOD_NONE; + + enum EServerProtocol{ X11, Wayland, Unknown }; + EServerProtocol mServerProtocol = Unknown; + + struct { + Window mXWindowID = None; + Display *mDisplay = nullptr; + } mX11Data; + // Wayland + struct { + wl_surface *mSurface = nullptr; + uint64_t mLastFrameEvent = 0; + } mWaylandData; + + bool mWaylandLoaded = false; + + bool isWaylandWindowNotDrawing() const; + + void setupWaylandFrameCallback(); + static void waylandFrameDoneCB(void *data, struct wl_callback *cb, uint32_t time); + // private: void tryFindFullscreenSize(int &aWidth, int &aHeight); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index f8294f063f..f349c4aea8 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -403,6 +403,7 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18 bool mGLReady = false; bool mGotGLBuffer = false; + bool mShuttingDown = false; }; @@ -4576,14 +4577,14 @@ inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread() void LLWindowWin32::LLWindowWin32Thread::close() { - if (!mQueue->isClosed()) + LL::ThreadPool::close(); + if (!mShuttingDown) { LL_WARNS() << "Closing window thread without using destroy_window_handler" << LL_ENDL; - LL::ThreadPool::close(); - // Workaround for SL-18721 in case window closes too early and abruptly LLSplashScreen::show(); LLSplashScreen::update("..."); // will be updated later + mShuttingDown = true; } } @@ -4795,6 +4796,8 @@ void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy() return; } + mShuttingDown = true; + // Make sure we don't leave a blank toolbar button. // Also hiding window now prevents user from suspending it // via some action (like dragging it around) diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp index e695035461..92bcb1a2ae 100644 --- a/indra/llxml/llxmlnode.cpp +++ b/indra/llxml/llxmlnode.cpp @@ -3256,6 +3256,27 @@ std::string LLXMLNode::getTextContents() const return msg; } +std::string LLXMLNode::getXMLRPCTextContents() const +{ + std::string msg; + std::string::size_type start = mValue.find_first_not_of(" \t\n"); + if (start != mValue.npos) + { + std::string::size_type end = mValue.find_last_not_of(" \t\n"); + if (end != mValue.npos) + { + msg = mValue.substr(start, end + 1 - start); + } + else + { + msg = mValue.substr(start); + } + } + // Convert any internal CR to LF + msg = utf8str_removeCRLF(msg); + return msg; +} + void LLXMLNode::setLineNumber(S32 line_number) { mLineNumber = line_number; @@ -3365,7 +3386,7 @@ bool LLXMLNode::fromXMLRPCValue(LLSD& target) if (childp->hasName("string")) { - target.assign(LLStringFn::xml_decode(childp->getTextContents())); + target.assign(LLStringFn::xml_decode(childp->getXMLRPCTextContents())); return true; } diff --git a/indra/llxml/llxmlnode.h b/indra/llxml/llxmlnode.h index 3769ec8293..09c7c4fdad 100644 --- a/indra/llxml/llxmlnode.h +++ b/indra/llxml/llxmlnode.h @@ -295,6 +295,7 @@ protected: bool removeChild(LLXMLNode* child); bool isFullyDefault(); + std::string getXMLRPCTextContents() const; bool parseXmlRpcArrayValue(LLSD& target); bool parseXmlRpcStructValue(LLSD& target); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 6c8d7b910e..4d79138aaf 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -30,6 +30,7 @@ include(LLPrimitive) include(LLWindow) include(SDL2) include(NDOF) +include(NFDE) include(NVAPI) include(OPENAL) include(OpenGL) @@ -734,6 +735,11 @@ set(viewer_SOURCE_FILES llxmlrpctransaction.cpp noise.cpp pipeline.cpp + rlvactions.cpp + rlvcommon.cpp + rlvfloaters.cpp + rlvhelper.cpp + rlvhandler.cpp ) set(VIEWER_BINARY_NAME "secondlife-bin" CACHE STRING @@ -1399,6 +1405,12 @@ set(viewer_HEADER_FILES llxmlrpctransaction.h noise.h pipeline.h + rlvdefines.h + rlvactions.h + rlvcommon.h + rlvfloaters.h + rlvhelper.h + rlvhandler.h roles_constants.h VertexCache.h VorbisFramework.h @@ -1933,6 +1945,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ll::ndof ll::tracy ll::openxr + ll::nfde ) if( TARGET ll::intel_memops ) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 36abebc942..049ec3ad34 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2469,6 +2469,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>UseGroupMemberPagination</key> + <map> + <key>Comment</key> + <string>Enable pagination of group memeber list 50 members at a time.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>DisplayTimecode</key> <map> <key>Comment</key> @@ -4122,6 +4133,17 @@ <key>Value</key> <array /> </map> + <key>LuaDebugShowSource</key> + <map> + <key>Comment</key> + <string>Show source info in Lua Debug Console output</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>GridStatusRSS</key> <map> <key>Comment</key> @@ -4630,6 +4652,17 @@ <key>Value</key> <integer>1</integer> </map> + <key>MapShowGridCoords</key> + <map> + <key>Comment</key> + <string>Shows/hides the grid coordinates of each region on the world map.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>MiniMapAutoCenter</key> <map> <key>Comment</key> @@ -8020,6 +8053,28 @@ <key>Value</key> <integer>0</integer> </map> + <key>RenderDebugTextureLabel</key> + <map> + <key>Comment</key> + <string>Enable texture labels via glObjectLabel.</string> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>RenderDebugTextureLabelLocalFilesSession</key> + <map> + <key>Comment</key> + <string>Enumerate textures with local file names. Doesn't do anything until RenderDebugTextureLabel is set to true. Requires restart AND applies on the next session only.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>RenderDelayCreation</key> <map> <key>Comment</key> @@ -9234,7 +9289,7 @@ <key>Type</key> <string>F32</string> <key>Value</key> - <real>1</real> + <real>1.0</real> </map> <key>RenderReflectionProbeDrawDistance</key> @@ -10045,7 +10100,7 @@ <key>Type</key> <string>F32</string> <key>Value</key> - <real>1.0</real> + <real>0.7</real> </map> <key>RenderTonemapType</key> <map> @@ -10056,7 +10111,7 @@ <key>Type</key> <string>U32</string> <key>Value</key> - <integer>0</integer> + <integer>1</integer> </map> <key>ReplaySession</key> <map> @@ -10080,6 +10135,83 @@ <key>Value</key> <string>https://feedback.secondlife.com/</string> </map> + <key>RestrainedLove</key> + <map> + <key>Comment</key> + <string>Toggles RLVa features (requires restart)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <boolean>0</boolean> + </map> + <key>RestrainedLoveDebug</key> + <map> + <key>Comment</key> + <string>Toggles RLVa debug mode (displays the commands when in debug mode)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <boolean>0</boolean> + </map> + <key>RLVaBlockedExperiences</key> + <map> + <key>Comment</key> + <string>List of experiences blocked from interacting with RLVa</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>bfe25fb4-222c-11e5-85a2-fa4c4ccaa202</string> + </map> + <key>RLVaDebugHideUnsetDuplicate</key> + <map> + <key>Comment</key> + <string>Suppresses reporting "unset" or "duplicate" command restrictions when RestrainedLoveDebug is TRUE</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <boolean>0</boolean> + </map> + <key>RLVaEnableTemporaryAttachments</key> + <map> + <key>Comment</key> + <string>Allows temporary attachments (regardless of origin) to issue RLV commands</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <boolean>1</boolean> + </map> + <key>RLVaExperimentalCommands</key> + <map> + <key>Comment</key> + <string>Enables the experimental command set</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <boolean>1</boolean> + </map> + <key>RLVaTopLevelMenu</key> + <map> + <key>Comment</key> + <string>Show the RLVa specific menu as a top level menu</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <boolean>1</boolean> + </map> <key>RevokePermsOnStopAnimation</key> <map> <key>Comment</key> @@ -11639,6 +11771,28 @@ <key>Value</key> <integer>0</integer> </map> + <key>TextureDiscardBackgroundedTime</key> + <map> + <key>Comment</key> + <string>Specify how long to wait before discarding texture data after viewer is backgrounded. (zero or negative to disable)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>60.0</real> + </map> + <key>TextureDiscardMinimizedTime</key> + <map> + <key>Comment</key> + <string>Specify how long to wait before discarding texture data after viewer is minimized. (zero or negative to disable)</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.0</real> + </map> <key>TextureFetchConcurrency</key> <map> <key>Comment</key> @@ -12967,9 +13121,9 @@ <key>Use24HourClock</key> <map> <key>Comment</key> - <string>12 vs 24. At the moment only for region restart schedule floater</string> + <string>12 vs 24. At the moment coverage is partial</string> <key>Persist</key> - <integer>0</integer> + <integer>1</integer> <key>Type</key> <string>Boolean</string> <key>Value</key> @@ -13624,7 +13778,7 @@ <key>Type</key> <string>Boolean</string> <key>Value</key> - <integer>0</integer> + <integer>1</integer> </map> <key>WarningsAsChat</key> <map> @@ -15268,7 +15422,7 @@ <key>Type</key> <string>U32</string> <key>Value</key> - <integer>5</integer> + <integer>8</integer> </map> <key>TerrainPaintResolution</key> <map> diff --git a/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl index cf20653a0f..a79a56d725 100644 --- a/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl +++ b/indra/newview/app_settings/shaders/class1/interface/pbrTerrainBakeF.glsl @@ -1,5 +1,5 @@ /** - * @file terrainBakeF.glsl + * @file pbrTerrainBakeF.glsl * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code diff --git a/indra/llmath/camera.h b/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl index 14a08c5985..e79e9010e6 100644 --- a/indra/llmath/camera.h +++ b/indra/newview/app_settings/shaders/class1/interface/terrainStampF.glsl @@ -1,10 +1,9 @@ /** - * @file camera.h - * @brief Legacy wrapper header. + * @file terrainStampF.glsl * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2024, 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 @@ -24,4 +23,22 @@ * $/LicenseInfo$ */ -#include "llcamera.h" +/*[EXTRA_CODE_HERE]*/ + +out vec4 frag_color; + +// Paint texture to stamp into the "paintmap" +uniform sampler2D diffuseMap; + +in vec2 vary_texcoord0; + +void main() +{ + vec4 col = texture(diffuseMap, vary_texcoord0); + if (col.a <= 0.0f) + { + discard; + } + + frag_color = max(col, vec4(0)); +} diff --git a/indra/llmath/coordframe.h b/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl index e7395b1212..294fa6be26 100644 --- a/indra/llmath/coordframe.h +++ b/indra/newview/app_settings/shaders/class1/interface/terrainStampV.glsl @@ -1,10 +1,9 @@ /** - * @file coordframe.h - * @brief Legacy wrapper header. + * @file terrainStampV.glsl * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2024, 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 @@ -24,4 +23,17 @@ * $/LicenseInfo$ */ -#include "llcoordframe.h" +uniform mat4 modelview_projection_matrix; +uniform vec2 terrain_stamp_scale; + +in vec3 position; + +out vec2 vary_texcoord0; + +void main() +{ + gl_Position = modelview_projection_matrix * vec4(position, 1.0); + // Positions without transforms are treated as UVs for the purpose of this shader. + vary_texcoord0.xy = terrain_stamp_scale * position.xy; +} + diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index 24fd7928a6..6390e43b7a 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -1,4 +1,4 @@ -version 63 +version 64 // The version number above should be incremented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -77,11 +77,13 @@ RenderScreenSpaceReflections 1 1 RenderMirrors 1 1 RenderHeroProbeResolution 1 2048 RenderHeroProbeDistance 1 16 -RenderHeroProbeUpdateRate 1 4 +RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderDownScaleMethod 1 1 RenderCASSharpness 1 1 - +RenderExposure 1 4 +RenderTonemapType 1 1 +RenderTonemapMix 1 1 // // Low Graphics Settings @@ -119,6 +121,9 @@ RenderHeroProbeDistance 1 4 RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium Low Graphics Settings @@ -156,6 +161,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium Graphics Settings (standard) @@ -193,6 +201,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium High Graphics Settings @@ -230,6 +241,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 2 RenderHeroProbeConservativeUpdateMultiplier 1 8 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // High Graphics Settings (SSAO + sun shadows) @@ -267,6 +281,9 @@ RenderHeroProbeDistance 1 8 RenderHeroProbeUpdateRate 1 2 RenderHeroProbeConservativeUpdateMultiplier 1 8 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // High Ultra Graphics Settings (deferred + SSAO + all shadows) @@ -304,6 +321,9 @@ RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 RenderHeroProbeConservativeUpdateMultiplier 1 4 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Ultra graphics (REALLY PURTY!) @@ -341,6 +361,9 @@ RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 RenderHeroProbeConservativeUpdateMultiplier 1 4 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Class Unknown Hardware (unknown) diff --git a/indra/newview/featuretable_linux.txt b/indra/newview/featuretable_linux.txt index dc5d1ff6c4..9465acae6b 100644 --- a/indra/newview/featuretable_linux.txt +++ b/indra/newview/featuretable_linux.txt @@ -1,4 +1,4 @@ -version 29 +version 30 // The version number above should be incremented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -81,7 +81,9 @@ RenderHeroProbeUpdateRate 1 4 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderDownScaleMethod 1 1 RenderCASSharpness 1 1 - +RenderExposure 1 4 +RenderTonemapType 1 1 +RenderTonemapMix 1 1 // // Low Graphics Settings @@ -119,6 +121,9 @@ RenderHeroProbeDistance 1 4 RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium Low Graphics Settings @@ -156,6 +161,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium Graphics Settings (standard) @@ -193,6 +201,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium High Graphics Settings @@ -230,6 +241,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 2 RenderHeroProbeConservativeUpdateMultiplier 1 8 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // High Graphics Settings (SSAO + sun shadows) @@ -267,6 +281,9 @@ RenderHeroProbeDistance 1 8 RenderHeroProbeUpdateRate 1 2 RenderHeroProbeConservativeUpdateMultiplier 1 8 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // High Ultra Graphics Settings (deferred + SSAO + all shadows) @@ -304,6 +321,9 @@ RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 RenderHeroProbeConservativeUpdateMultiplier 1 4 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Ultra graphics (REALLY PURTY!) @@ -341,6 +361,9 @@ RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 RenderHeroProbeConservativeUpdateMultiplier 1 4 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Class Unknown Hardware (unknown) diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index 06ad730a40..9ba922c3ce 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -1,4 +1,4 @@ -version 61 +version 62 // The version number above should be incremented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -77,9 +77,12 @@ RenderReflectionProbeLevel 1 3 RenderMirrors 1 1 RenderHeroProbeResolution 1 2048 RenderHeroProbeDistance 1 16 -RenderHeroProbeUpdateRate 1 4 +RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 1 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 1 // // Low Graphics Settings @@ -117,6 +120,9 @@ RenderHeroProbeDistance 1 4 RenderHeroProbeUpdateRate 1 6 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium Low Graphics Settings @@ -154,6 +160,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium Graphics Settings (standard) @@ -191,6 +200,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 3 RenderHeroProbeConservativeUpdateMultiplier 1 16 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Medium High Graphics Settings @@ -228,6 +240,9 @@ RenderHeroProbeDistance 1 6 RenderHeroProbeUpdateRate 1 2 RenderHeroProbeConservativeUpdateMultiplier 1 8 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // High Graphics Settings (SSAO + sun shadows) @@ -265,6 +280,9 @@ RenderHeroProbeDistance 1 8 RenderHeroProbeUpdateRate 1 2 RenderHeroProbeConservativeUpdateMultiplier 1 8 RenderCASSharpness 1 0 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // High Ultra Graphics Settings (SSAO + all shadows) @@ -302,6 +320,9 @@ RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 RenderHeroProbeConservativeUpdateMultiplier 1 4 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Ultra graphics (REALLY PURTY!) @@ -339,6 +360,9 @@ RenderHeroProbeDistance 1 16 RenderHeroProbeUpdateRate 1 1 RenderHeroProbeConservativeUpdateMultiplier 1 4 RenderCASSharpness 1 0.4 +RenderExposure 1 1 +RenderTonemapType 1 1 +RenderTonemapMix 1 0.7 // // Class Unknown Hardware (unknown) diff --git a/indra/newview/gltfscenemanager.cpp b/indra/newview/gltfscenemanager.cpp index ed66753267..f596ce04f8 100644 --- a/indra/newview/gltfscenemanager.cpp +++ b/indra/newview/gltfscenemanager.cpp @@ -500,7 +500,7 @@ void GLTFSceneManager::update() LLNewBufferedResourceUploadInfo::uploadFinish_f finish = [this, buffer](LLUUID assetId, LLSD response) { LLAppViewer::instance()->postToMainCoro( - [=]() + [=, this]() { if (mUploadingAsset) { diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index ca35608175..3d4f5e1054 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -355,20 +355,6 @@ bool LLAgent::isMicrophoneOn(const LLSD& sdname) return LLVoiceClient::getInstance()->getUserPTTState(); } -//static -void LLAgent::toggleHearMediaSoundFromAvatar() -{ - const S32 mediaSoundsEarLocation = gSavedSettings.getS32("MediaSoundsEarLocation"); - gSavedSettings.setS32("MediaSoundsEarLocation", !mediaSoundsEarLocation); -} - -//static -void LLAgent::toggleHearVoiceFromAvatar() -{ - const S32 voiceEarLocation = gSavedSettings.getS32("VoiceEarLocation"); - gSavedSettings.setS32("VoiceEarLocation", !voiceEarLocation); -} - // ************************************************************ // Enabled this definition to compile a 'hacked' viewer that // locally believes the end user has godlike powers. diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 448ee575f5..489131d974 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -377,13 +377,6 @@ private: bool mVoiceConnected; //-------------------------------------------------------------------- - // Sound - //-------------------------------------------------------------------- -public: - static void toggleHearMediaSoundFromAvatar(); - static void toggleHearVoiceFromAvatar(); - - //-------------------------------------------------------------------- // Chat //-------------------------------------------------------------------- public: diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index 14e443ec4e..3d07ead95d 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -32,11 +32,14 @@ #include "llagent.h" #include "llagentcamera.h" +#include "llavatarname.h" +#include "llavatarnamecache.h" #include "llvoavatar.h" #include "llcommandhandler.h" #include "llinventorymodel.h" #include "llslurl.h" #include "llurldispatcher.h" +#include "llviewercontrol.h" #include "llviewernetwork.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" @@ -46,7 +49,8 @@ #include "llsdutil_math.h" #include "lltoolgrab.h" #include "llhudeffectlookat.h" -#include "llagentcamera.h" +#include "llviewercamera.h" +#include "resultset.h" #include <functional> static const F64 PLAY_ANIM_THROTTLE_PERIOD = 1.f; @@ -176,6 +180,32 @@ LLAgentListener::LLAgentListener(LLAgent &agent) "Return information about [\"item_id\"] animation", &LLAgentListener::getAnimationInfo, llsd::map("item_id", LLSD(), "reply", LLSD())); + + add("getID", + "Return your own avatar ID", + &LLAgentListener::getID, + llsd::map("reply", LLSD())); + + add("getNearbyAvatarsList", + "Return result set key [\"result\"] for nearby avatars in a range of [\"dist\"]\n" + "if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n" + "reply contains \"result\" table with \"id\", \"name\", \"global_pos\", \"region_pos\", \"region_id\" fields", + &LLAgentListener::getNearbyAvatarsList, + llsd::map("reply", LLSD())); + + add("getNearbyObjectsList", + "Return result set key [\"result\"] for nearby objects in a range of [\"dist\"]\n" + "if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n" + "reply contains \"result\" table with \"id\", \"global_pos\", \"region_pos\", \"region_id\" fields", + &LLAgentListener::getNearbyObjectsList, + llsd::map("reply", LLSD())); + + add("getAgentScreenPos", + "Return screen position of the [\"avatar_id\"] avatar or own avatar if not specified\n" + "reply contains \"x\", \"y\" coordinates and \"onscreen\" flag to indicate if it's actually in within the current window\n" + "avatar render position is used as the point", + &LLAgentListener::getAgentScreenPos, + llsd::map("reply", LLSD())); } void LLAgentListener::requestTeleport(LLSD const & event_data) const @@ -693,3 +723,118 @@ void LLAgentListener::getAnimationInfo(LLSD const &event_data) "priority", motion->getPriority()); } } + +void LLAgentListener::getID(LLSD const& event_data) +{ + Response response(llsd::map("id", gAgentID), event_data); +} + +struct AvResultSet : public LL::VectorResultSet<LLVOAvatar*> +{ + AvResultSet() : super("nearby_avatars") {} + LLSD getSingleFrom(LLVOAvatar* const& av) const override + { + LLAvatarName av_name; + LLAvatarNameCache::get(av->getID(), &av_name); + LLVector3 region_pos = av->getCharacterPosition(); + return llsd::map("id", av->getID(), + "global_pos", ll_sd_from_vector3d(av->getPosGlobalFromAgent(region_pos)), + "region_pos", ll_sd_from_vector3(region_pos), + "name", av_name.getUserName(), + "region_id", av->getRegion()->getRegionID()); + } +}; + +struct ObjResultSet : public LL::VectorResultSet<LLViewerObject*> +{ + ObjResultSet() : super("nearby_objects") {} + LLSD getSingleFrom(LLViewerObject* const& obj) const override + { + return llsd::map("id", obj->getID(), + "global_pos", ll_sd_from_vector3d(obj->getPositionGlobal()), + "region_pos", ll_sd_from_vector3(obj->getPositionRegion()), + "region_id", obj->getRegion()->getRegionID()); + } +}; + + +F32 get_search_radius(LLSD const& event_data) +{ + static LLCachedControl<F32> render_far_clip(gSavedSettings, "RenderFarClip", 64); + F32 dist = render_far_clip; + if (event_data.has("dist")) + { + dist = llclamp((F32)event_data["dist"].asReal(), 1, 512); + } + return dist * dist; +} + +void LLAgentListener::getNearbyAvatarsList(LLSD const& event_data) +{ + Response response(LLSD(), event_data); + auto avresult = new AvResultSet; + + F32 radius = get_search_radius(event_data); + LLVector3d agent_pos = gAgent.getPositionGlobal(); + for (LLCharacter* character : LLCharacter::sInstances) + { + LLVOAvatar* avatar = (LLVOAvatar*)character; + if (!avatar->isDead() && !avatar->isControlAvatar() && !avatar->isSelf()) + { + if ((dist_vec_squared(avatar->getPositionGlobal(), agent_pos) <= radius)) + { + avresult->mVector.push_back(avatar); + } + } + } + response["result"] = avresult->getKeyLength(); +} + +void LLAgentListener::getNearbyObjectsList(LLSD const& event_data) +{ + Response response(LLSD(), event_data); + auto objresult = new ObjResultSet; + + F32 radius = get_search_radius(event_data); + S32 num_objects = gObjectList.getNumObjects(); + LLVector3d agent_pos = gAgent.getPositionGlobal(); + for (S32 i = 0; i < num_objects; ++i) + { + LLViewerObject* object = gObjectList.getObject(i); + if (object && object->getVolume() && !object->isAttachment()) + { + if ((dist_vec_squared(object->getPositionGlobal(), agent_pos) <= radius)) + { + objresult->mVector.push_back(object); + } + } + } + response["result"] = objresult->getKeyLength(); +} + +void LLAgentListener::getAgentScreenPos(LLSD const& event_data) +{ + Response response(LLSD(), event_data); + LLVector3 render_pos; + if (event_data.has("avatar_id") && (event_data["avatar_id"].asUUID() != gAgentID)) + { + LLUUID avatar_id(event_data["avatar_id"]); + for (LLCharacter* character : LLCharacter::sInstances) + { + LLVOAvatar* avatar = (LLVOAvatar*)character; + if (!avatar->isDead() && (avatar->getID() == avatar_id)) + { + render_pos = avatar->getRenderPosition(); + break; + } + } + } + else if (gAgentAvatarp.notNull() && gAgentAvatarp->isValid()) + { + render_pos = gAgentAvatarp->getRenderPosition(); + } + LLCoordGL screen_pos; + response["onscreen"] = LLViewerCamera::getInstance()->projectPosAgentToScreen(render_pos, screen_pos, false); + response["x"] = screen_pos.mX; + response["y"] = screen_pos.mY; +} diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h index 05724ff443..8801c9f7ea 100644 --- a/indra/newview/llagentlistener.h +++ b/indra/newview/llagentlistener.h @@ -67,6 +67,11 @@ private: void stopAnimation(LLSD const &event_data); void getAnimationInfo(LLSD const &event_data); + void getID(LLSD const& event_data); + void getNearbyAvatarsList(LLSD const& event_data); + void getNearbyObjectsList(LLSD const& event_data); + void getAgentScreenPos(LLSD const& event_data); + LLViewerObject * findObjectClosestTo( const LLVector3 & position, bool sit_target = false ) const; private: diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 75cd189980..423fe2469a 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -255,6 +255,10 @@ using namespace LL; #include "llcoproceduremanager.h" #include "llviewereventrecorder.h" +#include "rlvactions.h" +#include "rlvcommon.h" +#include "rlvhandler.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 @@ -269,10 +273,6 @@ using namespace LL; // define a self-registering event API object #include "llappviewerlistener.h" -#if LL_LINUX && LL_GTK -#include "glib.h" -#endif // (LL_LINUX) && LL_GTK - static LLAppViewerListener sAppViewerListener(LLAppViewer::instance); ////// Windows-specific includes to the bottom - nasty defines in these pollute the preprocessor @@ -1818,6 +1818,9 @@ bool LLAppViewer::cleanup() //ditch LLVOAvatarSelf instance gAgentAvatarp = NULL; + // Sanity check to catch cases where someone forgot to do an RlvActions::isRlvEnabled() check + LL_ERRS_IF(!RlvHandler::isEnabled() && RlvHandler::instanceExists()) << "RLV handler instance exists even though RLVa is disabled" << LL_ENDL; + LLNotifications::instance().clear(); // workaround for DEV-35406 crash on shutdown @@ -2912,6 +2915,14 @@ bool LLAppViewer::initConfiguration() gSavedSettings.setBOOL("RenderDebugGLSession", false); } + if (gSavedSettings.getBOOL("RenderDebugTextureLabelLocalFilesSession")) + { + gDebugTextureLabelLocalFilesSession = true; + // Texture label accounting for local files takes up memory in the + // background, so don't persist. + gSavedSettings.setBOOL("RenderDebugTextureLabelLocalFilesSession", false); + } + const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); if (skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) { @@ -3448,6 +3459,7 @@ LLSD LLAppViewer::getViewerInfo() const } #endif + info["RLV_VERSION"] = RlvActions::isRlvEnabled() ? Rlv::Strings::getVersionAbout() : "(disabled)"; info["OPENGL_VERSION"] = ll_safe_string((const char*)(glGetString(GL_VERSION))); // Settings diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index 7ce74649e2..3dbbda11f9 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -39,8 +39,9 @@ #include "llfindlocale.h" #include <exception> - +#ifdef LL_GLIB #include <gio/gio.h> +#endif #include <resolv.h> #if (__GLIBC__*1000 + __GLIBC_MINOR__) >= 2034 diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 6f6b89ea81..aff959d279 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -285,25 +285,18 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floate make_ui_sound("UISndStartIM"); } -/* AD *TODO: Is this function needed any more? - I fixed it a bit(added check for canCall), but it appears that it is not used - anywhere. Maybe it should be removed? // static -bool LLAvatarActions::isCalling(const LLUUID &id) +bool LLAvatarActions::canCall() { - if (id.isNull() || !canCall()) - { - return false; - } - - LLUUID session_id = gIMMgr->computeSessionID(IM_NOTHING_SPECIAL, id); - return (LLIMModel::getInstance()->findIMSession(session_id) != NULL); -}*/ + LLVoiceClient* voice_client = LLVoiceClient::getInstance(); + return voice_client->voiceEnabled() && voice_client->isVoiceWorking(); +} -//static -bool LLAvatarActions::canCall() +// static +bool LLAvatarActions::canCallTo(const LLUUID& id) { - return LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); + LLVoiceClient* voice_client = LLVoiceClient::getInstance(); + return voice_client->voiceEnabled() && voice_client->isVoiceWorking() && voice_client->getVoiceEnabled(id); } // static diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 1f5a42ed22..6f469e96ce 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -172,18 +172,15 @@ public: static bool canBlock(const LLUUID& id); /** - * Return true if the avatar is in a P2P voice call with a given user + * @return true if voice calls are available */ - /* AD *TODO: Is this function needed any more? - I fixed it a bit(added check for canCall), but it appears that it is not used - anywhere. Maybe it should be removed? - static bool isCalling(const LLUUID &id);*/ + static bool canCall(); /** * @return true if call to the resident can be made */ + static bool canCallTo(const LLUUID& id); - static bool canCall(); /** * Invite avatar to a group. */ diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 3e02820feb..bb5ee558ca 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -426,6 +426,7 @@ public: time_t current_time = time_corrected(); time_t message_time = (time_t)(current_time - LLFrameTimer::getElapsedSeconds() + mTime); + // Report abuse shouldn't use AM/PM, use 24-hour time time_string = "[" + LLTrans::getString("TimeMonth") + "]/[" + LLTrans::getString("TimeDay") + "]/[" + LLTrans::getString("TimeYear") + "] [" diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp index ed563cbec9..ed16a4b135 100644 --- a/indra/newview/llconversationlog.cpp +++ b/indra/newview/llconversationlog.cpp @@ -118,11 +118,21 @@ const std::string LLConversation::createTimestamp(const U64Seconds& utc_time) LLSD substitution; substitution["datetime"] = (S32)utc_time.value(); - timeStr = "["+LLTrans::getString ("TimeMonth")+"]/[" - +LLTrans::getString ("TimeDay")+"]/[" - +LLTrans::getString ("TimeYear")+"] [" - +LLTrans::getString ("TimeHour")+"]:[" - +LLTrans::getString ("TimeMin")+"]"; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + timeStr = "[" + LLTrans::getString("TimeMonth") + "]/[" + + LLTrans::getString("TimeDay") + "]/[" + + LLTrans::getString("TimeYear") + "] ["; + if (use_24h) + { + timeStr += LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + } + else + { + timeStr += LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; + } LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llcylinder.cpp b/indra/newview/llcylinder.cpp index c347d3e1be..d5f1e25151 100644 --- a/indra/newview/llcylinder.cpp +++ b/indra/newview/llcylinder.cpp @@ -29,7 +29,7 @@ #include "llcylinder.h" #include "llerror.h" -#include "math.h" +#include <cmath> #include "llmath.h" #include "noise.h" #include "v3math.h" diff --git a/indra/newview/lldirpicker.cpp b/indra/newview/lldirpicker.cpp index 51157fa430..032433434d 100644 --- a/indra/newview/lldirpicker.cpp +++ b/indra/newview/lldirpicker.cpp @@ -41,15 +41,17 @@ # include "llfilepicker.h" #endif -#ifdef LL_FLTK - #include "FL/Fl.H" - #include "FL/Fl_Native_File_Chooser.H" -#endif - #if LL_WINDOWS #include <shlobj.h> #endif +#if LL_NFD +#include "nfd.hpp" +#if LL_USE_SDL_WINDOW +#include "nfd_sdl2.h" +#endif +#endif + // // Implementation // @@ -70,7 +72,95 @@ bool LLDirPicker::check_local_file_access_enabled() return true; } -#if LL_WINDOWS +#if LL_NFD + +LLDirPicker::LLDirPicker() : + mFileName(nullptr), + mLocked(false) +{ + reset(); +} + +LLDirPicker::~LLDirPicker() +{ +} + + +void LLDirPicker::reset() +{ + mDir.clear(); +} + +bool LLDirPicker::getDir(std::string* filename, bool blocking) +{ + if( mLocked ) + { + return false; + } + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + bool success = false; + + if (blocking) + { + // Modal, so pause agent + send_agent_pause(); + } + + // initialize NFD + NFD::Guard nfdGuard; + + // auto-freeing memory + NFD::UniquePath outPath; + + nfdwindowhandle_t windowHandle = nfdwindowhandle_t(); +#if LL_USE_SDL_WINDOW + if(!NFD_GetNativeWindowFromSDLWindow((SDL_Window*)gViewerWindow->getPlatformWindow(), &windowHandle)) + { + windowHandle = nfdwindowhandle_t(); + } +#elif LL_WINDOWS + windowHandle = { NFD_WINDOW_HANDLE_TYPE_WINDOWS, gViewerWindow->getWindow()->getPlatformWindow() }; +#endif + + // show the dialog + nfdresult_t result = NFD::PickFolder(outPath, nullptr, windowHandle); + if (result == NFD_OKAY) + { + mDir = std::string(outPath.get()); + success = true; + } + else if (result == NFD_CANCEL) + { + LL_INFOS() << "User pressed cancel." << LL_ENDL; + } + else + { + LL_INFOS() << "DirPicker Error: " << NFD::GetError() << LL_ENDL; + } + + if (blocking) + { + send_agent_resume(); + + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + } + + return success; +} + +std::string LLDirPicker::getDirName() +{ + return mDir; +} + +#elif LL_WINDOWS LLDirPicker::LLDirPicker() : mFileName(NULL), @@ -219,28 +309,20 @@ LLDirPicker::LLDirPicker() : mFileName(NULL), mLocked(false) { -#ifndef LL_FLTK mFilePicker = new LLFilePicker(); -#endif reset(); } LLDirPicker::~LLDirPicker() { -#ifndef LL_FLTK delete mFilePicker; -#endif } void LLDirPicker::reset() { -#ifndef LL_FLTK if (mFilePicker) mFilePicker->reset(); -#else - mDir = ""; -#endif } bool LLDirPicker::getDir(std::string* filename, bool blocking) @@ -253,39 +335,16 @@ bool LLDirPicker::getDir(std::string* filename, bool blocking) return false; } -#ifdef LL_FLTK - gViewerWindow->getWindow()->beforeDialog(); - Fl_Native_File_Chooser flDlg; - flDlg.title(LLTrans::getString("choose_the_directory").c_str()); - flDlg.type(Fl_Native_File_Chooser::BROWSE_DIRECTORY ); - int res = flDlg.show(); - gViewerWindow->getWindow()->afterDialog(); - if( res == 0 ) - { - char const *pDir = flDlg.filename(0); - if( pDir ) - mDir = pDir; - } - else if( res == -1 ) - { - LL_WARNS() << "FLTK failed: " << flDlg.errmsg() << LL_ENDL; - } - return !mDir.empty(); -#endif return false; } std::string LLDirPicker::getDirName() { -#ifndef LL_FLTK if (mFilePicker) { return mFilePicker->getFirstFile(); } return ""; -#else - return mDir; -#endif } #else // not implemented @@ -322,7 +381,7 @@ std::queue<LLDirPickerThread*> LLDirPickerThread::sDeadQ; void LLDirPickerThread::getFile() { -#if LL_WINDOWS +#if LL_WINDOWS || (LL_NFD && !LL_DARWIN) start(); #else run(); @@ -332,7 +391,7 @@ void LLDirPickerThread::getFile() //virtual void LLDirPickerThread::run() { -#if LL_WINDOWS +#if LL_WINDOWS || (LL_NFD && !LL_DARWIN) bool blocking = false; #else bool blocking = true; // modal diff --git a/indra/newview/lldirpicker.h b/indra/newview/lldirpicker.h index 2ac3db7c2e..dc740caab2 100644 --- a/indra/newview/lldirpicker.h +++ b/indra/newview/lldirpicker.h @@ -77,10 +77,8 @@ private: #if LL_LINUX || LL_DARWIN // On Linux we just implement LLDirPicker on top of LLFilePicker -#ifndef LL_FLTK LLFilePicker *mFilePicker; #endif -#endif std::string* mFileName; diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index 5e676bc5b3..57def49539 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -296,7 +296,7 @@ void LLDrawPoolTerrain::renderFullShaderTextures() // GL_BLEND disabled by default drawLoop(); - // Disable multitexture + // Disable textures sShader->disableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP); sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL0); sShader->disableTexture(LLViewerShaderMgr::TERRAIN_DETAIL1); @@ -557,7 +557,7 @@ void LLDrawPoolTerrain::renderFullShaderPBR(bool use_local_materials) // GL_BLEND disabled by default drawLoop(); - // Disable multitexture + // Disable textures if (paint_type == TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE) { @@ -769,7 +769,7 @@ void LLDrawPoolTerrain::renderFull4TU() } LLVertexBuffer::unbind(); - // Disable multitexture + // Disable textures gGL.getTexUnit(3)->unbind(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(3)->disable(); gGL.getTexUnit(3)->activate(); @@ -949,7 +949,7 @@ void LLDrawPoolTerrain::renderFull2TU() // Restore blend state gGL.setSceneBlendType(LLRender::BT_ALPHA); - // Disable multitexture + // Disable textures gGL.getTexUnit(1)->unbind(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(1)->disable(); diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index 3639064cc4..ca0b58b611 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -36,9 +36,11 @@ #include "llviewercontrol.h" #include "llwindow.h" // beforeDialog() -#if LL_LINUX -#include "llwindowsdl.h" // for some X/GTK utils to help with filepickers -#include "llhttpconstants.h" // file picker uses some of thes constants on Linux +#if LL_NFD +#include "nfd.hpp" +#if LL_USE_SDL_WINDOW +#include "nfd_sdl2.h" +#endif #endif // @@ -80,7 +82,7 @@ LLFilePicker::LLFilePicker() { reset(); -#if LL_WINDOWS +#if LL_WINDOWS && !LL_NFD mOFN.lStructSize = sizeof(OPENFILENAMEW); mOFN.hwndOwner = NULL; // Set later mOFN.hInstance = NULL; @@ -165,7 +167,431 @@ void LLFilePicker::reset() mCurrentFile = 0; } +#if LL_NFD +std::vector<nfdfilteritem_t> LLFilePicker::setupFilter(ELoadFilter filter) +{ + std::vector<nfdfilteritem_t> filter_vec; + switch (filter) + { + case FFLOAD_EXE: #if LL_WINDOWS + filter_vec.emplace_back(nfdfilteritem_t{"Executables", "exe"}); +#endif + break; + case FFLOAD_ALL: + // Empty to allow picking all files by default + break; + case FFLOAD_WAV: + filter_vec.emplace_back(nfdfilteritem_t{"Sounds", "wav"}); + break; + case FFLOAD_IMAGE: + filter_vec.emplace_back(nfdfilteritem_t{"Images", "tga,bmp,jpg,jpeg,png"}); + break; + case FFLOAD_ANIM: + filter_vec.emplace_back(nfdfilteritem_t{"Animations", "bvh,anim"}); + break; + case FFLOAD_GLTF: + case FFLOAD_MATERIAL: + filter_vec.emplace_back(nfdfilteritem_t{"GLTF Files", "gltf,glb"}); + break; + case FFLOAD_COLLADA: + filter_vec.emplace_back(nfdfilteritem_t{"Scene", "dae"}); + break; + case FFLOAD_XML: + filter_vec.emplace_back(nfdfilteritem_t{"XML files", "xml"}); + break; + case FFLOAD_SLOBJECT: + filter_vec.emplace_back(nfdfilteritem_t{"Objects", "slobject"}); + break; + case FFLOAD_RAW: + filter_vec.emplace_back(nfdfilteritem_t{"RAW files", "raw"}); + break; + case FFLOAD_MODEL: + filter_vec.emplace_back(nfdfilteritem_t{"Model files", "dae"}); + break; + case FFLOAD_HDRI: + filter_vec.emplace_back(nfdfilteritem_t{"EXR files", "exr"}); + break; + case FFLOAD_MATERIAL_TEXTURE: + filter_vec.emplace_back(nfdfilteritem_t{"GLTF Import", "gltf,glb,tga,bmp,jpg,jpeg,png"}); + filter_vec.emplace_back(nfdfilteritem_t{"GLTF Files", "gltf,glb"}); + filter_vec.emplace_back(nfdfilteritem_t{"Images", "tga,bmp,jpg,jpeg,png"}); + break; + case FFLOAD_SCRIPT: + filter_vec.emplace_back(nfdfilteritem_t{"Script files (*.lsl)", "lsl"}); + break; + case FFLOAD_DICTIONARY: + filter_vec.emplace_back(nfdfilteritem_t{"Dictionary files", "dic,xcu"}); + break; + case FFLOAD_LUA: + filter_vec.emplace_back(nfdfilteritem_t{"Script files (*.lua)", "lua"}); + break; + default: + break; + } + return filter_vec; +} + +bool LLFilePicker::getOpenFile(ELoadFilter filter, bool blocking) +{ + if( mLocked ) + { + return false; + } + bool success = false; + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + // initialize NFD + NFD::Guard nfdGuard; + + // auto-freeing memory + NFD::UniquePath outPath; + + // prepare filters for the dialog + auto filterItem = setupFilter(filter); + + nfdwindowhandle_t windowHandle = nfdwindowhandle_t(); +#if LL_USE_SDL_WINDOW + if(!NFD_GetNativeWindowFromSDLWindow((SDL_Window*)gViewerWindow->getPlatformWindow(), &windowHandle)) + { + windowHandle = nfdwindowhandle_t(); + } +#elif LL_WINDOWS + windowHandle = { NFD_WINDOW_HANDLE_TYPE_WINDOWS, gViewerWindow->getWindow()->getPlatformWindow() }; +#endif + + if (blocking) + { + // Modal, so pause agent + send_agent_pause(); + } + + reset(); + + // show the dialog + nfdresult_t result = NFD::OpenDialog(outPath, filterItem.data(), narrow(filterItem.size()), nullptr, windowHandle); + if (result == NFD_OKAY) + { + mFiles.push_back(outPath.get()); + success = true; + } + + if (blocking) + { + send_agent_resume(); + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + } + + return success; +} + +bool LLFilePicker::getOpenFileModeless(ELoadFilter filter, + void (*callback)(bool, std::vector<std::string> &, void*), + void *userdata) +{ + if( mLocked ) + return false; + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + reset(); + LL_WARNS() << "NOT IMPLEMENTED" << LL_ENDL; + return false; +} + +bool LLFilePicker::getMultipleOpenFiles(ELoadFilter filter, bool blocking) +{ + if( mLocked ) + { + return false; + } + bool success = false; + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + // initialize NFD + NFD::Guard nfdGuard; + + auto filterItem = setupFilter(filter); + + reset(); + + if (blocking) + { + // Modal, so pause agent + send_agent_pause(); + } + + nfdwindowhandle_t windowHandle = nfdwindowhandle_t(); +#if LL_USE_SDL_WINDOW + if(!NFD_GetNativeWindowFromSDLWindow((SDL_Window*)gViewerWindow->getPlatformWindow(), &windowHandle)) + { + windowHandle = nfdwindowhandle_t(); + } +#elif LL_WINDOWS + windowHandle = { NFD_WINDOW_HANDLE_TYPE_WINDOWS, gViewerWindow->getWindow()->getPlatformWindow() }; +#endif + + // auto-freeing memory + NFD::UniquePathSet outPaths; + + // show the dialog + nfdresult_t result = NFD::OpenDialogMultiple(outPaths, filterItem.data(), narrow(filterItem.size()), nullptr, windowHandle); + if (result == NFD_OKAY) + { + LL_INFOS() << "Success!" << LL_ENDL; + + nfdpathsetsize_t numPaths; + NFD::PathSet::Count(outPaths, numPaths); + + nfdpathsetsize_t i; + for (i = 0; i < numPaths; ++i) + { + NFD::UniquePathSetPath path; + NFD::PathSet::GetPath(outPaths, i, path); + mFiles.push_back(path.get()); + LL_INFOS() << "Path " << i << ": " << path.get() << LL_ENDL; + } + success = true; + } + else if (result == NFD_CANCEL) + { + LL_INFOS() << "User pressed cancel." << LL_ENDL; + } + else + { + LL_INFOS() << "Error: " << NFD::GetError() << LL_ENDL; + } + + if (blocking) + { + send_agent_resume(); + + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + } + + return success; +} + +bool LLFilePicker::getMultipleOpenFilesModeless(ELoadFilter filter, + void (*callback)(bool, std::vector<std::string> &, void*), + void *userdata ) +{ + if( mLocked ) + return false; + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + reset(); + + LL_WARNS() << "NOT IMPLEMENTED" << LL_ENDL; + return false; +} + +bool LLFilePicker::getSaveFile(ESaveFilter filter, const std::string& filename, bool blocking) +{ + if( mLocked ) + { + return false; + } + bool success = false; + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + // initialize NFD + NFD::Guard nfdGuard; + + std::vector<nfdfilteritem_t> filter_vec; + std::string saved_filename = filename; + switch( filter ) + { + case FFSAVE_ALL: + filter_vec.emplace_back(nfdfilteritem_t{"WAV Sounds", "wav"}); + filter_vec.emplace_back(nfdfilteritem_t{"Targa, Bitmap Images", "tga,bmp"}); + break; + case FFSAVE_WAV: + if (filename.empty()) + { + saved_filename = "untitled.wav"; + } + filter_vec.emplace_back(nfdfilteritem_t{"WAV Sounds", "wav"}); + break; + case FFSAVE_TGA: + if (filename.empty()) + { + saved_filename = "untitled.tga"; + } + filter_vec.emplace_back(nfdfilteritem_t{"Targa Images", "tga"}); + break; + case FFSAVE_BMP: + if (filename.empty()) + { + saved_filename = "untitled.bmp"; + } + filter_vec.emplace_back(nfdfilteritem_t{"Bitmap Images", "bmp"}); + break; + case FFSAVE_PNG: + if (filename.empty()) + { + saved_filename = "untitled.png"; + } + filter_vec.emplace_back(nfdfilteritem_t{"PNG Images", "png"}); + break; + case FFSAVE_TGAPNG: + if (filename.empty()) + { + saved_filename = "untitled.png"; + } + + filter_vec.emplace_back(nfdfilteritem_t{"PNG Images", "png"}); + filter_vec.emplace_back(nfdfilteritem_t{"Targa Images", "tga"}); + filter_vec.emplace_back(nfdfilteritem_t{"JPEG Images", "jpg,jpeg"}); + filter_vec.emplace_back(nfdfilteritem_t{"Jpeg2000 Images", "j2c"}); + filter_vec.emplace_back(nfdfilteritem_t{"Bitmap Images", "bmp"}); + break; + case FFSAVE_JPEG: + if (filename.empty()) + { + saved_filename = "untitled.jpeg"; + } + filter_vec.emplace_back(nfdfilteritem_t{"JPEG Images", "jpg,jpeg"}); + break; + case FFSAVE_AVI: + if (filename.empty()) + { + saved_filename = "untitled.avi"; + } + filter_vec.emplace_back(nfdfilteritem_t{"AVI Movie File", "avi"}); + break; + case FFSAVE_ANIM: + if (filename.empty()) + { + saved_filename = "untitled.xaf"; + } + filter_vec.emplace_back(nfdfilteritem_t{"XAF Anim File", "xaf"}); + break; + case FFSAVE_XML: + if (filename.empty()) + { + saved_filename = "untitled.xml"; + } + filter_vec.emplace_back(nfdfilteritem_t{"XML File", "xml"}); + break; + case FFSAVE_COLLADA: + if (filename.empty()) + { + saved_filename = "untitled.collada"; + } + filter_vec.emplace_back(nfdfilteritem_t{"COLLADA File", "collada"}); + break; + case FFSAVE_RAW: + if (filename.empty()) + { + saved_filename = "untitled.raw"; + } + filter_vec.emplace_back(nfdfilteritem_t{"RAW files", "raw"}); + break; + case FFSAVE_J2C: + if (filename.empty()) + { + saved_filename = "untitled.j2c"; + } + filter_vec.emplace_back(nfdfilteritem_t{"Compressed Images", "j2c"}); + break; + case FFSAVE_SCRIPT: + if (filename.empty()) + { + saved_filename = "untitled.lsl"; + } + filter_vec.emplace_back(nfdfilteritem_t{"LSL Files", "lsl"}); + break; + default: + return false; + } + + nfdwindowhandle_t windowHandle = nfdwindowhandle_t(); +#if LL_USE_SDL_WINDOW + if(!NFD_GetNativeWindowFromSDLWindow((SDL_Window*)gViewerWindow->getPlatformWindow(), &windowHandle)) + { + windowHandle = nfdwindowhandle_t(); + } +#elif LL_WINDOWS + windowHandle = { NFD_WINDOW_HANDLE_TYPE_WINDOWS, gViewerWindow->getWindow()->getPlatformWindow() }; +#endif + + reset(); + + if (blocking) + { + // Modal, so pause agent + send_agent_pause(); + } + + { + NFD::UniquePath savePath; + + // show the dialog + nfdresult_t result = NFD::SaveDialog(savePath, filter_vec.data(), narrow(filter_vec.size()), nullptr, saved_filename.c_str(), windowHandle); + if (result == NFD_OKAY) { + mFiles.push_back(savePath.get()); + success = true; + } + gKeyboard->resetKeys(); + } + + if (blocking) + { + send_agent_resume(); + + // Account for the fact that the app has been stalled. + LLFrameTimer::updateFrameTime(); + } + + return success; +} + +bool LLFilePicker::getSaveFileModeless(ESaveFilter filter, + const std::string& filename, + void (*callback)(bool, std::string&, void*), + void *userdata) +{ + if( mLocked ) + return false; + + // if local file browsing is turned off, return without opening dialog + if ( check_local_file_access_enabled() == false ) + { + return false; + } + + reset(); + LL_WARNS() << "NOT IMPLEMENTED" << LL_ENDL; + return false; +} +#elif LL_WINDOWS bool LLFilePicker::setupFilter(ELoadFilter filter) { @@ -1104,488 +1530,6 @@ bool LLFilePicker::getSaveFileModeless(ESaveFilter filter, #elif LL_LINUX -# if LL_GTK - -// static -void LLFilePicker::add_to_selectedfiles(gpointer data, gpointer user_data) -{ - // We need to run g_filename_to_utf8 in the user's locale - std::string saved_locale(setlocale(LC_ALL, NULL)); - setlocale(LC_ALL, ""); - - LLFilePicker* picker = (LLFilePicker*) user_data; - GError *error = NULL; - gchar* filename_utf8 = g_filename_to_utf8((gchar*)data, - -1, NULL, NULL, &error); - if (error) - { - // *FIXME. - // This condition should really be notified to the user, e.g. - // through a message box. Just logging it is inappropriate. - - // g_filename_display_name is ideal, but >= glib 2.6, so: - // a hand-rolled hacky makeASCII which disallows control chars - std::string display_name; - for (const gchar *str = (const gchar *)data; *str; str++) - { - display_name += (char)((*str >= 0x20 && *str <= 0x7E) ? *str : '?'); - } - LL_WARNS() << "g_filename_to_utf8 failed on \"" << display_name << "\": " << error->message << LL_ENDL; - } - - if (filename_utf8) - { - picker->mFiles.push_back(std::string(filename_utf8)); - LL_DEBUGS() << "ADDED FILE " << filename_utf8 << LL_ENDL; - g_free(filename_utf8); - } - - setlocale(LC_ALL, saved_locale.c_str()); -} - -// static -void LLFilePicker::chooser_responder(GtkWidget *widget, gint response, gpointer user_data) -{ - LLFilePicker* picker = (LLFilePicker*)user_data; - - LL_DEBUGS() << "GTK DIALOG RESPONSE " << response << LL_ENDL; - - if (response == GTK_RESPONSE_ACCEPT) - { - GSList *file_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(widget)); - g_slist_foreach(file_list, (GFunc)add_to_selectedfiles, user_data); - g_slist_foreach(file_list, (GFunc)g_free, NULL); - g_slist_free (file_list); - } - - // let's save the extension of the last added file(considering current filter) - GtkFileFilter *gfilter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(widget)); - if(gfilter) - { - std::string filter = gtk_file_filter_get_name(gfilter); - - if(filter == LLTrans::getString("png_image_files")) - { - picker->mCurrentExtension = ".png"; - } - else if(filter == LLTrans::getString("targa_image_files")) - { - picker->mCurrentExtension = ".tga"; - } - } - - // set the default path for this usage context. - const char* cur_folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(widget)); - if (cur_folder != NULL) - { - picker->mContextToPathMap[picker->mCurContextName] = cur_folder; - } - - gtk_widget_destroy(widget); - gtk_main_quit(); -} - - -GtkWindow* LLFilePicker::buildFilePicker(bool is_save, bool is_folder, std::string context) -{ -#ifndef LL_MESA_HEADLESS - if (LLWindowSDL::ll_try_gtk_init()) - { - GtkWidget *win = NULL; - GtkFileChooserAction pickertype = - is_save? - (is_folder? - GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER : - GTK_FILE_CHOOSER_ACTION_SAVE) : - (is_folder? - GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER : - GTK_FILE_CHOOSER_ACTION_OPEN); - - win = gtk_file_chooser_dialog_new(NULL, NULL, - pickertype, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, - is_folder ? - GTK_STOCK_APPLY : - (is_save ? - GTK_STOCK_SAVE : - GTK_STOCK_OPEN), - GTK_RESPONSE_ACCEPT, - (gchar *)NULL); - mCurContextName = context; - - // get the default path for this usage context if it's been - // seen before. - std::map<std::string,std::string>::iterator - this_path = mContextToPathMap.find(context); - if (this_path != mContextToPathMap.end()) - { - gtk_file_chooser_set_current_folder - (GTK_FILE_CHOOSER(win), - this_path->second.c_str()); - } - -# if LL_X11 - // Make GTK tell the window manager to associate this - // dialog with our non-GTK raw X11 window, which should try - // to keep it on top etc. - Window XWindowID = LLWindowSDL::get_SDL_XWindowID(); - if (None != XWindowID) - { - gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin - GdkWindow *gdkwin = gdk_window_foreign_new(XWindowID); - gdk_window_set_transient_for(GTK_WIDGET(win)->window, - gdkwin); - } - else - { - LL_WARNS() << "Hmm, couldn't get xwid to use for transient." << LL_ENDL; - } -# endif //LL_X11 - - g_signal_connect (GTK_FILE_CHOOSER(win), - "response", - G_CALLBACK(LLFilePicker::chooser_responder), - this); - - gtk_window_set_modal(GTK_WINDOW(win), TRUE); - - /* GTK 2.6: if (is_folder) - gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(win), - TRUE); */ - - return GTK_WINDOW(win); - } - else - { - return NULL; - } -#else - return NULL; -#endif //LL_MESA_HEADLESS -} - -static void add_common_filters_to_gtkchooser(GtkFileFilter *gfilter, - GtkWindow *picker, - std::string filtername) -{ - gtk_file_filter_set_name(gfilter, filtername.c_str()); - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), - gfilter); - GtkFileFilter *allfilter = gtk_file_filter_new(); - gtk_file_filter_add_pattern(allfilter, "*"); - gtk_file_filter_set_name(allfilter, LLTrans::getString("all_files").c_str()); - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), allfilter); - gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(picker), gfilter); -} - -static std::string add_simple_pattern_filter_to_gtkchooser(GtkWindow *picker, - std::string pattern, - std::string filtername) -{ - GtkFileFilter *gfilter = gtk_file_filter_new(); - gtk_file_filter_add_pattern(gfilter, pattern.c_str()); - add_common_filters_to_gtkchooser(gfilter, picker, filtername); - return filtername; -} - -static std::string add_simple_mime_filter_to_gtkchooser(GtkWindow *picker, - std::string mime, - std::string filtername) -{ - GtkFileFilter *gfilter = gtk_file_filter_new(); - gtk_file_filter_add_mime_type(gfilter, mime.c_str()); - add_common_filters_to_gtkchooser(gfilter, picker, filtername); - return filtername; -} - -static std::string add_wav_filter_to_gtkchooser(GtkWindow *picker) -{ - return add_simple_mime_filter_to_gtkchooser(picker, "audio/x-wav", - LLTrans::getString("sound_files") + " (*.wav)"); -} - -static std::string add_anim_filter_to_gtkchooser(GtkWindow *picker) -{ - GtkFileFilter *gfilter = gtk_file_filter_new(); - gtk_file_filter_add_pattern(gfilter, "*.bvh"); - gtk_file_filter_add_pattern(gfilter, "*.anim"); - std::string filtername = LLTrans::getString("animation_files") + " (*.bvh; *.anim)"; - add_common_filters_to_gtkchooser(gfilter, picker, filtername); - return filtername; -} - -static std::string add_xml_filter_to_gtkchooser(GtkWindow *picker) -{ - return add_simple_pattern_filter_to_gtkchooser(picker, "*.xml", - LLTrans::getString("xml_files") + " (*.xml)"); -} - -static std::string add_collada_filter_to_gtkchooser(GtkWindow *picker) -{ - return add_simple_pattern_filter_to_gtkchooser(picker, "*.dae", - LLTrans::getString("scene_files") + " (*.dae)"); -} - -static std::string add_imageload_filter_to_gtkchooser(GtkWindow *picker) -{ - GtkFileFilter *gfilter = gtk_file_filter_new(); - gtk_file_filter_add_pattern(gfilter, "*.tga"); - gtk_file_filter_add_mime_type(gfilter, HTTP_CONTENT_IMAGE_JPEG.c_str()); - gtk_file_filter_add_mime_type(gfilter, HTTP_CONTENT_IMAGE_PNG.c_str()); - gtk_file_filter_add_mime_type(gfilter, HTTP_CONTENT_IMAGE_BMP.c_str()); - std::string filtername = LLTrans::getString("image_files") + " (*.tga; *.bmp; *.jpg; *.png)"; - add_common_filters_to_gtkchooser(gfilter, picker, filtername); - return filtername; -} - -static std::string add_script_filter_to_gtkchooser(GtkWindow *picker) -{ - return add_simple_mime_filter_to_gtkchooser(picker, HTTP_CONTENT_TEXT_PLAIN, - LLTrans::getString("script_files") + " (*.lsl)"); -} - -static std::string add_dictionary_filter_to_gtkchooser(GtkWindow *picker) -{ - return add_simple_mime_filter_to_gtkchooser(picker, HTTP_CONTENT_TEXT_PLAIN, - LLTrans::getString("dictionary_files") + " (*.dic; *.xcu)"); -} - -static std::string add_save_texture_filter_to_gtkchooser(GtkWindow *picker) -{ - GtkFileFilter *gfilter_tga = gtk_file_filter_new(); - GtkFileFilter *gfilter_png = gtk_file_filter_new(); - - gtk_file_filter_add_pattern(gfilter_tga, "*.tga"); - gtk_file_filter_add_mime_type(gfilter_png, "image/png"); - std::string caption = LLTrans::getString("save_texture_image_files") + " (*.tga; *.png)"; - gtk_file_filter_set_name(gfilter_tga, LLTrans::getString("targa_image_files").c_str()); - gtk_file_filter_set_name(gfilter_png, LLTrans::getString("png_image_files").c_str()); - - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), - gfilter_png); - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(picker), - gfilter_tga); - return caption; -} - -bool LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename, bool blocking ) -{ - bool rtn = false; - - // if local file browsing is turned off, return without opening dialog - if (!check_local_file_access_enabled()) - { - return false; - } - - gViewerWindow->getWindow()->beforeDialog(); - - reset(); - - GtkWindow* picker = buildFilePicker(true, false, "savefile"); - - if (picker) - { - std::string suggest_name = "untitled"; - std::string suggest_ext = ""; - std::string caption = LLTrans::getString("save_file_verb") + " "; - switch (filter) - { - case FFSAVE_WAV: - caption += add_wav_filter_to_gtkchooser(picker); - suggest_ext = ".wav"; - break; - case FFSAVE_TGA: - caption += add_simple_pattern_filter_to_gtkchooser - (picker, "*.tga", LLTrans::getString("targa_image_files") + " (*.tga)"); - suggest_ext = ".tga"; - break; - case FFSAVE_BMP: - caption += add_simple_mime_filter_to_gtkchooser - (picker, HTTP_CONTENT_IMAGE_BMP, LLTrans::getString("bitmap_image_files") + " (*.bmp)"); - suggest_ext = ".bmp"; - break; - case FFSAVE_PNG: - caption += add_simple_mime_filter_to_gtkchooser - (picker, "image/png", LLTrans::getString("png_image_files") + " (*.png)"); - suggest_ext = ".png"; - break; - case FFSAVE_TGAPNG: - caption += add_save_texture_filter_to_gtkchooser(picker); - suggest_ext = ".png"; - break; - case FFSAVE_AVI: - caption += add_simple_mime_filter_to_gtkchooser - (picker, "video/x-msvideo", - LLTrans::getString("avi_movie_file") + " (*.avi)"); - suggest_ext = ".avi"; - break; - case FFSAVE_ANIM: - caption += add_simple_pattern_filter_to_gtkchooser - (picker, "*.xaf", LLTrans::getString("xaf_animation_file") + " (*.xaf)"); - suggest_ext = ".xaf"; - break; - case FFSAVE_XML: - caption += add_simple_pattern_filter_to_gtkchooser - (picker, "*.xml", LLTrans::getString("xml_file") + " (*.xml)"); - suggest_ext = ".xml"; - break; - case FFSAVE_RAW: - caption += add_simple_pattern_filter_to_gtkchooser - (picker, "*.raw", LLTrans::getString("raw_file") + " (*.raw)"); - suggest_ext = ".raw"; - break; - case FFSAVE_J2C: - // *TODO: Should this be 'image/j2c' ? - caption += add_simple_mime_filter_to_gtkchooser - (picker, "images/jp2", - LLTrans::getString("compressed_image_files") + " (*.j2c)"); - suggest_ext = ".j2c"; - break; - case FFSAVE_SCRIPT: - caption += add_script_filter_to_gtkchooser(picker); - suggest_ext = ".lsl"; - break; - default:; - break; - } - - gtk_window_set_title(GTK_WINDOW(picker), caption.c_str()); - - if (filename.empty()) - { - suggest_name += suggest_ext; - - gtk_file_chooser_set_current_name - (GTK_FILE_CHOOSER(picker), - suggest_name.c_str()); - } - else - { - gtk_file_chooser_set_current_name - (GTK_FILE_CHOOSER(picker), filename.c_str()); - } - - gtk_widget_show_all(GTK_WIDGET(picker)); - - gtk_main(); - - rtn = (getFileCount() == 1); - - if(rtn && filter == FFSAVE_TGAPNG) - { - std::string selected_file = mFiles.back(); - mFiles.pop_back(); - mFiles.push_back(selected_file + mCurrentExtension); - } - } - - gViewerWindow->getWindow()->afterDialog(); - - return rtn; -} - -bool LLFilePicker::getOpenFile( ELoadFilter filter, bool blocking ) -{ - bool rtn = false; - - // if local file browsing is turned off, return without opening dialog - if (!check_local_file_access_enabled()) - { - return false; - } - - gViewerWindow->getWindow()->beforeDialog(); - - reset(); - - GtkWindow* picker = buildFilePicker(false, false, "openfile"); - - if (picker) - { - std::string caption = LLTrans::getString("load_file_verb") + " "; - std::string filtername = ""; - switch (filter) - { - case FFLOAD_WAV: - filtername = add_wav_filter_to_gtkchooser(picker); - break; - case FFLOAD_ANIM: - filtername = add_anim_filter_to_gtkchooser(picker); - break; - case FFLOAD_XML: - filtername = add_xml_filter_to_gtkchooser(picker); - break; - case FFLOAD_GLTF: - filtername = dead_code_should_blow_up_here(picker); - break; - case FFLOAD_COLLADA: - filtername = add_collada_filter_to_gtkchooser(picker); - break; - case FFLOAD_IMAGE: - filtername = add_imageload_filter_to_gtkchooser(picker); - break; - case FFLOAD_SCRIPT: - filtername = add_script_filter_to_gtkchooser(picker); - break; - case FFLOAD_DICTIONARY: - filtername = add_dictionary_filter_to_gtkchooser(picker); - break; - default:; - break; - } - - caption += filtername; - - gtk_window_set_title(GTK_WINDOW(picker), caption.c_str()); - - gtk_widget_show_all(GTK_WIDGET(picker)); - gtk_main(); - - rtn = (getFileCount() == 1); - } - - gViewerWindow->getWindow()->afterDialog(); - - return rtn; -} - -bool LLFilePicker::getMultipleOpenFiles( ELoadFilter filter, bool blocking) -{ - bool rtn = false; - - // if local file browsing is turned off, return without opening dialog - if (!check_local_file_access_enabled()) - { - return false; - } - - gViewerWindow->getWindow()->beforeDialog(); - - reset(); - - GtkWindow* picker = buildFilePicker(false, false, "openfile"); - - if (picker) - { - gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER(picker), - TRUE); - - gtk_window_set_title(GTK_WINDOW(picker), LLTrans::getString("load_files").c_str()); - - gtk_widget_show_all(GTK_WIDGET(picker)); - gtk_main(); - rtn = !mFiles.empty(); - } - - gViewerWindow->getWindow()->afterDialog(); - - return rtn; -} - -# else // LL_GTK - // Hacky stubs designed to facilitate fake getSaveFile and getOpenFile with // static results, when we don't have a real filepicker. @@ -1673,8 +1617,6 @@ bool LLFilePicker::getMultipleOpenFilesModeless(ELoadFilter filter, return false; } -#endif // LL_GTK - #else // not implemented bool LLFilePicker::getSaveFile( ESaveFilter filter, const std::string& filename ) diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h index e0bd32fe70..8c89570174 100644 --- a/indra/newview/llfilepicker.h +++ b/indra/newview/llfilepicker.h @@ -54,6 +54,10 @@ #include <commdlg.h> #endif +#if LL_NFD +#include "nfd.hpp" +#endif + class LLFilePicker { public: @@ -151,14 +155,18 @@ private: // is enabled and if not, tidy up and indicate we're not allowed to do this. bool check_local_file_access_enabled(); -#if LL_WINDOWS +#if LL_NFD + std::vector<nfdfilteritem_t> setupFilter(ELoadFilter filter); +#endif + +#if LL_WINDOWS && !LL_NFD OPENFILENAMEW mOFN; // for open and save dialogs WCHAR mFilesW[FILENAME_BUFFER_SIZE]; bool setupFilter(ELoadFilter filter); #endif -#if LL_DARWIN +#if LL_DARWIN && !LL_NFD S32 mPickOptions; std::vector<std::string> mFileVector; @@ -174,14 +182,6 @@ private: void *userdata); #endif -#if LL_FLTK - enum EType - { - eSaveFile, eOpenFile, eOpenMultiple - }; - bool openFileDialog( int32_t filter, bool blocking, EType aType ); -#endif - std::vector<std::string> mFiles; S32 mCurrentFile; bool mLocked; diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp index 7d098b2676..8897ad55e0 100644 --- a/indra/newview/llflexibleobject.cpp +++ b/indra/newview/llflexibleobject.cpp @@ -151,7 +151,6 @@ void LLVolumeImplFlexible::remapSections(LLFlexibleObjectSection *source, S32 so { S32 num_output_sections = 1<<dest_sections; LLVector3 scale = mVO->mDrawable->getScale(); - F32 source_section_length = scale.mV[VZ] / (F32)(1<<source_sections); F32 section_length = scale.mV[VZ] / (F32)num_output_sections; if (source_sections == -1) { @@ -183,6 +182,7 @@ void LLVolumeImplFlexible::remapSections(LLFlexibleObjectSection *source, S32 so // Iterate from right to left since it may be an in-place computation S32 step_shift = dest_sections-source_sections; S32 num_steps = 1<<step_shift; + F32 source_section_length = scale.mV[VZ] / (F32)(1<<source_sections); for (S32 section=num_output_sections-num_steps; section>=0; section -= num_steps) { LLFlexibleObjectSection *last_source_section = &source[section>>step_shift]; diff --git a/indra/newview/llfloaterbump.cpp b/indra/newview/llfloaterbump.cpp index d56e6cdf20..2a4f1ddd12 100644 --- a/indra/newview/llfloaterbump.cpp +++ b/indra/newview/llfloaterbump.cpp @@ -56,7 +56,7 @@ LLFloaterBump::LLFloaterBump(const LLSD& key) mCommitCallbackRegistrar.add("ShowAgentProfile", { boost::bind(&LLFloaterBump::showProfile, this), cb_info::UNTRUSTED_THROTTLE }); mCommitCallbackRegistrar.add("Avatar.InviteToGroup", { boost::bind(&LLFloaterBump::inviteToGroup, this), cb_info::UNTRUSTED_THROTTLE }); mCommitCallbackRegistrar.add("Avatar.Call", { boost::bind(&LLFloaterBump::startCall, this), cb_info::UNTRUSTED_BLOCK }); - mEnableCallbackRegistrar.add("Avatar.EnableCall", boost::bind(&LLAvatarActions::canCall)); + mEnableCallbackRegistrar.add("Avatar.EnableCall", { boost::bind(&LLFloaterBump::canCall, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Avatar.AddFriend", { boost::bind(&LLFloaterBump::addFriend, this), cb_info::UNTRUSTED_THROTTLE }); mEnableCallbackRegistrar.add("Avatar.EnableAddFriend", boost::bind(&LLFloaterBump::enableAddFriend, this)); mCommitCallbackRegistrar.add("Avatar.Mute", { boost::bind(&LLFloaterBump::muteAvatar, this), cb_info::UNTRUSTED_BLOCK }); @@ -214,6 +214,11 @@ void LLFloaterBump::startCall() LLAvatarActions::startCall(mItemUUID); } +bool LLFloaterBump::canCall() +{ + return LLAvatarActions::canCallTo(mItemUUID); +} + void LLFloaterBump::reportAbuse() { LLFloaterReporter::showFromAvatar(mItemUUID, "av_name"); diff --git a/indra/newview/llfloaterbump.h b/indra/newview/llfloaterbump.h index 53e730d73f..48d6f7e33e 100644 --- a/indra/newview/llfloaterbump.h +++ b/indra/newview/llfloaterbump.h @@ -52,6 +52,7 @@ public: void startIM(); void startCall(); + bool canCall(); void reportAbuse(); void showProfile(); void addFriend(); diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index 1e2d790cfc..ed24a8af57 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -1592,15 +1592,15 @@ bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_v } else if ("can_call" == item) { + if (is_single_select) + { + return LLAvatarActions::canCallTo(single_id); + } return LLAvatarActions::canCall(); } else if ("can_open_voice_conversation" == item) { - return is_single_select && LLAvatarActions::canCall(); - } - else if ("can_open_voice_conversation" == item) - { - return is_single_select && LLAvatarActions::canCall(); + return is_single_select && LLAvatarActions::canCallTo(single_id); } else if ("can_zoom_in" == item) { diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index 6c3e8391cd..f108b5198e 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -37,6 +37,7 @@ #include "llfloatersidepanelcontainer.h" #include "llfocusmgr.h" #include "lllogchat.h" +#include "llregex.h" #include "llresizebar.h" #include "llresizehandle.h" #include "lldraghandle.h" @@ -55,6 +56,7 @@ #include "llfloaterimnearbychatlistener.h" #include "llagent.h" // gAgent #include "llgesturemgr.h" +#include "llluamanager.h" #include "llmultigesture.h" #include "llkeyboard.h" #include "llanimationstates.h" @@ -606,6 +608,13 @@ void LLFloaterIMNearbyChat::sendChat( EChatType type ) if(!LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text)) { utf8_revised_text = utf8text; + // check if the message is /filename.lua and execute the Lua script + static const boost::regex is_lua_script("^/.*\\.luau?(?:\\s+\\S+)*$"); + if (ll_regex_match(utf8text, is_lua_script)) + { + LLLUAmanager::runScriptFile(utf8text.substr(1)); + utf8_revised_text.clear(); + } } } else diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index b2f2984c65..0855a628fb 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -39,6 +39,7 @@ #include "llchicletbar.h" #include "lldraghandle.h" #include "llemojidictionary.h" +#include "llemojihelper.h" #include "llfloaterreg.h" #include "llfloateremojipicker.h" #include "llfloaterimsession.h" @@ -298,6 +299,8 @@ bool LLFloaterIMSessionTab::postBuild() mEmojiPickerShowBtn = getChild<LLButton>("emoji_picker_show_btn"); mEmojiPickerShowBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerShowBtnClicked(); }); + mEmojiPickerShowBtn->setMouseDownCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerShowBtnDown(); }); + mEmojiCloseConn = LLEmojiHelper::instance().setCloseCallback([this](LLUICtrl*, const LLSD&) { onEmojiPickerClosed(); }); mGearBtn = getChild<LLButton>("gear_btn"); mAddBtn = getChild<LLButton>("add_btn"); @@ -526,8 +529,43 @@ void LLFloaterIMSessionTab::onEmojiRecentPanelToggleBtnClicked() void LLFloaterIMSessionTab::onEmojiPickerShowBtnClicked() { - mInputEditor->setFocus(true); - mInputEditor->showEmojiHelper(); + if (!mEmojiPickerShowBtn->getToggleState()) + { + mInputEditor->hideEmojiHelper(); + mInputEditor->setFocus(true); + mInputEditor->showEmojiHelper(); + mEmojiPickerShowBtn->setToggleState(true); // in case hideEmojiHelper closed a visible instance + } + else + { + mInputEditor->hideEmojiHelper(); + mEmojiPickerShowBtn->setToggleState(false); + } +} + +void LLFloaterIMSessionTab::onEmojiPickerShowBtnDown() +{ + if (mEmojiHelperLastCallbackFrame == LLFrameTimer::getFrameCount()) + { + // Helper gets closed by focus lost event on Down before before onEmojiPickerShowBtnDown + // triggers. + // If this condition is true, user pressed button and it was 'toggled' during press, + // restore 'toggled' state so that button will not reopen helper. + mEmojiPickerShowBtn->setToggleState(true); + } +} + +void LLFloaterIMSessionTab::onEmojiPickerClosed() +{ + if (mEmojiPickerShowBtn->getToggleState()) + { + mEmojiPickerShowBtn->setToggleState(false); + // Helper gets closed by focus lost event on Down before onEmojiPickerShowBtnDown + // triggers. If mEmojiHelperLastCallbackFrame is set and matches Down, means close + // was triggered by user's press. + // A bit hacky, but I can't think of a better way to handle this without rewriting helper. + mEmojiHelperLastCallbackFrame = LLFrameTimer::getFrameCount(); + } } void LLFloaterIMSessionTab::initEmojiRecentPanel() @@ -591,8 +629,19 @@ void LLFloaterIMSessionTab::deleteAllChildren() std::string LLFloaterIMSessionTab::appendTime() { - std::string timeStr = "[" + LLTrans::getString("TimeHour") + "]:" - "[" + LLTrans::getString("TimeMin") + "]"; + std::string timeStr; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr = "[" + LLTrans::getString("TimeHour") + "]:" + "[" + LLTrans::getString("TimeMin") + "]"; + } + else + { + timeStr = "[" + LLTrans::getString("TimeHour12") + "]:" + "[" + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; + } LLSD substitution; substitution["datetime"] = (S32)time_corrected(); diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h index bee5c8c2c4..890c920bbe 100644 --- a/indra/newview/llfloaterimsessiontab.h +++ b/indra/newview/llfloaterimsessiontab.h @@ -227,6 +227,8 @@ private: void onEmojiRecentPanelToggleBtnClicked(); void onEmojiPickerShowBtnClicked(); + void onEmojiPickerShowBtnDown(); + void onEmojiPickerClosed(); void initEmojiRecentPanel(); void onEmojiRecentPanelFocusReceived(); void onEmojiRecentPanelFocusLost(); @@ -241,6 +243,9 @@ private: S32 mInputEditorPad; S32 mChatLayoutPanelHeight; S32 mFloaterHeight; + + boost::signals2::connection mEmojiCloseConn; + U32 mEmojiHelperLastCallbackFrame = { 0 }; }; diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp index 0f1eb0cef0..5dea46843c 100644 --- a/indra/newview/llfloaterinspect.cpp +++ b/indra/newview/llfloaterinspect.cpp @@ -220,7 +220,8 @@ void LLFloaterInspect::refresh() } time_t timestamp = (time_t) (obj->mCreationDate/1000000); - std::string timeStr = getString("timeStamp"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string timeStr = use_24h ? getString("timeStamp") : getString("timeStampAMPM"); LLSD substitution; substitution["datetime"] = (S32) timestamp; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 52a3e78d04..5c5219bcdd 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -733,7 +733,8 @@ void LLPanelLandGeneral::refresh() // Display claim date time_t claim_date = parcel->getClaimDate(); - std::string claim_date_str = getString("time_stamp_template"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string claim_date_str = use_24h ? getString("time_stamp_template") : getString("time_stamp_template_ampm"); LLSD substitution; substitution["datetime"] = (S32) claim_date; LLStringUtil::format (claim_date_str, substitution); diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp index 06877df816..362a02a642 100644 --- a/indra/newview/llfloaterluadebug.cpp +++ b/indra/newview/llfloaterluadebug.cpp @@ -58,7 +58,9 @@ bool LLFloaterLUADebug::postBuild() .listen("LLFloaterLUADebug", [mResultOutput=mResultOutput](const LLSD& data) { - mResultOutput->pasteTextWithLinebreaks(data.asString()); + LLCachedControl<bool> show_source_info(gSavedSettings, "LuaDebugShowSource", false); + std::string source_info = show_source_info ? data["source_info"].asString() : ""; + mResultOutput->pasteTextWithLinebreaks(stringize(data["level"].asString(), source_info, data["msg"].asString()), true); mResultOutput->addLineBreakChar(true); return false; }); diff --git a/indra/newview/llfloaterluascripts.cpp b/indra/newview/llfloaterluascripts.cpp index 0eba45ec29..6b3d87543a 100644 --- a/indra/newview/llfloaterluascripts.cpp +++ b/indra/newview/llfloaterluascripts.cpp @@ -51,9 +51,9 @@ LLFloaterLUAScripts::LLFloaterLUAScripts(const LLSD &key) }, cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Script.Terminate", {[this](LLUICtrl*, const LLSD &userdata) { - if (mScriptList->hasSelectedItem()) + std::vector<LLSD> coros = mScriptList->getAllSelectedValues(); + for (auto coro_name : coros) { - std::string coro_name = mScriptList->getSelectedValue(); LLCoros::instance().killreq(coro_name); } }, cb_info::UNTRUSTED_BLOCK }); @@ -97,7 +97,7 @@ void LLFloaterLUAScripts::draw() void LLFloaterLUAScripts::populateScriptList() { S32 prev_pos = mScriptList->getScrollPos(); - LLSD prev_selected = mScriptList->getSelectedValue(); + std::vector<LLSD> prev_selected = mScriptList->getAllSelectedValues(); mScriptList->clearRows(); mScriptList->updateColumns(true); std::map<std::string, std::string> scripts = LLLUAmanager::getScriptNames(); @@ -112,7 +112,10 @@ void LLFloaterLUAScripts::populateScriptList() mScriptList->addElement(row); } mScriptList->setScrollPos(prev_pos); - mScriptList->setSelectedByValue(prev_selected, true); + for (auto value : prev_selected) + { + mScriptList->setSelectedByValue(value, true); + } } void LLFloaterLUAScripts::onScrollListRightClicked(LLUICtrl *ctrl, S32 x, S32 y) @@ -120,10 +123,13 @@ void LLFloaterLUAScripts::onScrollListRightClicked(LLUICtrl *ctrl, S32 x, S32 y) LLScrollListItem *item = mScriptList->hitItem(x, y); if (item) { - mScriptList->selectItemAt(x, y, MASK_NONE); + if (!item->getSelected()) + mScriptList->selectItemAt(x, y, MASK_NONE); + auto menu = mContextMenuHandle.get(); if (menu) { + menu->setItemEnabled(std::string("open_folder"), (mScriptList->getNumSelected() == 1)); menu->show(x, y); LLMenuGL::showPopup(this, menu, x, y); } diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 0c96c93d31..d7ac8f0552 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -473,6 +473,8 @@ bool LLFloaterPreference::postBuild() getChild<LLUICtrl>("log_path_string")->setEnabled(false); // make it read-only but selectable getChild<LLComboBox>("language_combobox")->setCommitCallback(boost::bind(&LLFloaterPreference::onLanguageChange, this)); + mTimeFormatCombobox = getChild<LLComboBox>("time_format_combobox"); + mTimeFormatCombobox->setCommitCallback(boost::bind(&LLFloaterPreference::onTimeFormatChange, this)); getChild<LLComboBox>("FriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"FriendIMOptions")); getChild<LLComboBox>("NonFriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"NonFriendIMOptions")); @@ -1108,6 +1110,13 @@ void LLFloaterPreference::onLanguageChange() } } +void LLFloaterPreference::onTimeFormatChange() +{ + std::string val = mTimeFormatCombobox->getValue(); + gSavedSettings.setBOOL("Use24HourClock", val == "1"); + onLanguageChange(); +} + void LLFloaterPreference::onNotificationsChange(const std::string& OptionName) { mNotificationOptions[OptionName] = getChild<LLComboBox>(OptionName)->getSelectedItemLabel(); @@ -1331,6 +1340,8 @@ void LLFloaterPreference::refresh() advanced->refresh(); } updateClickActionViews(); + + mTimeFormatCombobox->selectByValue(gSavedSettings.getBOOL("Use24HourClock") ? "1" : "0"); } void LLFloaterPreference::onCommitWindowedMode() diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index e06e758e3a..d5bd43e0ab 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -124,6 +124,7 @@ protected: void onClickClearCache(); // Clear viewer texture cache, file cache on next startup void onClickBrowserClearCache(); // Clear web history and caches as well as viewer caches above void onLanguageChange(); + void onTimeFormatChange(); void onNotificationsChange(const std::string& OptionName); void onNameTagOpacityChange(const LLSD& newvalue); @@ -242,6 +243,7 @@ private: LLButton* mDeleteTranscriptsBtn = nullptr; LLButton* mEnablePopupBtn = nullptr; LLButton* mDisablePopupBtn = nullptr; + LLComboBox* mTimeFormatCombobox = nullptr; std::unique_ptr< ll::prefs::SearchData > mSearchData; bool mSearchDataDirty; diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 165fb5ef88..ac3e942e15 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -148,40 +148,6 @@ public: static LLSD getIDs( sparam_t::const_iterator it, sparam_t::const_iterator end, S32 count ); }; - -/* -void unpack_request_params( - LLMessageSystem* msg, - LLDispatcher::sparam_t& strings, - LLDispatcher::iparam_t& integers) -{ - char str_buf[MAX_STRING]; - S32 str_count = msg->getNumberOfBlocksFast(_PREHASH_StringData); - S32 i; - for (i = 0; i < str_count; ++i) - { - // we treat the SParam as binary data (since it might be an - // LLUUID in compressed form which may have embedded \0's,) - str_buf[0] = '\0'; - S32 data_size = msg->getSizeFast(_PREHASH_StringData, i, _PREHASH_SParam); - if (data_size >= 0) - { - msg->getBinaryDataFast(_PREHASH_StringData, _PREHASH_SParam, - str_buf, data_size, i, MAX_STRING - 1); - strings.push_back(std::string(str_buf, data_size)); - } - } - - U32 int_buf; - S32 int_count = msg->getNumberOfBlocksFast(_PREHASH_IntegerData); - for (i = 0; i < int_count; ++i) - { - msg->getU32("IntegerData", "IParam", int_buf, i); - integers.push_back(int_buf); - } -} -*/ - class LLPanelRegionEnvironment : public LLPanelEnvironmentInfo { public: @@ -326,8 +292,8 @@ void LLFloaterRegionInfo::onOpen(const LLSD& key) disableTabCtrls(); return; } - refreshFromRegion(gAgent.getRegion()); - requestRegionInfo(); + refreshFromRegion(gAgent.getRegion(), ERefreshFromRegionPhase::BeforeRequestRegionInfo); + requestRegionInfo(true); if (!mGodLevelChangeSlot.connected()) { @@ -347,12 +313,14 @@ void LLFloaterRegionInfo::onRegionChanged() { if (getVisible()) //otherwise onOpen will do request { - requestRegionInfo(); + requestRegionInfo(false); } } -void LLFloaterRegionInfo::requestRegionInfo() +void LLFloaterRegionInfo::requestRegionInfo(bool is_opening) { + mIsRegionInfoRequestedFromOpening = is_opening; + LLTabContainer* tab = findChild<LLTabContainer>("region_panels"); if (tab) { @@ -523,8 +491,7 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg) panel->getChild<LLSpinCtrl>("agent_limit_spin")->setMaxValue((F32)hard_agent_limit); - LLPanelRegionGeneralInfo* panel_general = LLFloaterRegionInfo::getPanelGeneral(); - if (panel) + if (LLPanelRegionGeneralInfo* panel_general = LLFloaterRegionInfo::getPanelGeneral()) { panel_general->setObjBonusFactor(object_bonus_factor); } @@ -537,6 +504,18 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg) panel->getChildView("access_combo")->setEnabled(gAgent.isGodlike() || (region && region->canManageEstate() && !teen_grid)); panel->setCtrlsEnabled(allow_modify); + panel->getChild<LLLineEditor>("estate_id")->setValue((S32)region_info.mEstateID); + + if (region) + { + panel->getChild<LLLineEditor>("grid_position_x")->setValue((S32)(region->getOriginGlobal()[VX] / 256)); + panel->getChild<LLLineEditor>("grid_position_y")->setValue((S32)(region->getOriginGlobal()[VY] / 256)); + } + else + { + panel->getChild<LLLineEditor>("grid_position_x")->setDefaultText(); + panel->getChild<LLLineEditor>("grid_position_y")->setDefaultText(); + } // DEBUG PANEL panel = tab->getChild<LLPanel>("Debug"); @@ -561,21 +540,25 @@ void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg) { // Note: region info also causes LLRegionInfoModel::instance().update(msg); -> requestRegion(); -> changed message // we need to know env version here and in update(msg) to know when to request and when not to, when to filter 'changed' - floater->refreshFromRegion(gAgent.getRegion()); + ERefreshFromRegionPhase phase = floater->mIsRegionInfoRequestedFromOpening ? + ERefreshFromRegionPhase::AfterRequestRegionInfo : + ERefreshFromRegionPhase::NotFromFloaterOpening; + floater->refreshFromRegion(gAgent.getRegion(), phase); } // else will rerequest on onOpen either way } // static -void LLFloaterRegionInfo::sRefreshFromRegion(LLViewerRegion* region) +void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region) { - if (region != gAgent.getRegion()) { return; } - - LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance<LLFloaterRegionInfo>("region_info"); - if (!floater) { return; } + if (region != gAgent.getRegion()) + return; - if (floater->getVisible() && region == gAgent.getRegion()) + if (LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance<LLFloaterRegionInfo>("region_info")) { - floater->refreshFromRegion(region); + if (floater->getVisible() && region == gAgent.getRegion()) + { + floater->refreshFromRegion(region, ERefreshFromRegionPhase::NotFromFloaterOpening); + } } } @@ -682,7 +665,7 @@ void LLFloaterRegionInfo::onTabSelected(const LLSD& param) active_panel->onOpen(LLSD()); } -void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region) +void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { if (!region) { @@ -692,7 +675,7 @@ void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region) // call refresh from region on all panels for (const auto& infoPanel : mInfoPanels) { - infoPanel->refreshFromRegion(region); + infoPanel->refreshFromRegion(region, phase); } mEnvironmentPanel->refreshFromRegion(region); } @@ -725,7 +708,7 @@ void LLFloaterRegionInfo::onGodLevelChange(U8 god_level) LLFloaterRegionInfo* floater = LLFloaterReg::getTypedInstance<LLFloaterRegionInfo>("region_info"); if (floater && floater->getVisible()) { - refreshFromRegion(gAgent.getRegion()); + refreshFromRegion(gAgent.getRegion(), ERefreshFromRegionPhase::NotFromFloaterOpening); } } @@ -795,7 +778,7 @@ void LLPanelRegionInfo::updateChild(LLUICtrl* child_ctr) } // virtual -bool LLPanelRegionInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { if (region) mHost = region->getHost(); return true; @@ -823,12 +806,10 @@ void LLPanelRegionInfo::sendEstateOwnerMessage( } else { - strings_t::const_iterator it = strings.begin(); - strings_t::const_iterator end = strings.end(); - for(; it != end; ++it) + for (const std::string& string : strings) { msg->nextBlock("ParamList"); - msg->addString("Parameter", *it); + msg->addString("Parameter", string); } } msg->sendReliable(mHost); @@ -887,13 +868,16 @@ void LLPanelRegionInfo::onClickManageRestartSchedule() ///////////////////////////////////////////////////////////////////////////// // LLPanelRegionGeneralInfo // -bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { bool allow_modify = gAgent.isGodlike() || (region && region->canManageEstate()); setCtrlsEnabled(allow_modify); getChildView("apply_btn")->setEnabled(false); getChildView("access_text")->setEnabled(allow_modify); // getChildView("access_combo")->setEnabled(allow_modify); + getChildView("estate_id")->setEnabled(false); + getChildView("grid_position_x")->setEnabled(false); + getChildView("grid_position_y")->setEnabled(false); // now set in processRegionInfo for teen grid detection getChildView("kick_btn")->setEnabled(allow_modify); getChildView("kick_all_btn")->setEnabled(allow_modify); @@ -904,7 +888,7 @@ bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region) // Data gets filled in by processRegionInfo - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } bool LLPanelRegionGeneralInfo::postBuild() @@ -1173,7 +1157,7 @@ bool LLPanelRegionDebugInfo::postBuild() } // virtual -bool LLPanelRegionDebugInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionDebugInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { bool allow_modify = gAgent.isGodlike() || (region && region->canManageEstate()); setCtrlsEnabled(allow_modify); @@ -1191,7 +1175,7 @@ bool LLPanelRegionDebugInfo::refreshFromRegion(LLViewerRegion* region) getChildView("cancel_restart_btn")->setEnabled(allow_modify); getChildView("region_debug_console_btn")->setEnabled(allow_modify); - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } // virtual @@ -1233,7 +1217,7 @@ void LLPanelRegionDebugInfo::callbackAvatarID(const uuid_vec_t& ids, const std:: if (ids.empty() || names.empty()) return; mTargetAvatar = ids[0]; getChild<LLUICtrl>("target_avatar_name")->setValue(LLSD(names[0].getCompleteName())); - refreshFromRegion( gAgent.getRegion() ); + refreshFromRegion(gAgent.getRegion(), ERefreshFromRegionPhase::NotFromFloaterOpening); } // static @@ -1675,7 +1659,7 @@ void LLPanelRegionTerrainInfo::updateForMaterialType() } // virtual -bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { bool owner_or_god = gAgent.isGodlike() || (region && (region->getOwner() == gAgent.getID())); @@ -1819,7 +1803,7 @@ bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region) getChildView("upload_raw_btn")->setEnabled(owner_or_god); getChildView("bake_terrain_btn")->setEnabled(owner_or_god); - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } @@ -2289,25 +2273,27 @@ void LLPanelEstateInfo::updateControls(LLViewerRegion* region) refresh(); } -bool LLPanelEstateInfo::refreshFromRegion(LLViewerRegion* region) +bool LLPanelEstateInfo::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { updateControls(region); // let the parent class handle the general data collection. - bool rv = LLPanelRegionInfo::refreshFromRegion(region); - - // We want estate info. To make sure it works across region - // boundaries and multiple packets, we add a serial number to the - // integers and track against that on update. - strings_t strings; - //integers_t integers; - //LLFloaterRegionInfo::incrementSerial(); - LLFloaterRegionInfo::nextInvoice(); - LLUUID invoice(LLFloaterRegionInfo::getLastInvoice()); - //integers.push_back(LLFloaterRegionInfo::());::getPanelEstate(); + bool rv = LLPanelRegionInfo::refreshFromRegion(region, phase); + if (phase != ERefreshFromRegionPhase::BeforeRequestRegionInfo) + { + // We want estate info. To make sure it works across region + // boundaries and multiple packets, we add a serial number to the + // integers and track against that on update. + strings_t strings; + //integers_t integers; + //LLFloaterRegionInfo::incrementSerial(); + LLFloaterRegionInfo::nextInvoice(); + LLUUID invoice(LLFloaterRegionInfo::getLastInvoice()); + //integers.push_back(LLFloaterRegionInfo::());::getPanelEstate(); - sendEstateOwnerMessage(gMessageSystem, "getinfo", invoice, strings); + sendEstateOwnerMessage(gMessageSystem, "getinfo", invoice, strings); + } refresh(); @@ -2528,7 +2514,7 @@ LLPanelEstateCovenant::LLPanelEstateCovenant() } // virtual -bool LLPanelEstateCovenant::refreshFromRegion(LLViewerRegion* region) +bool LLPanelEstateCovenant::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { LLTextBox* region_name = getChild<LLTextBox>("region_name_text"); if (region_name) @@ -2574,13 +2560,17 @@ bool LLPanelEstateCovenant::refreshFromRegion(LLViewerRegion* region) getChild<LLButton>("reset_covenant")->setEnabled(gAgent.isGodlike() || (region && region->canManageEstate())); // let the parent class handle the general data collection. - bool rv = LLPanelRegionInfo::refreshFromRegion(region); - LLMessageSystem *msg = gMessageSystem; - msg->newMessage("EstateCovenantRequest"); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID()); - msg->sendReliable(region->getHost()); + bool rv = LLPanelRegionInfo::refreshFromRegion(region, phase); + + if (phase != ERefreshFromRegionPhase::AfterRequestRegionInfo) + { + gMessageSystem->newMessage("EstateCovenantRequest"); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->sendReliable(region->getHost()); + } + return rv; } @@ -3128,7 +3118,7 @@ std::string LLPanelRegionExperiences::regionCapabilityQuery(LLViewerRegion* regi return region->getCapability(cap); } -bool LLPanelRegionExperiences::refreshFromRegion(LLViewerRegion* region) +bool LLPanelRegionExperiences::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { bool allow_modify = gAgent.isGodlike() || (region && region->canManageEstate()); @@ -3151,10 +3141,13 @@ bool LLPanelRegionExperiences::refreshFromRegion(LLViewerRegion* region) mTrusted->loading(); mTrusted->setReadonly(!allow_modify); - LLExperienceCache::instance().getRegionExperiences(boost::bind(&LLPanelRegionExperiences::regionCapabilityQuery, region, _1), - boost::bind(&LLPanelRegionExperiences::infoCallback, getDerivedHandle<LLPanelRegionExperiences>(), _1)); + if (phase != ERefreshFromRegionPhase::AfterRequestRegionInfo) + { + LLExperienceCache::instance().getRegionExperiences(boost::bind(&LLPanelRegionExperiences::regionCapabilityQuery, region, _1), + boost::bind(&LLPanelRegionExperiences::infoCallback, getDerivedHandle<LLPanelRegionExperiences>(), _1)); + } - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } LLSD LLPanelRegionExperiences::addIds(LLPanelExperienceListEditor* panel) @@ -4181,35 +4174,32 @@ void LLPanelEstateAccess::copyListToClipboard(std::string list_name) { LLPanelEstateAccess* panel = LLFloaterRegionInfo::getPanelAccess(); if (!panel) return; - LLNameListCtrl* name_list = panel->getChild<LLNameListCtrl>(list_name); - if (!name_list) return; + LLNameListCtrl* name_list = panel->getChild<LLNameListCtrl>(list_name); std::vector<LLScrollListItem*> list_vector = name_list->getAllData(); - if (list_vector.size() == 0) return; + if (list_vector.empty()) + return; LLSD::String list_to_copy; - for (std::vector<LLScrollListItem*>::const_iterator iter = list_vector.begin(); - iter != list_vector.end(); - iter++) + for (LLScrollListItem* item : list_vector) { - LLScrollListItem *item = (*iter); if (item) { + if (!list_to_copy.empty()) + { + list_to_copy += "\n"; + } list_to_copy += item->getColumn(0)->getValue().asString(); } - if (std::next(iter) != list_vector.end()) - { - list_to_copy += "\n"; - } } LLClipboard::instance().copyToClipboard(utf8str_to_wstring(list_to_copy), 0, static_cast<S32>(list_to_copy.length())); } -bool LLPanelEstateAccess::refreshFromRegion(LLViewerRegion* region) +bool LLPanelEstateAccess::refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) { updateLists(); - return LLPanelRegionInfo::refreshFromRegion(region); + return LLPanelRegionInfo::refreshFromRegion(region, phase); } //========================================================================= diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index 65c1291728..119f7c98f3 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -71,6 +71,13 @@ class LLPanelRegionEnvironment; class LLEventTimer; +enum class ERefreshFromRegionPhase +{ + NotFromFloaterOpening, + BeforeRequestRegionInfo, + AfterRequestRegionInfo +}; + class LLFloaterRegionInfo : public LLFloater { friend class LLFloaterReg; @@ -85,7 +92,7 @@ public: // get and process region info if necessary. static void processRegionInfo(LLMessageSystem* msg); - static void sRefreshFromRegion(LLViewerRegion* region); + static void refreshFromRegion(LLViewerRegion* region); static const LLUUID& getLastInvoice() { return sRequestInvoice; } static void nextInvoice() { sRequestInvoice.generate(); } @@ -104,7 +111,7 @@ public: void refresh() override; void onRegionChanged(); - void requestRegionInfo(); + void requestRegionInfo(bool is_opening); void enableTopButtons(); void disableTopButtons(); @@ -116,7 +123,7 @@ private: protected: void onTabSelected(const LLSD& param); void disableTabCtrls(); - void refreshFromRegion(LLViewerRegion* region); + void refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase); void onGodLevelChange(U8 god_level); // member data @@ -124,6 +131,7 @@ protected: typedef std::vector<LLPanelRegionInfo*> info_panels_t; info_panels_t mInfoPanels; LLPanelRegionEnvironment *mEnvironmentPanel; + bool mIsRegionInfoRequestedFromOpening { false }; //static S32 sRequestSerial; // serial # of last EstateOwnerRequest static LLUUID sRequestInvoice; @@ -144,7 +152,7 @@ public: void onChangeAnything(); static void onChangeText(LLLineEditor* caller, void* user_data); - virtual bool refreshFromRegion(LLViewerRegion* region); + virtual bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase); virtual bool estateUpdate(LLMessageSystem* msg) { return true; } bool postBuild() override; @@ -190,7 +198,7 @@ public: : LLPanelRegionInfo() {} ~LLPanelRegionGeneralInfo() {} - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; bool postBuild() override; @@ -222,7 +230,7 @@ public: bool postBuild() override; - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; protected: bool sendUpdate() override; @@ -254,8 +262,8 @@ public: bool postBuild() override; - bool refreshFromRegion(LLViewerRegion* region) override; // refresh local settings from region update from simulator - void setEnvControls(bool available); // Whether environment settings are available for this region + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; // refresh local settings from region update from simulator + void setEnvControls(bool available); // Whether environment settings are available for this region bool validateTextureSizes(); bool validateMaterials(); @@ -327,7 +335,7 @@ public: static void updateEstateName(const std::string& name); static void updateEstateOwnerName(const std::string& name); - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; bool estateUpdate(LLMessageSystem* msg) override; bool postBuild() override; @@ -364,7 +372,7 @@ public: bool postBuild() override; void updateChild(LLUICtrl* child_ctrl) override; - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; bool estateUpdate(LLMessageSystem* msg) override; // LLView overrides @@ -430,7 +438,7 @@ public: static void sendEstateExperienceDelta(U32 flags, const LLUUID& agent_id); static void infoCallback(LLHandle<LLPanelRegionExperiences> handle, const LLSD& content); - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; void sendPurchaseRequest()const; void processResponse( const LLSD& content ); @@ -470,7 +478,7 @@ public: void setPendingUpdate(bool pending) { mPendingUpdate = pending; } bool getPendingUpdate() { return mPendingUpdate; } - bool refreshFromRegion(LLViewerRegion* region) override; + bool refreshFromRegion(LLViewerRegion* region, ERefreshFromRegionPhase phase) override; private: void onClickAddAllowedAgent(); diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index dda7266220..4674cd68b6 100755 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -486,8 +486,11 @@ void LLFloaterWorldMap::onOpen(const LLSD& key) const LLUUID landmark_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); LLInventoryModelBackgroundFetch::instance().start(landmark_folder_id); - mLocationEditor->setFocus( true); - gFocusMgr.triggerFocusFlash(); + if (hasFocus()) + { + mLocationEditor->setFocus( true); + gFocusMgr.triggerFocusFlash(); + } buildAvatarIDList(); buildLandmarkIDLists(); diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index 590cd09a31..e050fb77e0 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -422,6 +422,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, U8 *binary_bucket, S32 binary_bucket_size, LLHost &sender, + LLSD metadata, LLUUID aux_id) { LLChat chat; @@ -451,6 +452,28 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, bool is_linden = chat.mSourceType != CHAT_SOURCE_OBJECT && LLMuteList::isLinden(name); + /*** + * The simulator may have flagged this sender as a bot, if the viewer would like to display + * the chat text in a different color or font, the below code is how the viewer can + * tell if the sender is a bot. + *----------------------------------------------------- + bool is_bot = false; + if (metadata.has("sender")) + { // The server has identified this sender as a bot. + is_bot = metadata["sender"]["bot"].asBoolean(); + } + *----------------------------------------------------- + */ + + std::string notice_name; + LLSD notice_args; + if (metadata.has("notice")) + { // The server has injected a notice into the IM conversation. + // These will be things like bot notifications, etc. + notice_name = metadata["notice"]["id"].asString(); + notice_args = metadata["notice"]["data"]; + } + chat.mMuted = is_muted; chat.mFromID = from_id; chat.mFromName = name; @@ -544,7 +567,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, } else { - // standard message, not from system + // standard message, server may have injected a notice into the conversation. std::string saved; if (offline == IM_OFFLINE) { @@ -579,8 +602,17 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, region_message = true; } } - gIMMgr->addMessage( - session_id, + + std::string real_name; + + if (!notice_name.empty()) + { // The simulator has injected some sort of notice into the conversation. + // findString will only replace the contents of buffer if the notice_id is found. + LLTrans::findString(buffer, notice_name, notice_args); + real_name = SYSTEM_FROM; + } + + gIMMgr->addMessage(session_id, from_id, name, buffer, @@ -591,7 +623,9 @@ void LLIMProcessing::processNewMessage(LLUUID from_id, region_id, position, region_message, - timestamp); + timestamp, + LLUUID::null, + real_name); } else { @@ -1627,6 +1661,12 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) from_group = message_data["from_group"].asString() == "Y"; } + LLSD metadata; + if (message_data.has("metadata")) + { + metadata = message_data["metadata"]; + } + EInstantMessage dialog = static_cast<EInstantMessage>(message_data["dialog"].asInteger()); LLUUID session_id = message_data["transaction-id"].asUUID(); if (session_id.isNull() && dialog == IM_FROM_TASK) @@ -1654,6 +1694,7 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) local_bin_bucket.data(), S32(local_bin_bucket.size()), local_sender, + metadata, message_data["asset_id"].asUUID()); }); diff --git a/indra/newview/llimprocessing.h b/indra/newview/llimprocessing.h index 030d28b198..66ffc59ae0 100644 --- a/indra/newview/llimprocessing.h +++ b/indra/newview/llimprocessing.h @@ -48,6 +48,7 @@ public: U8 *binary_bucket, S32 binary_bucket_size, LLHost &sender, + LLSD metadata, LLUUID aux_id = LLUUID::null); // Either receives list of offline messages from 'ReadOfflineMsgs' capability diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 1402cc8c37..beb32dd6fb 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -3143,9 +3143,16 @@ void LLIMMgr::addMessage( const LLUUID& region_id, const LLVector3& position, bool is_region_msg, - U32 timestamp) // May be zero + U32 timestamp, // May be zero + LLUUID display_id, + std::string_view display_name) { LLUUID other_participant_id = target_id; + std::string message_display_name = (display_name.empty()) ? from : std::string(display_name); + if (display_id.isNull() && (display_name.empty())) + { + display_id = other_participant_id; + } LLUUID new_session_id = session_id; if (new_session_id.isNull()) @@ -3241,7 +3248,7 @@ void LLIMMgr::addMessage( } //Play sound for new conversations - if (!skip_message & !gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNewConversation"))) + if (!skip_message && !gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNewConversation"))) { make_ui_sound("UISndNewIncomingIMSession"); } @@ -3255,7 +3262,7 @@ void LLIMMgr::addMessage( if (!LLMuteList::getInstance()->isMuted(other_participant_id, LLMute::flagTextChat) && !skip_message) { - LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg, true, is_region_msg, timestamp); + LLIMModel::instance().addMessage(new_session_id, message_display_name, display_id, msg, true, is_region_msg, timestamp); } // Open conversation floater if offline messages are present diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 233fb075e8..162a8d93dd 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -368,7 +368,9 @@ public: const LLUUID& region_id = LLUUID::null, const LLVector3& position = LLVector3::zero, bool is_region_msg = false, - U32 timestamp = 0); + U32 timestamp = 0, + LLUUID display_id = LLUUID::null, + std::string_view display_name = ""); void addSystemMessage(const LLUUID& session_id, const std::string& message_name, const LLSD& args); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 896c7d8d70..2deb7caad5 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -4189,6 +4189,32 @@ void LLFolderBridge::pasteLinkFromClipboard() std::vector<LLUUID> objects; LLClipboard::instance().pasteFromClipboard(objects); + if (objects.size() == 0) + { + LLClipboard::instance().setCutMode(false); + return; + } + + LLUUID& first_id = objects[0]; + LLInventoryItem* item = model->getItem(first_id); + if (item && item->getAssetUUID().isNull()) + { + if (item->getActualType() == LLAssetType::AT_NOTECARD) + { + // otehrwise AIS will return 'Cannot link to items with a NULL asset_id.' + LLNotificationsUtil::add("CantLinkNotecard"); + LLClipboard::instance().setCutMode(false); + return; + } + else if (item->getActualType() == LLAssetType::AT_MATERIAL) + { + LLNotificationsUtil::add("CantLinkMaterial"); + LLClipboard::instance().setCutMode(false); + return; + } + } + + LLPointer<LLInventoryCallback> cb = NULL; LLInventoryPanel* panel = mInventoryPanel.get(); if (panel->getRootFolder()->isSingleFolderMode()) diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp index c4f93cee98..c7f2b0cb72 100644 --- a/indra/newview/llinventorygallery.cpp +++ b/indra/newview/llinventorygallery.cpp @@ -2105,6 +2105,30 @@ void LLInventoryGallery::pasteAsLink() std::vector<LLUUID> objects; LLClipboard::instance().pasteFromClipboard(objects); + if (objects.size() == 0) + { + LLClipboard::instance().setCutMode(false); + return; + } + + LLUUID& first_id = objects[0]; + LLInventoryItem* item = gInventory.getItem(first_id); + if (item && item->getAssetUUID().isNull()) + { + if (item->getActualType() == LLAssetType::AT_NOTECARD) + { + LLNotificationsUtil::add("CantLinkNotecard"); + LLClipboard::instance().setCutMode(false); + return; + } + else if (item->getActualType() == LLAssetType::AT_MATERIAL) + { + LLNotificationsUtil::add("CantLinkMaterial"); + LLClipboard::instance().setCutMode(false); + return; + } + } + bool paste_into_root = mSelectedItemIDs.empty(); for (LLUUID& dest : mSelectedItemIDs) { @@ -2753,6 +2777,7 @@ static LLDefaultChildRegistry::Register<LLInventoryGalleryItem> r("inventory_gal LLInventoryGalleryItem::LLInventoryGalleryItem(const Params& p) : LLPanel(p), mSelected(false), + mWorn(false), mDefaultImage(true), mItemName(""), mWornSuffix(""), diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp index c84b9abba0..340ecfcbbc 100644 --- a/indra/newview/llinventorygallerymenu.cpp +++ b/indra/newview/llinventorygallerymenu.cpp @@ -598,20 +598,23 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men bool has_children = false; bool is_full_perm_item = false; bool is_copyable = false; - LLViewerInventoryItem* selected_item = gInventory.getItem(selected_id); + + LLViewerInventoryCategory* selected_category = nullptr; + LLViewerInventoryItem* selected_item = nullptr; if(is_folder) { - LLInventoryCategory* category = gInventory.getCategory(selected_id); - if (category) + selected_category = dynamic_cast<LLViewerInventoryCategory*>(obj); + if (selected_category) { - folder_type = category->getPreferredType(); + folder_type = selected_category->getPreferredType(); is_system_folder = LLFolderType::lookupIsProtectedType(folder_type); has_children = (gInventory.categoryHasChildren(selected_id) != LLInventoryModel::CHILDREN_NO); } } else { + selected_item = dynamic_cast<LLViewerInventoryItem*>(obj); if (selected_item) { is_full_perm_item = selected_item->getIsFullPerm(); @@ -730,8 +733,7 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men { if (is_agent_inventory && !is_inbox && !is_cof && !is_in_favorites && !is_outfits) { - LLViewerInventoryCategory* category = gInventory.getCategory(selected_id); - if (!category || !LLFriendCardsManager::instance().isCategoryInFriendFolder(category)) + if (!selected_category || !LLFriendCardsManager::instance().isCategoryInFriendFolder(selected_category)) { items.push_back(std::string("New Folder")); } @@ -759,6 +761,22 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men if (inventory_linking) { items.push_back(std::string("Paste As Link")); + + if (selected_item) + { + if (!LLAssetType::lookupCanLink(selected_item->getActualType())) + { + disabled_items.push_back(std::string("Paste As Link")); + } + else if (gInventory.isObjectDescendentOf(selected_item->getUUID(), gInventory.getLibraryRootFolderID())) + { + disabled_items.push_back(std::string("Paste As Link")); + } + } + else if (selected_category && LLFolderType::lookupIsProtectedType(selected_category->getPreferredType())) + { + disabled_items.push_back(std::string("Paste As Link")); + } } } if (is_folder && is_agent_inventory) @@ -979,9 +997,8 @@ void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* men { if (is_folder) { - LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id); - if (cat - && !LLFolderType::lookupIsProtectedType(cat->getPreferredType()) + if (selected_category + && !LLFolderType::lookupIsProtectedType(selected_category->getPreferredType()) && gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID())) { can_list = true; diff --git a/indra/newview/llinventorylistener.cpp b/indra/newview/llinventorylistener.cpp index 79726c3e0b..13ca12a69a 100644 --- a/indra/newview/llinventorylistener.cpp +++ b/indra/newview/llinventorylistener.cpp @@ -107,16 +107,13 @@ LLInventoryListener::LLInventoryListener() // This struct captures (possibly large) category results from // getDirectDescendants() and collectDescendantsIf(). -struct CatResultSet: public LL::ResultSet +struct CatResultSet: public LL::VectorResultSet<LLInventoryModel::cat_array_t::value_type> { - CatResultSet(): LL::ResultSet("categories") {} - LLInventoryModel::cat_array_t mCategories; - - int getLength() const override { return narrow(mCategories.size()); } - LLSD getSingle(int index) const override + CatResultSet(): super("categories") {} + LLSD getSingleFrom(const LLPointer<LLViewerInventoryCategory>& cat) const override { - auto cat = mCategories[index]; - return llsd::map("name", cat->getName(), + return llsd::map("id", cat->getUUID(), + "name", cat->getName(), "parent_id", cat->getParentUUID(), "type", LLFolderType::lookup(cat->getPreferredType())); } @@ -124,16 +121,13 @@ struct CatResultSet: public LL::ResultSet // This struct captures (possibly large) item results from // getDirectDescendants() and collectDescendantsIf(). -struct ItemResultSet: public LL::ResultSet +struct ItemResultSet: public LL::VectorResultSet<LLInventoryModel::item_array_t::value_type> { - ItemResultSet(): LL::ResultSet("items") {} - LLInventoryModel::item_array_t mItems; - - int getLength() const override { return narrow(mItems.size()); } - LLSD getSingle(int index) const override + ItemResultSet(): super("items") {} + LLSD getSingleFrom(const LLPointer<LLViewerInventoryItem>& item) const override { - auto item = mItems[index]; - return llsd::map("name", item->getName(), + return llsd::map("id", item->getUUID(), + "name", item->getName(), "parent_id", item->getParentUUID(), "desc", item->getDescription(), "inv_type", LLInventoryType::lookup(item->getInventoryType()), @@ -158,14 +152,14 @@ void LLInventoryListener::getItemsInfo(LLSD const &data) LLViewerInventoryItem* item = gInventory.getItem(it); if (item) { - itemresult->mItems.push_back(item); + itemresult->mVector.push_back(item); } else { LLViewerInventoryCategory *cat = gInventory.getCategory(it); if (cat) { - catresult->mCategories.push_back(cat); + catresult->mVector.push_back(cat); } } } @@ -200,8 +194,8 @@ void LLInventoryListener::getDirectDescendants(LLSD const &data) auto catresult = new CatResultSet; auto itemresult = new ItemResultSet; - catresult->mCategories = *cats; - itemresult->mItems = *items; + catresult->mVector = *cats; + itemresult->mVector = *items; response["categories"] = catresult->getKeyLength(); response["items"] = itemresult->getKeyLength(); @@ -258,8 +252,8 @@ void LLInventoryListener::collectDescendantsIf(LLSD const &data) // collectDescendentsIf() method so it doesn't steal too many cycles. gInventory.collectDescendentsIf( folder_id, - catresult->mCategories, - itemresult->mItems, + catresult->mVector, + itemresult->mVector, LLInventoryModel::EXCLUDE_TRASH, collector); @@ -267,14 +261,6 @@ void LLInventoryListener::collectDescendantsIf(LLSD const &data) response["items"] = itemresult->getKeyLength(); } -/*==========================================================================*| -void LLInventoryListener::getSingle(LLSD const& data) -{ - auto result = LL::ResultSet::getInstance(data["result"]); - sendReply(llsd::map("single", result->getSingle(data["index"])), data); -} -|*==========================================================================*/ - void LLInventoryListener::getSlice(LLSD const& data) { auto result = LL::ResultSet::getInstance(data["result"]); diff --git a/indra/newview/lllocationinputctrl.h b/indra/newview/lllocationinputctrl.h index 56e5555ba5..c048b028f5 100644 --- a/indra/newview/lllocationinputctrl.h +++ b/indra/newview/lllocationinputctrl.h @@ -206,7 +206,7 @@ private: std::string mEditLandmarkTooltip; // this field holds a human-readable form of the location string, it is needed to be able to compare copy-pated value and real location std::string mHumanReadableLocation; - bool isHumanReadableLocationVisible; + bool isHumanReadableLocationVisible = true; std::string mMaturityHelpTopic; }; diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index bf49f33049..51f30dd86b 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -78,8 +78,8 @@ const static std::string MULTI_LINE_PREFIX(" "); * * Note: "You" was used as an avatar names in viewers of previous versions */ -const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$"); -const static boost::regex TIMESTAMP("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]|\\[\\d{1,2}:\\d{2}\\]).*"); +const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\s[AaPp][Mm]\\]\\s+|\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\s[AaPp][Mm]\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$"); +const static boost::regex TIMESTAMP("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}(\\s[AaPp][Mm])?\\]|\\[\\d{1,2}:\\d{2}(\\s[AaPp][Mm])?\\]).*"); /** * Regular expression suitable to match names like @@ -150,6 +150,10 @@ public: void checkAndCutOffDate(std::string& time_str) { + if (time_str.size() < 10) // not enough space for a date + { + return; + } // Cuts off the "%Y/%m/%d" from string for todays timestamps. // Assume that passed string has at least "%H:%M" time format. date log_date(not_a_date_time); @@ -166,20 +170,12 @@ public: if ( days_alive == zero_days ) { - // Yep, today's so strip "%Y/%m/%d" info - ptime stripped_time(not_a_date_time); - - mTimeStream.str(LLStringUtil::null); - mTimeStream << time_str; - mTimeStream >> stripped_time; - mTimeStream.clear(); - - time_str.clear(); - - mTimeStream.str(LLStringUtil::null); - mTimeStream << stripped_time; - mTimeStream >> time_str; - mTimeStream.clear(); + size_t pos = time_str.find_first_of(' '); + if (pos != std::string::npos) + { + time_str.erase(0, pos + 1); + LLStringUtil::trim(time_str); + } } LL_DEBUGS("LLChatLogParser") @@ -308,16 +304,22 @@ std::string LLLogChat::timestamp2LogString(U32 timestamp, bool withdate) std::string timeStr; if (withdate) { - timeStr = "[" + LLTrans::getString ("TimeYear") + "]/[" - + LLTrans::getString ("TimeMonth") + "]/[" - + LLTrans::getString ("TimeDay") + "] [" - + LLTrans::getString ("TimeHour") + "]:[" - + LLTrans::getString ("TimeMin") + "]"; + timeStr = "[" + LLTrans::getString("TimeYear") + "]/[" + + LLTrans::getString("TimeMonth") + "]/[" + + LLTrans::getString("TimeDay") + "] "; + } + + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr += "[" + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; } else { - timeStr = "[" + LLTrans::getString("TimeHour") + "]:[" - + LLTrans::getString ("TimeMin")+"]"; + timeStr += "[" + LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; } LLSD substitution; diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 7fe5c1ece0..9b6dcc5277 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -72,9 +72,10 @@ std::string lua_print_msg(lua_State* L, std::string_view level) lluau_checkstack(L, 2); luaL_where(L, 1); // start with the 'where' info at the top of the stack - std::ostringstream out; - out << lua_tostring(L, -1); + std::string source_info{ lua_tostring(L, -1) }; lua_pop(L, 1); + + std::ostringstream out; const char* sep = ""; // 'where' info ends with ": " // now iterate over arbitrary args, calling Lua tostring() on each and // concatenating with separators @@ -101,10 +102,10 @@ std::string lua_print_msg(lua_State* L, std::string_view level) // capture message string std::string msg{ out.str() }; // put message out there for any interested party (*koff* LLFloaterLUADebug *koff*) - LLEventPumps::instance().obtain("lua output").post(stringize(level, ": ", msg)); + LLEventPumps::instance().obtain("lua output").post(llsd::map("msg", msg, "level", stringize(level, ": "), "source_info", source_info)); llcoro::suspend(); - return msg; + return source_info + msg; } lua_function(print_debug, "print_debug(args...): DEBUG level logging") @@ -317,7 +318,7 @@ LLRequireResolver::LLRequireResolver(lua_State *L, const std::string& path) : void LLRequireResolver::findModule() { // If mPathToResolve is absolute, this replaces mSourceDir. - auto absolutePath = (mSourceDir / mPathToResolve).u8string(); + fsyspath absolutePath(mSourceDir / mPathToResolve); // Push _MODULES table on stack for checking and saving to the cache luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1); @@ -333,7 +334,7 @@ void LLRequireResolver::findModule() // not already cached - prep error message just in case auto fail{ - [L=L, path=mPathToResolve.u8string()]() + [L=L, path=mPathToResolve.string()]() { luaL_error(L, "could not find require('%s')", path.data()); }}; if (mPathToResolve.is_absolute()) @@ -348,10 +349,10 @@ void LLRequireResolver::findModule() { // if path is already absolute, operator/() preserves it auto abspath(fsyspath(gDirUtilp->getAppRODataDir()) / path.asString()); - std::string absolutePathOpt = (abspath / mPathToResolve).u8string(); + fsyspath absolutePathOpt = (abspath / mPathToResolve); if (absolutePathOpt.empty()) - luaL_error(L, "error requiring module '%s'", mPathToResolve.u8string().data()); + luaL_error(L, "error requiring module '%s'", mPathToResolve.string().data()); if (findModuleImpl(absolutePathOpt)) return; diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp index b5e494379d..dc7511b642 100644 --- a/indra/newview/llmaterialeditor.cpp +++ b/indra/newview/llmaterialeditor.cpp @@ -3177,14 +3177,7 @@ void LLMaterialEditor::applyToSelection() { LL_WARNS("MaterialEditor") << "Not connected to materials capable region, missing ModifyMaterialParams cap" << LL_ENDL; - // Fallback local preview. Will be removed once override systems is finished and new cap is deployed everywhere. - LLPointer<LLFetchedGLTFMaterial> mat = new LLFetchedGLTFMaterial(); - getGLTFMaterial(mat); - static const LLUUID placeholder("984e183e-7811-4b05-a502-d79c6f978a98"); - gGLTFMaterialList.addMaterial(placeholder, mat); - LLRenderMaterialFunctor mat_func(placeholder); - LLObjectSelectionHandle selected_objects = LLSelectMgr::getInstance()->getSelection(); - selected_objects->applyToTEs(&mat_func); + LLNotificationsUtil::add("MissingMaterialCaps"); } } diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 49260d5c50..ef9e8d7dac 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -1022,10 +1022,6 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) case MEDIA_EVENT_NAVIGATE_COMPLETE: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_COMPLETE, result string is: " << self->getNavigateResultString() << LL_ENDL; - if(mHidingInitialLoad) - { - mHidingInitialLoad = false; - } }; break; diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h index 9f9564af46..5d3d8f3ba2 100644 --- a/indra/newview/llmediactrl.h +++ b/indra/newview/llmediactrl.h @@ -201,7 +201,6 @@ public: mStretchToFill, mMaintainAspectRatio, mHideLoading, - mHidingInitialLoad, mClearCache, mHoverTextChanged, mDecoupleTextureSize, diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 5fc49b32ea..e4e70b02f9 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -548,8 +548,8 @@ LLViewerFetchedTexture* LLMeshUploadThread::FindViewerTexture(const LLImportMate return ppTex ? (*ppTex).get() : NULL; } -volatile S32 LLMeshRepoThread::sActiveHeaderRequests = 0; -volatile S32 LLMeshRepoThread::sActiveLODRequests = 0; +std::atomic<S32> LLMeshRepoThread::sActiveHeaderRequests = 0; +std::atomic<S32> LLMeshRepoThread::sActiveLODRequests = 0; U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; S32 LLMeshRepoThread::sRequestLowWater = REQUEST2_LOW_WATER_MIN; S32 LLMeshRepoThread::sRequestHighWater = REQUEST2_HIGH_WATER_MIN; @@ -3959,7 +3959,7 @@ void LLMeshRepository::notifyLoadedMeshes() } // erase from background thread - mThread->mWorkQueue.post([=]() + mThread->mWorkQueue.post([=, this]() { mThread->mSkinMap.erase(id); }); diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index d864a07615..c7572c9cbf 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -256,8 +256,8 @@ class LLMeshRepoThread : public LLThread { public: - volatile static S32 sActiveHeaderRequests; - volatile static S32 sActiveLODRequests; + static std::atomic<S32> sActiveHeaderRequests; + static std::atomic<S32> sActiveLODRequests; static U32 sMaxConcurrentRequests; static S32 sRequestLowWater; static S32 sRequestHighWater; diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 1e8b7d39cc..ecca593d51 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -3365,7 +3365,7 @@ bool LLModelPreview::render() mFMP->childSetEnabled("upload_joints", upload_skin); } - F32 explode = (F32)mFMP->childGetValue("physics_explode").asReal(); + F32 physics_explode = (F32)mFMP->childGetValue("physics_explode").asReal(); LLGLDepthTest gls_depth(GL_TRUE); // SL-12781 re-enable z-buffer for 3D model preview @@ -3565,7 +3565,7 @@ bool LLModelPreview::render() LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; if (decomp) { - LLMutexLock(decomp->mMutex); + LLMutexLock lock(decomp->mMutex); LLModel::Decomposition& physics = model->mPhysics; @@ -3590,12 +3590,12 @@ bool LLModelPreview::render() for (U32 i = 0; i < physics.mMesh.size(); ++i) { - if (explode > 0.f) + if (physics_explode > 0.f) { gGL.pushMatrix(); LLVector3 offset = model->mHullCenter[i] - model->mCenterOfHullCenters; - offset *= explode; + offset *= physics_explode; gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); } @@ -3610,7 +3610,7 @@ bool LLModelPreview::render() gGL.diffuseColor4ubv(hull_colors[i].mV); LLVertexBuffer::drawArrays(LLRender::TRIANGLES, physics.mMesh[i].mPositions); - if (explode > 0.f) + if (physics_explode > 0.f) { gGL.popMatrix(); } @@ -3625,7 +3625,8 @@ bool LLModelPreview::render() if (render_mesh) { auto num_models = mVertexBuffer[LLModel::LOD_PHYSICS][model].size(); - if (pass > 0){ + if (pass > 0) + { for (size_t i = 0; i < num_models; ++i) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); @@ -3685,7 +3686,7 @@ bool LLModelPreview::render() LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; if (decomp) { - LLMutexLock(decomp->mMutex); + LLMutexLock lock(decomp->mMutex); LLModel::Decomposition& physics = model->mPhysics; @@ -3805,7 +3806,8 @@ bool LLModelPreview::render() } } - for (U32 i = 0, e = static_cast<U32>(mVertexBuffer[mPreviewLOD][model].size()); i < e; ++i) + std::size_t size = mVertexBuffer[mPreviewLOD][model].size(); + for (U32 i = 0; i < size; ++i) { model->mSkinInfo.updateHash(); LLRenderPass::uploadMatrixPalette(mPreviewAvatar, &model->mSkinInfo); diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index 483ddcdd63..bf9aa66094 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -96,6 +96,7 @@ LLNetMap::LLNetMap (const Params & p) mPopupWorldPos(0.f, 0.f, 0.f), mMouseDown(0, 0), mPanning(false), + mCentering(false), mUpdateNow(false), mObjectImageCenterGlobal( gAgentCamera.getCameraPositionGlobal() ), mObjectRawImagep(), diff --git a/indra/newview/llnotificationlistitem.cpp b/indra/newview/llnotificationlistitem.cpp index 5b8b28ebe6..9a33bcb1b9 100644 --- a/indra/newview/llnotificationlistitem.cpp +++ b/indra/newview/llnotificationlistitem.cpp @@ -38,6 +38,7 @@ #include "lluicolortable.h" #include "message.h" #include "llnotificationsutil.h" +#include "llviewercontrol.h" #include <boost/regex.hpp> LLNotificationListItem::LLNotificationListItem(const Params& p) : LLPanel(p), @@ -133,10 +134,22 @@ std::string LLNotificationListItem::buildNotificationDate(const LLDate& time_sta default: timeStr = "[" + LLTrans::getString("TimeMonth") + "]/[" +LLTrans::getString("TimeDay")+"]/[" - +LLTrans::getString("TimeYear")+"] [" - +LLTrans::getString("TimeHour")+"]:[" - +LLTrans::getString("TimeMin")+"] [" - +LLTrans::getString("TimeTimezone")+"]"; + +LLTrans::getString("TimeYear")+"] ["; + + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr += LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } + else + { + timeStr += LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } break; } LLSD substitution; diff --git a/indra/newview/llpanelemojicomplete.cpp b/indra/newview/llpanelemojicomplete.cpp index 0d6ca1cb94..2d659cd19d 100644 --- a/indra/newview/llpanelemojicomplete.cpp +++ b/indra/newview/llpanelemojicomplete.cpp @@ -440,7 +440,7 @@ void LLPanelEmojiComplete::updateConstraints() { mRenderRect = getLocalRect(); - mEmojiWidth = (U16)(mIconFont->getWidthF32(u8"\U0001F431") + mPadding * 2); + mEmojiWidth = (U16)(mIconFont->getWidthF32(LLWString(1, 0x1F431).c_str()) + mPadding * 2); if (mVertical) { mEmojiHeight = mIconFont->getLineHeight() + mPadding * 2; diff --git a/indra/newview/llpanelenvironment.cpp b/indra/newview/llpanelenvironment.cpp index be61c44b7c..d3df88b65e 100644 --- a/indra/newview/llpanelenvironment.cpp +++ b/indra/newview/llpanelenvironment.cpp @@ -48,6 +48,7 @@ #include "llappviewer.h" #include "llcallbacklist.h" +#include "llviewercontrol.h" #include "llviewerparcelmgr.h" #include "llinventorymodel.h" @@ -939,19 +940,29 @@ void LLPanelEnvironmentInfo::udpateApparentTimeOfDay() S32Hours hourofday(secondofday); S32Seconds secondofhour(secondofday - hourofday); S32Minutes minutesofhour(secondofhour); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); bool am_pm(hourofday.value() >= 12); - if (hourofday.value() < 1) - hourofday = S32Hours(12); - if (hourofday.value() > 12) - hourofday -= S32Hours(12); + if (!use_24h) + { + if (hourofday.value() < 1) + hourofday = S32Hours(12); + if (hourofday.value() > 12) + hourofday -= S32Hours(12); + } std::string lblminute(((minutesofhour.value() < 10) ? "0" : "") + LLSD(minutesofhour.value()).asString()); - mLabelApparentTime->setTextArg("[HH]", LLSD(hourofday.value()).asString()); mLabelApparentTime->setTextArg("[MM]", lblminute); - mLabelApparentTime->setTextArg("[AP]", std::string(am_pm ? "PM" : "AM")); + if (use_24h) + { + mLabelApparentTime->setTextArg("[AP]", std::string()); + } + else + { + mLabelApparentTime->setTextArg("[AP]", std::string(am_pm ? "PM" : "AM")); + } mLabelApparentTime->setTextArg("[PRC]", LLSD((S32)(100 * perc)).asString()); } diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index 4404efff98..e1f2d7588c 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -1360,7 +1360,8 @@ void LLPanelGroupMembersSubTab::activate() { if (!gdatap || !gdatap->isMemberDataComplete()) { - const U32 page_size = 50; + static LLCachedControl<bool> enable_pagination(gSavedSettings, "UseGroupMemberPagination", false); + const U32 page_size = enable_pagination() ? 50 : 0; std::string sort_column_name = mMembersList->getSortColumnName(); bool sort_descending = !mMembersList->getSortAscending(); LLGroupMgr::getInstance()->sendCapGroupMembersRequest(mGroupID, page_size, 0, sort_column_name, sort_descending); diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp index 41373cd7f5..7596c0eba8 100644 --- a/indra/newview/llpanellandmarkinfo.cpp +++ b/indra/newview/llpanellandmarkinfo.cpp @@ -41,6 +41,7 @@ #include "lllandmarkactions.h" #include "llparcel.h" #include "llslurl.h" +#include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" @@ -326,7 +327,8 @@ void LLPanelLandmarkInfo::displayItemInfo(const LLInventoryItem* pItem) } else { - std::string timeStr = getString("acquired_date"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string timeStr = use_24h ? getString("acquired_date") : getString("acquired_date_ampm"); LLSD substitution; substitution["datetime"] = (S32) time_utc; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 60017db51d..1c2dac6d6e 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -1307,7 +1307,7 @@ void LLPanelOutfitEdit::showFilteredWearablesListView(LLWearableType::EType type showWearablesListView(); //e_list_view_item_type implicitly contains LLWearableType::EType starting from LVIT_SHAPE - applyListViewFilter(static_cast<EListViewItemType>(LVIT_SHAPE + type)); + applyListViewFilter(EListViewItemType(LVIT_SHAPE + EListViewItemType(type))); mWearableItemsList->setMenuWearableType(type); } diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp index 1af585708e..955b5e7730 100644 --- a/indra/newview/llpanelprimmediacontrols.cpp +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -777,7 +777,7 @@ void LLPanelPrimMediaControls::draw() else if(mFadeTimer.getStarted()) { F32 time = mFadeTimer.getElapsedTimeF32(); - alpha *= llmax(lerp(1.0, 0.0, time / mControlFadeTime), 0.0f); + alpha *= llmax(lerp(1.0f, 0.0f, time / mControlFadeTime), 0.0f); if(time >= mControlFadeTime) { diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 2086706bf8..5c54a3fc27 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -754,8 +754,6 @@ void LLPanelProfileSecondLife::onOpen(const LLSD& key) resetData(); - LLUUID avatar_id = getAvatarId(); - bool own_profile = getSelfProfile(); mGroupList->setShowNone(!own_profile); @@ -793,7 +791,6 @@ void LLPanelProfileSecondLife::onOpen(const LLSD& key) if (!own_profile) { - mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(avatar_id) ? LLAvatarTracker::instance().isBuddyOnline(avatar_id) : true); updateOnlineStatus(); fillRightsData(); } @@ -1214,17 +1211,6 @@ void LLPanelProfileSecondLife::changed(U32 mask) } } -// virtual, called by LLVoiceClient -void LLPanelProfileSecondLife::onChange(EStatusType status, const LLSD& channelInfo, bool proximal) -{ - if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL) - { - return; - } - - mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(getAvatarId()) ? LLAvatarTracker::instance().isBuddyOnline(getAvatarId()) : true); -} - void LLPanelProfileSecondLife::setAvatarId(const LLUUID& avatar_id) { if (avatar_id.notNull()) @@ -1502,7 +1488,7 @@ bool LLPanelProfileSecondLife::onEnableMenu(const LLSD& userdata) } else if (item_name == "voice_call") { - return mVoiceStatus; + return LLAvatarActions::canCallTo(agent_id); } else if (item_name == "chat_history") { diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index c207a4162a..ba00e12441 100644 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -33,7 +33,6 @@ #include "llpanel.h" #include "llpanelavatar.h" #include "llmediactrl.h" -#include "llvoiceclient.h" // class LLPanelProfileClassifieds; // class LLTabContainer; @@ -70,7 +69,6 @@ class LLViewerFetchedTexture; class LLPanelProfileSecondLife : public LLPanelProfilePropertiesProcessorTab , public LLFriendObserver - , public LLVoiceClientStatusObserver { public: LLPanelProfileSecondLife(); @@ -89,10 +87,6 @@ public: */ void changed(U32 mask) override; - // Implements LLVoiceClientStatusObserver::onChange() to enable the call - // button when voice is available - void onChange(EStatusType status, const LLSD& channelInfo, bool proximal) override; - void setAvatarId(const LLUUID& avatar_id) override; bool postBuild() override; @@ -203,7 +197,6 @@ private: LLHandle<LLFloater> mFloaterTexturePickerHandle; bool mHasUnsavedDescriptionChanges; - bool mVoiceStatus; bool mWaitingForImageUpload; bool mAllowPublish; bool mHideAge; diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index ebfdafdfe2..8097e0ac0b 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -42,6 +42,7 @@ #include "llnotificationsutil.h" #include "lltextbox.h" #include "lltoggleablemenu.h" +#include "llviewercontrol.h" #include "llviewermenu.h" #include "lllandmarkactions.h" #include "llclipboard.h" @@ -215,8 +216,18 @@ std::string LLTeleportHistoryFlatItem::getTimestamp() // Only show timestamp for today and yesterday if(time_diff < seconds_today + seconds_in_day) { - timestamp = "[" + LLTrans::getString("TimeHour12")+"]:[" - + LLTrans::getString("TimeMin")+"] ["+ LLTrans::getString("TimeAMPM")+"]"; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timestamp = "[" + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + } + else + { + timestamp = "[" + LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + LLTrans::getString("TimeAMPM") + "]"; + } + LLSD substitution; substitution["datetime"] = (S32) date.secondsSinceEpoch(); LLStringUtil::format(timestamp, substitution); diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp index 60877494e7..e65ba523f0 100644 --- a/indra/newview/llpanelvoicedevicesettings.cpp +++ b/indra/newview/llpanelvoicedevicesettings.cpp @@ -42,7 +42,7 @@ static LLPanelInjector<LLPanelVoiceDeviceSettings> t_panel_group_general("panel_voice_device_settings"); static const std::string DEFAULT_DEVICE("Default"); - +static const std::string NO_DEVICE("No Device"); LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings() : LLPanel() @@ -51,12 +51,10 @@ LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings() mCtrlOutputDevices = NULL; mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); - mDevicesUpdated = false; //obsolete mUseTuningMode = true; // grab "live" mic volume level mMicVolume = gSavedSettings.getF32("AudioLevelMic"); - } LLPanelVoiceDeviceSettings::~LLPanelVoiceDeviceSettings() @@ -81,7 +79,7 @@ bool LLPanelVoiceDeviceSettings::postBuild() boost::bind(&LLPanelVoiceDeviceSettings::onCommitUnmute, this)); mLocalizedDeviceNames[DEFAULT_DEVICE] = getString("default_text"); - mLocalizedDeviceNames["No Device"] = getString("name_no_device"); + mLocalizedDeviceNames[NO_DEVICE] = getString("name_no_device"); mLocalizedDeviceNames["Default System Device"] = getString("name_default_system_device"); mCtrlOutputDevices->setMouseDownCallback(boost::bind(&LLPanelVoiceDeviceSettings::onOutputDevicesClicked, this)); @@ -115,7 +113,7 @@ void LLPanelVoiceDeviceSettings::draw() bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled(); if (voice_enabled) { - getChildView("wait_text")->setVisible( !is_in_tuning_mode && mUseTuningMode); + getChildView("wait_text")->setVisible(!is_in_tuning_mode && mUseTuningMode); getChildView("disabled_text")->setVisible(false); mUnmuteBtn->setVisible(false); } @@ -212,56 +210,29 @@ void LLPanelVoiceDeviceSettings::cancel() void LLPanelVoiceDeviceSettings::refresh() { - //grab current volume + LLVoiceClient* voice_client = LLVoiceClient::getInstance(); + + // grab current volume LLSlider* volume_slider = getChild<LLSlider>("mic_volume_slider"); + // set mic volume tuning slider based on last mic volume setting F32 current_volume = (F32)volume_slider->getValue().asReal(); - LLVoiceClient::getInstance()->tuningSetMicVolume(current_volume); + voice_client->tuningSetMicVolume(current_volume); // Fill in popup menus - bool device_settings_available = LLVoiceClient::getInstance()->deviceSettingsAvailable(); + bool device_settings_available = voice_client->deviceSettingsAvailable(); + bool device_settings_updated = voice_client->deviceSettingsUpdated(); if (mCtrlInputDevices) { - mCtrlInputDevices->setEnabled(device_settings_available); - } - - if (mCtrlOutputDevices) - { - mCtrlOutputDevices->setEnabled(device_settings_available); - } - - getChild<LLSlider>("mic_volume_slider")->setEnabled(device_settings_available); - - if(!device_settings_available) - { - // The combo boxes are disabled, since we can't get the device settings from the daemon just now. - // Put the currently set default (ONLY) in the box, and select it. - if(mCtrlInputDevices) - { - mCtrlInputDevices->removeall(); - mCtrlInputDevices->add(getLocalizedDeviceName(mInputDevice), mInputDevice, ADD_BOTTOM); - mCtrlInputDevices->setValue(mInputDevice); - } - if(mCtrlOutputDevices) - { - mCtrlOutputDevices->removeall(); - mCtrlOutputDevices->add(getLocalizedDeviceName(mOutputDevice), mOutputDevice, ADD_BOTTOM); - mCtrlOutputDevices->setValue(mOutputDevice); - } - } - else if (LLVoiceClient::getInstance()->deviceSettingsUpdated()) - { - LLVoiceDeviceList::const_iterator device; - - if(mCtrlInputDevices) + if (device_settings_available && !voice_client->getCaptureDevices().empty()) { - LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getCaptureDevices(); - if (devices.size() > 0) // if zero, we've not received our devices yet + mCtrlInputDevices->setEnabled(true); + if (device_settings_updated) { mCtrlInputDevices->removeall(); mCtrlInputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); - for (auto& device : devices) + for (auto& device : voice_client->getCaptureDevices()) { mCtrlInputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM); } @@ -275,16 +246,24 @@ void LLPanelVoiceDeviceSettings::refresh() } } } + else + { + mCtrlInputDevices->setEnabled(false); + mCtrlInputDevices->removeall(); + mCtrlInputDevices->setLabel(getLocalizedDeviceName(NO_DEVICE)); + } + } - if(mCtrlOutputDevices) + if (mCtrlOutputDevices) + { + if (device_settings_available && !voice_client->getRenderDevices().empty()) { - LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getRenderDevices(); - if (devices.size() > 0) // if zero, we've not received our devices yet + mCtrlOutputDevices->setEnabled(true); + if (device_settings_updated) { mCtrlOutputDevices->removeall(); mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); - - for (auto& device : devices) + for (auto& device : voice_client->getRenderDevices()) { mCtrlOutputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM); } @@ -298,7 +277,15 @@ void LLPanelVoiceDeviceSettings::refresh() } } } + else + { + mCtrlOutputDevices->setEnabled(false); + mCtrlOutputDevices->removeall(); + mCtrlOutputDevices->setLabel(getLocalizedDeviceName(NO_DEVICE)); + } } + + getChild<LLSlider>("mic_volume_slider")->setEnabled(device_settings_available && !voice_client->getCaptureDevices().empty()); } void LLPanelVoiceDeviceSettings::initialize() diff --git a/indra/newview/llpanelvoicedevicesettings.h b/indra/newview/llpanelvoicedevicesettings.h index 815396cbd1..d0d14c212c 100644 --- a/indra/newview/llpanelvoicedevicesettings.h +++ b/indra/newview/llpanelvoicedevicesettings.h @@ -63,7 +63,6 @@ protected: class LLComboBox *mCtrlInputDevices; class LLComboBox *mCtrlOutputDevices; class LLButton *mUnmuteBtn; - bool mDevicesUpdated; bool mUseTuningMode; std::map<std::string, std::string> mLocalizedDeviceNames; }; diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index 7cbbb89313..66dcd2f7ba 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -517,6 +517,7 @@ void LLPreviewGesture::addKeys() void LLPreviewGesture::addAnimations() { LLComboBox* combo = mAnimationCombo; + LLUUID old_value = combo->getCurrentID(); combo->removeall(); @@ -565,6 +566,8 @@ void LLPreviewGesture::addAnimations() combo->add(item->getName(), item->getAssetUUID(), ADD_BOTTOM); } + + combo->setCurrentByID(old_value); } @@ -1410,7 +1413,7 @@ void LLPreviewGesture::onCommitAnimation(LLUICtrl* ctrl, void* data) { // Assign the animation name LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step; - if (self->mAnimationCombo->getCurrentIndex() == 0) + if (self->mAnimationCombo->getCurrentIndex() <= 0) { anim_step->mAnimName.clear(); anim_step->mAnimAssetID.setNull(); diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index 1b657d9ea1..800fc7ec0c 100644 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -441,6 +441,16 @@ void LLPreviewTexture::onFileLoadedForSave(bool success, self->getWindow()->decBusyCount(); self->mLoadingFullImage = false; } + if (!success) + { + LL_WARNS("FileSaveAs") << "Failed to download file " << *item_uuid << " for saving." + << " Is missing: " << (src_vi->isMissingAsset() ? "true" : "false") + << " Discard: " << src_vi->getDiscardLevel() + << " Raw discard: " << discard_level + << " Size: " << src_vi->getWidth() << "x" << src_vi->getHeight() + << " Has GL texture: " << (src_vi->hasGLTexture() ? "true" : "false") + << " Has saved raw image: " << (src_vi->hasSavedRawImage() ? "true" : "false") << LL_ENDL; + } } if( self && final && success ) diff --git a/indra/newview/llscrollingpanelparam.h b/indra/newview/llscrollingpanelparam.h index 3aba4e4e40..93daf22e76 100644 --- a/indra/newview/llscrollingpanelparam.h +++ b/indra/newview/llscrollingpanelparam.h @@ -81,7 +81,6 @@ public: protected: LLTimer mMouseDownTimer; // timer for how long mouse has been held down on a hint. F32 mLastHeldTime; - bool mAllowModify; LLButton* mLessBtn; LLButton* mMoreBtn; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 51da051340..d0347c7abf 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -3133,6 +3133,8 @@ void LLSelectMgr::adjustTexturesByScale(bool send_to_sim, bool stretch) F32 scale_x = 1; F32 scale_y = 1; + F32 offset_x = 0; + F32 offset_y = 0; for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) { @@ -3149,6 +3151,21 @@ void LLSelectMgr::adjustTexturesByScale(bool send_to_sim, bool stretch) scale_y = scale_ratio.mV[t_axis] * object_scale.mV[t_axis]; } material->mTextureTransform[i].mScale.set(scale_x, scale_y); + + LLVector2 scales = selectNode->mGLTFScales[te_num][i]; + LLVector2 offsets = selectNode->mGLTFOffsets[te_num][i]; + F64 int_part = 0; + offset_x = (F32)modf((offsets[VX] + (scales[VX] - scale_x)) / 2, &int_part); + if (offset_x < 0) + { + offset_x++; + } + offset_y = (F32)modf((offsets[VY] + (scales[VY] - scale_y)) / 2, &int_part); + if (offset_y < 0) + { + offset_y++; + } + material->mTextureTransform[i].mOffset.set(offset_x, offset_y); } LLFetchedGLTFMaterial* render_mat = (LLFetchedGLTFMaterial*)tep->getGLTFRenderMaterial(); @@ -6888,6 +6905,8 @@ void LLSelectNode::saveTextureScaleRatios(LLRender::eTexIndex index_to_query) { mTextureScaleRatios.clear(); mGLTFScaleRatios.clear(); + mGLTFScales.clear(); + mGLTFOffsets.clear(); if (mObject.notNull()) { @@ -6928,6 +6947,8 @@ void LLSelectNode::saveTextureScaleRatios(LLRender::eTexIndex index_to_query) F32 scale_x = 1; F32 scale_y = 1; std::vector<LLVector3> material_v_vec; + std::vector<LLVector2> material_scales_vec; + std::vector<LLVector2> material_offset_vec; for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i) { if (material) @@ -6935,12 +6956,16 @@ void LLSelectNode::saveTextureScaleRatios(LLRender::eTexIndex index_to_query) LLGLTFMaterial::TextureTransform& transform = material->mTextureTransform[i]; scale_x = transform.mScale[VX]; scale_y = transform.mScale[VY]; + material_scales_vec.push_back(transform.mScale); + material_offset_vec.push_back(transform.mOffset); } else { // Not having an override doesn't mean that there is no material scale_x = 1; scale_y = 1; + material_scales_vec.emplace_back(scale_x, scale_y); + material_offset_vec.emplace_back(0.f, 0.f); } if (tep->getTexGen() == LLTextureEntry::TEX_GEN_PLANAR) @@ -6956,6 +6981,8 @@ void LLSelectNode::saveTextureScaleRatios(LLRender::eTexIndex index_to_query) material_v_vec.push_back(material_v); } mGLTFScaleRatios.push_back(material_v_vec); + mGLTFScales.push_back(material_scales_vec); + mGLTFOffsets.push_back(material_offset_vec); } } } diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index f8242675dc..f2b1e691b5 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -240,6 +240,8 @@ public: gltf_materials_vec_t mSavedGLTFOverrideMaterials; std::vector<LLVector3> mTextureScaleRatios; std::vector< std::vector<LLVector3> > mGLTFScaleRatios; + std::vector< std::vector<LLVector2> > mGLTFScales; + std::vector< std::vector<LLVector2> > mGLTFOffsets; std::vector<LLVector3> mSilhouetteVertices; // array of vertices to render silhouette of object std::vector<LLVector3> mSilhouetteNormals; // array of normals to render silhouette of object bool mSilhouetteExists; // need to generate silhouette? diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index 34b8535c84..fee0bbcda4 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -671,7 +671,8 @@ void LLSettingsVOSky::updateSettings() // After some A/B comparison of relesae vs EEP, tweak to allow strength to fall below 2 // at night, for better match. (mSceneLightStrength is a divisor, so lower value means brighter // local lights) - F32 sun_dynamic_range = llmax(gSavedSettings.getF32("RenderSunDynamicRange"), 0.0001f); + LLCachedControl<F32> sdr(gSavedSettings, "RenderSunDynamicRange", 1.f); + F32 sun_dynamic_range = llmax(sdr(), 0.0001f); mSceneLightStrength = 2.0f * (0.75f + sun_dynamic_range * dp); gSky.setSunAndMoonDirectionsCFR(sun_direction, moon_direction); diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index fccf745a74..385e4314a9 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -483,7 +483,8 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) } else { - std::string timeStr = getString("acquiredDate"); + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + std::string timeStr = use_24h ? getString("acquiredDate") : getString("acquiredDateAMPM"); LLSD substitution; substitution["datetime"] = (S32) time_utc; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llsky.cpp b/indra/newview/llsky.cpp index 82caa14433..66fbe0029a 100644 --- a/indra/newview/llsky.cpp +++ b/indra/newview/llsky.cpp @@ -39,7 +39,7 @@ // linden library includes #include "llerror.h" #include "llmath.h" -#include "math.h" +#include <cmath> #include "v4color.h" #include "llviewerobjectlist.h" diff --git a/indra/newview/llsky.h b/indra/newview/llsky.h index 32599dcee2..d06f181357 100644 --- a/indra/newview/llsky.h +++ b/indra/newview/llsky.h @@ -28,7 +28,6 @@ #define LL_LLSKY_H #include "llmath.h" -//#include "vmath.h" #include "v3math.h" #include "v4math.h" #include "v4color.h" diff --git a/indra/newview/llsprite.cpp b/indra/newview/llsprite.cpp index e51aeb6080..09c57eaf1d 100644 --- a/indra/newview/llsprite.cpp +++ b/indra/newview/llsprite.cpp @@ -37,7 +37,7 @@ #include "llsprite.h" -#include "math.h" +#include <cmath> #include "lldrawable.h" #include "llface.h" diff --git a/indra/newview/llsprite.h b/indra/newview/llsprite.h index 44439bd30c..d6e8e37ec9 100644 --- a/indra/newview/llsprite.h +++ b/indra/newview/llsprite.h @@ -27,8 +27,6 @@ #ifndef LL_LLSPRITE_H #define LL_LLSPRITE_H -////#include "vmath.h" -//#include "llmath.h" #include "v3math.h" #include "v4math.h" #include "v4color.h" diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 83d6b57fa4..dd8c6d989e 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -204,6 +204,7 @@ #include "threadpool.h" #include "llperfstats.h" +#include "rlvhandler.h" #if LL_WINDOWS #include "lldxhardware.h" @@ -875,6 +876,8 @@ bool idle_startup() return false; } + RlvHandler::setEnabled(gSavedSettings.get<bool>(Rlv::Settings::Main)); + // reset the values that could have come in from a slurl // DEV-42215: Make sure they're not empty -- gUserCredential // might already have been set from gSavedSettings, and it's too bad diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h index 10a104730b..fc72ab7db7 100644 --- a/indra/newview/llsurface.h +++ b/indra/newview/llsurface.h @@ -27,7 +27,6 @@ #ifndef LL_LLSURFACE_H #define LL_LLSURFACE_H -//#include "vmath.h" #include "v3math.h" #include "v3dmath.h" #include "v4math.h" diff --git a/indra/newview/llterrainpaintmap.cpp b/indra/newview/llterrainpaintmap.cpp index 8ccde74c93..a858f92d8e 100644 --- a/indra/newview/llterrainpaintmap.cpp +++ b/indra/newview/llterrainpaintmap.cpp @@ -31,6 +31,8 @@ // library includes #include "llglslshader.h" #include "llrendertarget.h" +#include "llrender2dutils.h" +#include "llshadermgr.h" #include "llvertexbuffer.h" // newview includes @@ -38,18 +40,31 @@ #include "llsurface.h" #include "llsurfacepatch.h" #include "llviewercamera.h" +#include "llviewercontrol.h" #include "llviewerregion.h" #include "llviewershadermgr.h" #include "llviewertexture.h" -// static -bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex) +namespace +{ +#ifdef SHOW_ASSERT +void check_tex(const LLViewerTexture& tex) { llassert(tex.getComponents() == 3); llassert(tex.getWidth() > 0 && tex.getHeight() > 0); llassert(tex.getWidth() == tex.getHeight()); llassert(tex.getPrimaryFormat() == GL_RGB); llassert(tex.getGLTexture()); +} +#else +#define check_tex(tex) +#endif +} // namespace + +// static +bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex) +{ + check_tex(tex); const LLSurface& surface = region.getLand(); const U32 patch_count = surface.getPatchesPerEdge(); @@ -76,8 +91,8 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& // Bind the debug shader and render terrain to tex // Use a scratch render target because its dimensions may exceed the standard bake target, and this is a one-off bake LLRenderTarget scratch_target; - const S32 dim = llmin(tex.getWidth(), tex.getHeight()); - scratch_target.allocate(dim, dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, LLTexUnit::eTextureMipGeneration::TMG_NONE); if (!scratch_target.isComplete()) { @@ -104,6 +119,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& const F32 region_half_width = region_width / 2.0f; const F32 region_camera_height = surface.getMaxZ() + DEFAULT_NEAR_PLANE; LLViewerCamera camera; + // TODO: Huh... I just realized this view vector is not completely vertical const LLVector3 region_center = LLVector3(region_half_width, region_half_width, 0.0) + region.getOriginAgent(); const LLVector3 camera_origin = LLVector3(0.0f, 0.0f, region_camera_height) + region_center; camera.lookAt(camera_origin, region_center, LLVector3::y_axis); @@ -237,6 +253,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& S32 alpha_ramp = shader.enableTexture(LLViewerShaderMgr::TERRAIN_ALPHARAMP); LLPointer<LLViewerTexture> alpha_ramp_texture = LLViewerTextureManager::getFetchedTexture(IMG_ALPHA_GRAD_2D); + // TODO: Consider using LLGLSLShader::bindTexture gGL.getTexUnit(alpha_ramp)->bind(alpha_ramp_texture); gGL.getTexUnit(alpha_ramp)->setTextureAddressMode(LLTexUnit::TAM_CLAMP); @@ -250,6 +267,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& const U32 vertex_offset = n * patch_index; llassert(index_offset + ni <= region_indices); llassert(vertex_offset + n <= region_vertices); + // TODO: Try a single big drawRange and see if that still works buf->drawRange(LLRender::TRIANGLES, vertex_offset, vertex_offset + n - 1, ni, index_offset); } } @@ -269,7 +287,7 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& gGL.flush(); LLVertexBuffer::unbind(); // Final step: Copy the output to the terrain paintmap - const bool success = tex.getGLTexture()->setSubImageFromFrameBuffer(0, 0, 0, 0, dim, dim); + const bool success = tex.getGLTexture()->setSubImageFromFrameBuffer(0, 0, 0, 0, tex.getWidth(), tex.getHeight()); if (!success) { LL_WARNS() << "Failed to copy framebuffer to paintmap" << LL_ENDL; @@ -283,3 +301,687 @@ bool LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& return success; } + +// *TODO: Decide when to apply the paint queue - ideally once per frame per region +// Applies paints and then clears the paint queue +// *NOTE The paint queue is also cleared when setting the paintmap texture +void LLTerrainPaintMap::applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue) +{ + if (queue.empty()) { return; } + + check_tex(tex); + + gGL.getTexUnit(0)->bind(tex.getGLTexture(), false, true); + + // glTexSubImage2D replaces all pixels in the rectangular region. That + // makes it unsuitable for alpha. + llassert(queue.getComponents() == LLTerrainPaint::RGB); + constexpr GLenum pixformat = GL_RGB; + + const std::vector<LLTerrainPaint::ptr_t>& queue_list = queue.get(); + for (size_t i = 0; i < queue_list.size(); ++i) + { + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for the paintmap (this could + // change in the future). + queue.convertBitDepths(i, 8); + const LLTerrainPaint::ptr_t& paint = queue_list[i]; + + if (paint->mData.empty()) { continue; } + constexpr GLint level = 0; + if ((paint->mStartX >= tex.getWidth() - 1) || (paint->mStartY >= tex.getHeight() - 1)) { continue; } + constexpr GLint miplevel = 0; + const S32 x_offset = paint->mStartX; + const S32 y_offset = paint->mStartY; + const S32 width = llmin(paint->mWidthX, tex.getWidth() - x_offset); + const S32 height = llmin(paint->mWidthY, tex.getHeight() - y_offset); + const U8* pixels = paint->mData.data(); + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + // *TODO: Performance suggestion: Use the sub-image utility function + // that LLImageGL::setSubImage uses to split texture updates into + // lines, if that's faster. + glTexSubImage2D(GL_TEXTURE_2D, miplevel, x_offset, y_offset, width, height, pixformat, pixtype, pixels); + stop_glerror(); + } + + // Generating mipmaps at the end... + glGenerateMipmap(GL_TEXTURE_2D); + stop_glerror(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + queue.clear(); +} + +namespace +{ + +// A general-purpose vertex buffer of a quad for stamping textures on the z=0 +// plane. +// *NOTE: Because we know the vertex XY coordinates go from 0 to 1 +// pre-transform, UVs can be calculated from the vertices +LLVertexBuffer* get_paint_triangle_buffer() +{ + static LLPointer<LLVertexBuffer> buf = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX); + static bool initialized = false; + if (!initialized) + { + // Two triangles forming a square from (0,0) to (1,1) + buf->allocateBuffer(/*vertices =*/ 4, /*indices =*/ 6); + LLStrider<U16> indices; + LLStrider<LLVector3> vertices; + buf->getVertexStrider(vertices); + buf->getIndexStrider(indices); + // y + // 2....3 + // ^ . . + // | 0....1 + // | + // -------> x + // + // triangle 1: 0,1,2 + // triangle 2: 1,3,2 + (*(vertices++)).set(0.0f, 0.0f, 0.0f); + (*(vertices++)).set(1.0f, 0.0f, 0.0f); + (*(vertices++)).set(0.0f, 1.0f, 0.0f); + (*(vertices++)).set(1.0f, 1.0f, 0.0f); + *(indices++) = 0; + *(indices++) = 1; + *(indices++) = 2; + *(indices++) = 1; + *(indices++) = 3; + *(indices++) = 2; + buf->unmapBuffer(); + } + return buf.get(); +} + +}; + +// static +LLTerrainPaintQueue LLTerrainPaintMap::convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in) +{ + check_tex(tex); + llassert(queue_in.getComponents() == LLTerrainPaint::RGBA); + + // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead + // TODO: even if it means performing extra render operations to apply the paints, in rare cases where the paints can't all fit within an area that can be represented by the buffer + LLRenderTarget scratch_target; + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + LLTexUnit::eTextureMipGeneration::TMG_NONE); + if (!scratch_target.isComplete()) + { + llassert(false); + LL_WARNS() << "Failed to allocate render target" << LL_ENDL; + return false; + } + gGL.getTexUnit(0)->disable(); + stop_glerror(); + + scratch_target.bindTarget(); + glClearColor(0, 0, 0, 0); + scratch_target.clear(); + const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; + const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; + + LLVertexBuffer* buf = get_paint_triangle_buffer(); + + // Update projection matrix and viewport + // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. + { + stop_glerror(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(-target_half_width, target_half_width, -target_half_height, target_half_height, 0.25f, 1.0f); + stop_glerror(); + const LLRect texture_rect(0, scratch_target.getHeight(), scratch_target.getWidth(), 0); + glViewport(texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight()); + } + + // View matrix + // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. + // Camera is centered in the middle of the framebuffer. + glm::mat4 view(glm::make_mat4((GLfloat*) OGL_TO_CFR_ROTATION)); + { + LLViewerCamera camera; + const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); + const LLVector3 camera_look_down(target_half_width, target_half_height, 0.0f); + camera.lookAt(camera_origin, camera_look_down, LLVector3::y_axis); + camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); + GLfloat ogl_matrix[16]; + camera.getOpenGLTransform(ogl_matrix); + view *= glm::make_mat4(ogl_matrix); + } + + LLGLDisable stencil(GL_STENCIL_TEST); + LLGLDisable scissor(GL_SCISSOR_TEST); + LLGLEnable cull_face(GL_CULL_FACE); + LLGLDepthTest depth_test(GL_FALSE, GL_FALSE, GL_ALWAYS); + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + LLGLSLShader& shader = gTerrainStampProgram; + shader.bind(); + + // First, apply the paint map as the background + { + glm::mat4 model; + { + model = glm::scale(model, glm::vec3((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); + } + glm::mat4 modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(glm::value_ptr(modelview)); + + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); + // We care about the whole paintmap, which is already a power of two. + // Hence, TERRAIN_STAMP_SCALE = (1.0,1.0) + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + LLTerrainPaintQueue queue_out(LLTerrainPaint::RGB); + + // Incrementally apply each RGBA paint to the render target, then extract + // the result back into memory as an RGB paint. + // Put each result in queue_out. + const std::vector<LLTerrainPaint::ptr_t>& queue_in_list = queue_in.get(); + for (size_t i = 0; i < queue_in_list.size(); ++i) + { + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for paint operations (this + // could change in the future). + queue_in.convertBitDepths(i, 8); + const LLTerrainPaint::ptr_t& paint_in = queue_in_list[i]; + + // Modelview matrix for the current paint + // View matrix is already computed. Just need the model matrix. + // Orthographic projection matrix is already updated + glm::mat4 model; + { + model = glm::scale(model, glm::vec3(paint_in->mWidthX, paint_in->mWidthY, 1.0f)); + model = glm::translate(model, glm::vec3(paint_in->mStartX, paint_in->mStartY, 0.0f)); + } + glm::mat4 modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(glm::value_ptr(modelview)); + + // Generate temporary stamp texture from paint contents. + // Our stamp image needs to be a power of two. + // Because the paint data may not cover a whole power-of-two region, + // allocate a bigger 2x2 image if needed, but set the image data later + // for a subset of the image. + // Pixel data outside this subset is left undefined. We will use + // TERRAIN_STAMP_SCALE in the stamp shader to define the subset of the + // image we care about. + const U32 width_rounded = 1 << U32(ceil(log2(F32(paint_in->mWidthX)))); + const U32 height_rounded = 1 << U32(ceil(log2(F32(paint_in->mWidthY)))); + LLPointer<LLImageGL> stamp_image; + { + // Create image object (dimensions not yet initialized in GL) + U32 stamp_tex_name; + LLImageGL::generateTextures(1, &stamp_tex_name); + const U32 components = paint_in->mComponents; + constexpr LLGLenum target = GL_TEXTURE_2D; + const LLGLenum internal_format = paint_in->mComponents == 4 ? GL_RGBA8 : GL_RGB8; + const LLGLenum format = paint_in->mComponents == 4 ? GL_RGBA : GL_RGB; + constexpr LLGLenum type = GL_UNSIGNED_BYTE; + stamp_image = new LLImageGL(stamp_tex_name, components, target, internal_format, format, type, LLTexUnit::TAM_WRAP); + // Nearest-neighbor filtering to reduce surprises + stamp_image->setFilteringOption(LLTexUnit::TFO_POINT); + + // Initialize the image dimensions in GL + constexpr U8* undefined_data_for_now = nullptr; + gGL.getTexUnit(0)->bind(stamp_image, false, true); + glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width_rounded, height_rounded, 0, format, type, undefined_data_for_now); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + stamp_image->setSize(width_rounded, height_rounded, components); + stamp_image->setDiscardLevel(0); + + // Manually set a subset of the image in GL + const U8* data = paint_in->mData.data(); + const S32 data_width = paint_in->mWidthX; + const S32 data_height = paint_in->mWidthY; + constexpr S32 origin = 0; + // width = data_width; height = data_height. i.e.: Copy the full + // contents of data into the image. + stamp_image->setSubImage(data, data_width, data_height, origin, origin, /*width=*/data_width, /*height=*/data_height); + } + + // Apply ("stamp") the paint to the render target + { + shader.bindTextureImageGL(LLShaderMgr::DIFFUSE_MAP, stamp_image); + const F32 width_fraction = F32(paint_in->mWidthX) / F32(width_rounded); + const F32 height_fraction = F32(paint_in->mWidthY) / F32(height_rounded); + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, width_fraction, height_fraction); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + // Extract the result back into memory as an RGB paint + LLTerrainPaint::ptr_t paint_out = std::make_shared<LLTerrainPaint>(); + { + paint_out->mStartX = paint_in->mStartX; + paint_out->mStartY = paint_in->mStartY; + paint_out->mWidthX = paint_in->mWidthX; + paint_out->mWidthY = paint_in->mWidthY; + paint_out->mBitDepth = 8; // Will be reduced to TerrainPaintBitDepth bits later + paint_out->mComponents = LLTerrainPaint::RGB; +#ifdef SHOW_ASSERT + paint_out->assert_confined_to(tex); +#endif + paint_out->confine_to(tex); + paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY); + constexpr GLint miplevel = 0; + const S32 x_offset = paint_out->mStartX; + const S32 y_offset = paint_out->mStartY; + const S32 width = paint_out->mWidthX; + const S32 height = paint_out->mWidthY; + constexpr GLenum pixformat = GL_RGB; + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + llassert(paint_out->mData.size() <= std::numeric_limits<GLsizei>::max()); + const GLsizei buf_size = (GLsizei)paint_out->mData.size(); + U8* pixels = paint_out->mData.data(); + glReadPixels(x_offset, y_offset, width, height, pixformat, pixtype, pixels); + } + + // Enqueue the result to the new paint queue, with bit depths per color + // channel reduced from 8 to 5, and reduced from RGBA (paintmap + // sub-rectangle update with alpha mask) to RGB (paintmap sub-rectangle + // update without alpha mask). This format is suitable for sending + // over the network. + // *TODO: At some point, queue_out will pass through a network + // round-trip which will reduce the bit depth, making the + // pre-conversion step not necessary. + queue_out.enqueue(paint_out); + LLCachedControl<U32> bit_depth(gSavedSettings, "TerrainPaintBitDepth"); + queue_out.convertBitDepths(queue_out.size()-1, bit_depth); + } + + queue_in.clear(); + + scratch_target.flush(); + + LLGLSLShader::unbind(); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + return queue_out; +} + +// static +LLTerrainPaintQueue LLTerrainPaintMap::convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in) +{ + check_tex(tex); + + // TODO: Avoid allocating a scratch render buffer and use mAuxillaryRT instead + // TODO: even if it means performing extra render operations to apply the brushes, in rare cases where the paints can't all fit within an area that can be represented by the buffer + LLRenderTarget scratch_target; + const S32 max_dim = llmax(tex.getWidth(), tex.getHeight()); + scratch_target.allocate(max_dim, max_dim, GL_RGB, false, LLTexUnit::eTextureType::TT_TEXTURE, + LLTexUnit::eTextureMipGeneration::TMG_NONE); + if (!scratch_target.isComplete()) + { + llassert(false); + LL_WARNS() << "Failed to allocate render target" << LL_ENDL; + return false; + } + gGL.getTexUnit(0)->disable(); + stop_glerror(); + + scratch_target.bindTarget(); + glClearColor(0, 0, 0, 0); + scratch_target.clear(); + const F32 target_half_width = (F32)scratch_target.getWidth() / 2.0f; + const F32 target_half_height = (F32)scratch_target.getHeight() / 2.0f; + + LLVertexBuffer* buf = get_paint_triangle_buffer(); + + // Update projection matrix and viewport + // *NOTE: gl_state_for_2d also sets the modelview matrix. This will be overridden later. + { + stop_glerror(); + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.pushMatrix(); + gGL.loadIdentity(); + gGL.ortho(-target_half_width, target_half_width, -target_half_height, target_half_height, 0.25f, 1.0f); + stop_glerror(); + const LLRect texture_rect(0, scratch_target.getHeight(), scratch_target.getWidth(), 0); + glViewport(texture_rect.mLeft, texture_rect.mBottom, texture_rect.getWidth(), texture_rect.getHeight()); + } + + // View matrix + // Coordinates should be in pixels. 1.0f = 1 pixel on the framebuffer. + // Camera is centered in the middle of the framebuffer. + glm::mat4 view(glm::make_mat4((GLfloat*)OGL_TO_CFR_ROTATION)); + { + LLViewerCamera camera; + const LLVector3 camera_origin(target_half_width, target_half_height, 0.5f); + const LLVector3 camera_look_down(target_half_width, target_half_height, 0.0f); + camera.lookAt(camera_origin, camera_look_down, LLVector3::y_axis); + camera.setAspect(F32(scratch_target.getHeight()) / F32(scratch_target.getWidth())); + GLfloat ogl_matrix[16]; + camera.getOpenGLTransform(ogl_matrix); + view *= glm::make_mat4(ogl_matrix); + } + + LLGLDisable stencil(GL_STENCIL_TEST); + LLGLDisable scissor(GL_SCISSOR_TEST); + LLGLEnable cull_face(GL_CULL_FACE); + LLGLDepthTest depth_test(GL_FALSE, GL_FALSE, GL_ALWAYS); + LLGLEnable blend(GL_BLEND); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + + LLGLSLShader& shader = gTerrainStampProgram; + shader.bind(); + + // First, apply the paint map as the background + { + glm::mat4 model; + { + model = glm::scale(model, glm::vec3((F32)tex.getWidth(), (F32)tex.getHeight(), 1.0f)); + model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); + } + glm::mat4 modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(glm::value_ptr(modelview)); + + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, &tex); + // We care about the whole paintmap, which is already a power of two. + // Hence, TERRAIN_STAMP_SCALE = (1.0,1.0) + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + LLTerrainPaintQueue queue_out(LLTerrainPaint::RGB); + + // Incrementally apply each brush stroke to the render target, then extract + // the result back into memory as an RGB paint. + // Put each result in queue_out. + const std::vector<LLTerrainBrush::ptr_t>& brush_list = queue_in.get(); + for (size_t i = 0; i < brush_list.size(); ++i) + { + const LLTerrainBrush::ptr_t& brush_in = brush_list[i]; + + // Modelview matrix for the current brush + // View matrix is already computed. Just need the model matrix. + // Orthographic projection matrix is already updated + // *NOTE: Brush path information is in region space. It will need to be + // converted to paintmap pixel space before it makes sense. + F32 brush_width_x; + F32 brush_width_y; + F32 brush_start_x; + F32 brush_start_y; + { + F32 min_x = brush_in->mPath[0].mV[VX]; + F32 max_x = min_x; + F32 min_y = brush_in->mPath[0].mV[VY]; + F32 max_y = min_y; + for (size_t i = 1; i < brush_in->mPath.size(); ++i) + { + const F32 x = brush_in->mPath[i].mV[VX]; + const F32 y = brush_in->mPath[i].mV[VY]; + min_x = llmin(min_x, x); + max_x = llmax(max_x, x); + min_y = llmin(min_y, y); + max_y = llmax(max_y, y); + } + brush_width_x = brush_in->mBrushSize + (max_x - min_x); + brush_width_y = brush_in->mBrushSize + (max_y - min_y); + brush_start_x = min_x - (brush_in->mBrushSize / 2.0f); + brush_start_y = min_y - (brush_in->mBrushSize / 2.0f); + // Convert brush path information to paintmap pixel space from region + // space. + brush_width_x *= tex.getWidth() / region.getWidth(); + brush_width_y *= tex.getHeight() / region.getWidth(); + brush_start_x *= tex.getWidth() / region.getWidth(); + brush_start_y *= tex.getHeight() / region.getWidth(); + } + glm::mat4 model; + { + model = glm::scale(model, glm::vec3(brush_width_x, brush_width_y, 1.0f)); + model = glm::translate(model, glm::vec3(brush_start_x, brush_start_y, 0.0f)); + } + glm::mat4 modelview = view * model; + gGL.matrixMode(LLRender::MM_MODELVIEW); + gGL.loadMatrix(glm::value_ptr(modelview)); + + // Apply the "brush" to the render target + { + // TODO: Use different shader for this - currently this is using the stamp shader. The white image is just a placeholder for now + shader.bindTexture(LLShaderMgr::DIFFUSE_MAP, LLViewerFetchedTexture::sWhiteImagep); + shader.uniform2f(LLShaderMgr::TERRAIN_STAMP_SCALE, 1.0f, 1.0f); + buf->setBuffer(); + buf->draw(LLRender::TRIANGLES, buf->getIndicesSize(), 0); + } + + // Extract the result back into memory as an RGB paint + LLTerrainPaint::ptr_t paint_out = std::make_shared<LLTerrainPaint>(); + { + paint_out->mStartX = U16(floor(brush_start_x)); + paint_out->mStartY = U16(floor(brush_start_y)); + const F32 dX = brush_start_x - F32(paint_out->mStartX); + const F32 dY = brush_start_y - F32(paint_out->mStartY); + paint_out->mWidthX = U16(ceil(brush_width_x + dX)); + paint_out->mWidthY = U16(ceil(brush_width_y + dY)); + paint_out->mBitDepth = 8; // Will be reduced to TerrainPaintBitDepth bits later + paint_out->mComponents = LLTerrainPaint::RGB; + // The brush strokes are expected to sometimes partially venture + // outside of the paintmap bounds. + paint_out->confine_to(tex); + paint_out->mData.resize(paint_out->mComponents * paint_out->mWidthX * paint_out->mWidthY); + constexpr GLint miplevel = 0; + const S32 x_offset = paint_out->mStartX; + const S32 y_offset = paint_out->mStartY; + const S32 width = paint_out->mWidthX; + const S32 height = paint_out->mWidthY; + constexpr GLenum pixformat = GL_RGB; + constexpr GLenum pixtype = GL_UNSIGNED_BYTE; + llassert(paint_out->mData.size() <= std::numeric_limits<GLsizei>::max()); + const GLsizei buf_size = (GLsizei)paint_out->mData.size(); + U8* pixels = paint_out->mData.data(); + glReadPixels(x_offset, y_offset, width, height, pixformat, pixtype, pixels); + } + + // Enqueue the result to the new paint queue, with bit depths per color + // channel reduced from 8 to 5, and reduced from RGBA (paintmap + // sub-rectangle update with alpha mask) to RGB (paintmap sub-rectangle + // update without alpha mask). This format is suitable for sending + // over the network. + // *TODO: At some point, queue_out will pass through a network + // round-trip which will reduce the bit depth, making the + // pre-conversion step not necessary. + queue_out.enqueue(paint_out); + LLCachedControl<U32> bit_depth(gSavedSettings, "TerrainPaintBitDepth"); + queue_out.convertBitDepths(queue_out.size()-1, bit_depth); + } + + queue_in.clear(); + + scratch_target.flush(); + + LLGLSLShader::unbind(); + + gGL.matrixMode(LLRender::MM_PROJECTION); + gGL.popMatrix(); + + return queue_out; +} + +template<typename T> +LLTerrainQueue<T>::LLTerrainQueue(LLTerrainQueue<T>& other) +{ + *this = other; +} + +template<typename T> +LLTerrainQueue<T>& LLTerrainQueue<T>::operator=(LLTerrainQueue<T>& other) +{ + mList = other.mList; + return *this; +} + +template<typename T> +bool LLTerrainQueue<T>::enqueue(std::shared_ptr<T>& t, bool dry_run) +{ + if (!dry_run) { mList.push_back(t); } + return true; +} + +template<typename T> +bool LLTerrainQueue<T>::enqueue(std::vector<std::shared_ptr<T>>& list) +{ + constexpr bool dry_run = true; + for (auto& t : list) + { + if (!enqueue(t, dry_run)) { return false; } + } + for (auto& t : list) + { + enqueue(t); + } + return true; +} + +template<typename T> +size_t LLTerrainQueue<T>::size() const +{ + return mList.size(); +} + +template<typename T> +bool LLTerrainQueue<T>::empty() const +{ + return mList.empty(); +} + +template<typename T> +void LLTerrainQueue<T>::clear() +{ + mList.clear(); +} + +void LLTerrainPaint::assert_confined_to(const LLTexture& tex) const +{ + llassert(mStartX >= 0 && mStartX < tex.getWidth()); + llassert(mStartY >= 0 && mStartY < tex.getHeight()); + llassert(mWidthX <= tex.getWidth() - mStartX); + llassert(mWidthY <= tex.getHeight() - mStartY); +} + +void LLTerrainPaint::confine_to(const LLTexture& tex) +{ + mStartX = llmax(mStartX, 0); + mStartY = llmax(mStartY, 0); + mWidthX = llmin(mWidthX, tex.getWidth() - mStartX); + mWidthY = llmin(mWidthY, tex.getHeight() - mStartY); + assert_confined_to(tex); +} + +LLTerrainPaintQueue::LLTerrainPaintQueue(U8 components) +: mComponents(components) +{ + llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); +} + +LLTerrainPaintQueue::LLTerrainPaintQueue(LLTerrainPaintQueue& other) +: LLTerrainQueue<LLTerrainPaint>(other) +, mComponents(other.mComponents) +{ + llassert(mComponents == LLTerrainPaint::RGB || mComponents == LLTerrainPaint::RGBA); +} + +LLTerrainPaintQueue& LLTerrainPaintQueue::operator=(LLTerrainPaintQueue& other) +{ + LLTerrainQueue<LLTerrainPaint>::operator=(other); + mComponents = other.mComponents; + return *this; +} + +bool LLTerrainPaintQueue::enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run) +{ + llassert(paint); + if (!paint) { return false; } + + // The paint struct should be pre-validated before this code is reached. + llassert(!paint->mData.empty()); + // The internal paint map image is currently 8 bits, so that's the maximum + // allowed bit depth. + llassert(paint->mBitDepth > 0 && paint->mBitDepth <= 8); + llassert(paint->mData.size() == (mComponents * paint->mWidthX * paint->mWidthY)); + llassert(paint->mWidthX > 0); + llassert(paint->mWidthY > 0); +#ifdef SHOW_ASSERT + static LLCachedControl<U32> max_texture_width(gSavedSettings, "RenderMaxTextureResolution", 2048); +#endif + llassert(paint->mWidthX <= max_texture_width); + llassert(paint->mWidthY <= max_texture_width); + llassert(paint->mStartX < max_texture_width); + llassert(paint->mStartY < max_texture_width); + + return LLTerrainQueue<LLTerrainPaint>::enqueue(paint, dry_run); +} + +bool LLTerrainPaintQueue::enqueue(LLTerrainPaintQueue& queue) +{ + return LLTerrainQueue<LLTerrainPaint>::enqueue(queue.mList); +} + +void LLTerrainPaintQueue::convertBitDepths(size_t index, U8 target_bit_depth) +{ + llassert(target_bit_depth > 0 && target_bit_depth <= 8); + llassert(index < mList.size()); + + LLTerrainPaint::ptr_t& paint = mList[index]; + if (paint->mBitDepth == target_bit_depth) { return; } + + const F32 old_bit_max = F32((1 << paint->mBitDepth) - 1); + const F32 new_bit_max = F32((1 << target_bit_depth) - 1); + const F32 bit_conversion_factor = new_bit_max / old_bit_max; + + for (U8& color : paint->mData) + { + color = (U8)llround(F32(color) * bit_conversion_factor); + } + + paint->mBitDepth = target_bit_depth; +} + +LLTerrainBrushQueue::LLTerrainBrushQueue() +: LLTerrainQueue<LLTerrainBrush>() +{ +} + +LLTerrainBrushQueue::LLTerrainBrushQueue(LLTerrainBrushQueue& other) +: LLTerrainQueue<LLTerrainBrush>(other) +{ +} + +LLTerrainBrushQueue& LLTerrainBrushQueue::operator=(LLTerrainBrushQueue& other) +{ + LLTerrainQueue<LLTerrainBrush>::operator=(other); + return *this; +} + +bool LLTerrainBrushQueue::enqueue(LLTerrainBrush::ptr_t& brush, bool dry_run) +{ + llassert(brush->mBrushSize > 0); + llassert(!brush->mPath.empty()); + llassert(brush->mPathOffset < brush->mPath.size()); + llassert(brush->mPathOffset < 2); // Harmless, but doesn't do anything useful, so might be a sign of implementation error + return LLTerrainQueue<LLTerrainBrush>::enqueue(brush, dry_run); +} + +bool LLTerrainBrushQueue::enqueue(LLTerrainBrushQueue& queue) +{ + return LLTerrainQueue<LLTerrainBrush>::enqueue(queue.mList); +} + +template class LLTerrainQueue<LLTerrainPaint>; +template class LLTerrainQueue<LLTerrainBrush>; diff --git a/indra/newview/llterrainpaintmap.h b/indra/newview/llterrainpaintmap.h index 66827862c5..d4859b747b 100644 --- a/indra/newview/llterrainpaintmap.h +++ b/indra/newview/llterrainpaintmap.h @@ -26,9 +26,20 @@ #pragma once +#include <memory> +#include <vector> + +#include "stdtypes.h" +#include "v2math.h" + +class LLTexture; class LLViewerRegion; class LLViewerTexture; +class LLTerrainPaintQueue; +class LLTerrainBrushQueue; +// TODO: Terrain painting across regions. Assuming painting is confined to one +// region for now. class LLTerrainPaintMap { public: @@ -39,4 +50,121 @@ public: // to type TERRAIN_PAINT_TYPE_PBR_PAINTMAP. // Returns true if successful static bool bakeHeightNoiseIntoPBRPaintMapRGB(const LLViewerRegion& region, LLViewerTexture& tex); + + // This operation clears the queue + // TODO: Decide if clearing the queue is needed - seems inconsistent + static void applyPaintQueueRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue); + + static LLTerrainPaintQueue convertPaintQueueRGBAToRGB(LLViewerTexture& tex, LLTerrainPaintQueue& queue_in); + // TODO: Implement (it's similar to convertPaintQueueRGBAToRGB but different shader + need to calculate the dimensions + need a different vertex buffer for each brush stroke) + static LLTerrainPaintQueue convertBrushQueueToPaintRGB(const LLViewerRegion& region, LLViewerTexture& tex, LLTerrainBrushQueue& queue_in); +}; + +template<typename T> +class LLTerrainQueue +{ +public: + LLTerrainQueue() = default; + LLTerrainQueue(LLTerrainQueue<T>& other); + LLTerrainQueue& operator=(LLTerrainQueue<T>& other); + + bool enqueue(std::shared_ptr<T>& t, bool dry_run = false); + size_t size() const; + bool empty() const; + void clear(); + + const std::vector<std::shared_ptr<T>>& get() const { return mList; } + +protected: + bool enqueue(std::vector<std::shared_ptr<T>>& list); + + std::vector<std::shared_ptr<T>> mList; +}; + +// Enqueued paint operations, in texture coordinates. +// mData is always RGB or RGBA (determined by mComponents), with each U8 +// storing one color with a max value of (1 >> mBitDepth) - 1 +struct LLTerrainPaint +{ + using ptr_t = std::shared_ptr<LLTerrainPaint>; + + U16 mStartX; + U16 mStartY; + U16 mWidthX; + U16 mWidthY; + U8 mBitDepth; + U8 mComponents; + static constexpr U8 RGB = 3; + static constexpr U8 RGBA = 4; + std::vector<U8> mData; + + // Asserts that this paint's start/width fit within the bounds of the + // provided texture dimensions. + void assert_confined_to(const LLTexture& tex) const; + // Confines this paint's start/width so it fits within the bounds of the + // provided texture dimensions. + // Does not allocate mData. + void confine_to(const LLTexture& tex); +}; + +class LLTerrainPaintQueue : public LLTerrainQueue<LLTerrainPaint> +{ +public: + LLTerrainPaintQueue() = delete; + // components determines what type of LLTerrainPaint is allowed. Must be 3 (RGB) or 4 (RGBA) + LLTerrainPaintQueue(U8 components); + LLTerrainPaintQueue(LLTerrainPaintQueue& other); + LLTerrainPaintQueue& operator=(LLTerrainPaintQueue& other); + + bool enqueue(LLTerrainPaint::ptr_t& paint, bool dry_run = false); + bool enqueue(LLTerrainPaintQueue& queue); + + U8 getComponents() const { return mComponents; } + // Convert mBitDepth for the LLTerrainPaint in the queue at index + // If mBitDepth is already equal to target_bit_depth, no conversion takes + // place. + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for the paintmap (this could + // change in the future). + void convertBitDepths(size_t index, U8 target_bit_depth); + +private: + U8 mComponents; +}; + +struct LLTerrainBrush +{ + using ptr_t = std::shared_ptr<LLTerrainBrush>; + + // Width of the brush in region space. The brush is a square texture with + // alpha. + F32 mBrushSize; + // Brush path points in region space, excluding the vertical axis, which + // does not contribute to the paint map. + std::vector<LLVector2> mPath; + // Offset of the brush path to actually start drawing at. An offset of 0 + // indicates that a brush stroke has just started (i.e. the user just + // pressed down the mouse button). An offset greater than 0 indicates the + // continuation of a brush stroke. Skipped entries in mPath are not drawn + // directly, but are used for stroke orientation and path interpolation. + // TODO: For the initial implementation, mPathOffset will be 0 and mPath + // will be of length of at most 1, leading to discontinuous paint paths. + // Then, mPathOffset may be 1 or 0, 1 indicating the continuation of a + // stroke with linear interpolation. It is unlikely that we will implement + // anything more sophisticated than that for now. + U8 mPathOffset; + // Indicates if this is the end of the brush stroke. Can occur if the mouse + // button is lifted, or if the mouse temporarily stops while held down. + bool mPathEnd; +}; + +class LLTerrainBrushQueue : public LLTerrainQueue<LLTerrainBrush> +{ +public: + LLTerrainBrushQueue(); + LLTerrainBrushQueue(LLTerrainBrushQueue& other); + LLTerrainBrushQueue& operator=(LLTerrainBrushQueue& other); + + bool enqueue(LLTerrainBrush::ptr_t& brush, bool dry_run = false); + bool enqueue(LLTerrainBrushQueue& queue); }; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 69c40066b4..7b6f591254 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -2090,7 +2090,7 @@ bool LLTextureFetchWorker::deleteOK() // Allow any pending reads or writes to complete if (mCacheReadHandle != LLTextureCache::nullHandle()) { - if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, true)) + if (!mFetcher->mTextureCache || mFetcher->mTextureCache->readComplete(mCacheReadHandle, true)) { mCacheReadHandle = LLTextureCache::nullHandle(); } @@ -2101,7 +2101,7 @@ bool LLTextureFetchWorker::deleteOK() } if (mCacheWriteHandle != LLTextureCache::nullHandle()) { - if (mFetcher->mTextureCache->writeComplete(mCacheWriteHandle)) + if (!mFetcher->mTextureCache || mFetcher->mTextureCache->writeComplete(mCacheWriteHandle)) { mCacheWriteHandle = LLTextureCache::nullHandle(); } @@ -2832,7 +2832,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) { LL_PROFILE_ZONE_SCOPED; - mRequestQueue.tryPost([=]() + mRequestQueue.tryPost([=, this]() { LLTextureFetchWorker* worker = getWorker(id); if (worker) @@ -3523,8 +3523,8 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) //if (! gViewerAssetStatsThread1) // return true; - static volatile bool reporting_started(false); - static volatile S32 report_sequence(0); + static std::atomic<bool> reporting_started(false); + static std::atomic<S32> report_sequence(0); // In mStatsSD, we have a copy we own of the LLSD representation // of the asset stats. Add some additional fields and ship it off. diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp index 3c3440d41a..95653dc19b 100644 --- a/indra/newview/lltoastgroupnotifypanel.cpp +++ b/indra/newview/lltoastgroupnotifypanel.cpp @@ -87,10 +87,21 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(const LLNotificationPtr& notifi std::string timeStr = "[" + LLTrans::getString("TimeWeek") + "], [" + LLTrans::getString("TimeMonth") + "]/[" + LLTrans::getString("TimeDay") + "]/[" - + LLTrans::getString("TimeYear") + "] [" - + LLTrans::getString("TimeHour") + "]:[" - + LLTrans::getString("TimeMin") + "] [" - + LLTrans::getString("TimeTimezone") + "]"; + + LLTrans::getString("TimeYear") + "] ["; + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + timeStr += LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } + else + { + timeStr += LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "] [" + + LLTrans::getString("TimeTimezone") + "]"; + } const LLDate timeStamp = notification->getDate(); LLDate notice_date = timeStamp.notNull() ? timeStamp : payload["received_time"].asDate(); diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index 166542324d..647210b7e6 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -306,7 +306,7 @@ public: if (tokens.size() < 1) return false; LLVector3 coords(128, 128, 0); - if (tokens.size() <= 4) + if (tokens.size() >= 3) // Require at least [1] and [2] { coords = LLVector3((F32)tokens[1].asReal(), (F32)tokens[2].asReal(), diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 865d7fd442..184c0e7d8b 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -76,6 +76,7 @@ #include "llslurl.h" #include "llstartup.h" #include "llperfstats.h" +#include "rlvcommon.h" // Third party library includes #include <boost/algorithm/string.hpp> @@ -910,6 +911,7 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "AutoTuneImpostorByDistEnabled", handleUserImpostorByDistEnabledChanged); setting_setup_signal_listener(gSavedSettings, "TuningFPSStrategy", handleFPSTuningStrategyChanged); { + setting_setup_signal_listener(gSavedSettings, "LocalTerrainPaintEnabled", handleSetShaderChanged); setting_setup_signal_listener(gSavedSettings, "LocalTerrainPaintEnabled", handleLocalTerrainChanged); const char* transform_suffixes[] = { "ScaleU", @@ -932,6 +934,8 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "TerrainPaintBitDepth", handleSetShaderChanged); setting_setup_signal_listener(gSavedPerAccountSettings, "AvatarHoverOffsetZ", handleAvatarHoverOffsetChanged); + + setting_setup_signal_listener(gSavedSettings, Rlv::Settings::TopLevelMenu, Rlv::Util::menuToggleVisible); } #if TEST_CACHED_CONTROL diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 1f4502323c..b0e07d8ea3 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -79,6 +79,7 @@ #include "llviewerparcelmgr.h" #include "llviewerregion.h" #include "llviewershadermgr.h" +#include "llviewerstats.h" #include "llviewertexturelist.h" #include "llviewerwindow.h" #include "llvoavatarself.h" @@ -472,6 +473,35 @@ void display(bool rebuild, F32 zoom_factor, int subfield, bool for_snapshot) // true = minimized, do not show/update the TP screen. HB update_tp_display(true); } + + // Run texture subsystem to discard memory while backgrounded + if (!gNonInteractive) + { + LL_PROFILE_ZONE_NAMED("Update Images"); + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("Class"); + LLViewerTexture::updateClass(); + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("Image Update Bump"); + gBumpImageList.updateImages(); // must be called before gTextureList version so that it's textures are thrown out first. + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("List"); + F32 max_image_decode_time = 0.050f * gFrameIntervalSeconds.value(); // 50 ms/second decode time + max_image_decode_time = llclamp(max_image_decode_time, 0.002f, 0.005f); // min 2ms/frame, max 5ms/frame) + gTextureList.updateImages(max_image_decode_time); + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DISPLAY("GLTF Materials Cleanup"); + // remove dead gltf materials + gGLTFMaterialList.flushMaterials(); + } + } return; } @@ -1085,6 +1115,9 @@ void getProfileStatsContext(boost::json::object& stats) context.emplace("parcelid", parcel->getLocalID()); } context.emplace("time", LLDate::now().toHTTPDateString("%Y-%m-%dT%H:%M:%S")); + + // supplement with stats packet + stats.emplace("stats", LlsdToJson(capture_viewer_stats(true))); } std::string getProfileStatsFilename() diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 9d9961d51c..b2a7d875ab 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -176,6 +176,7 @@ #include "llpreviewtexture.h" #include "llscriptfloater.h" #include "llsyswellwindow.h" +#include "rlvfloaters.h" // *NOTE: Please add files in alphabetical order to keep merges easy. @@ -483,6 +484,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("region_debug_console", "floater_region_debug_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionDebugConsole>); LLFloaterReg::add("region_info", "floater_region_info.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionInfo>); LLFloaterReg::add("region_restarting", "floater_region_restarting.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterRegionRestarting>); + LLFloaterReg::add("rlv_console", "floater_rlv_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<Rlv::FloaterConsole>); LLFloaterReg::add("script_debug", "floater_script_debug.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptDebug>); LLFloaterReg::add("script_debug_output", "floater_script_debug_panel.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptDebugOutput>); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 92db598410..9633e54f29 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -2903,14 +2903,14 @@ void LLViewerMediaImpl::update() media_tex->ref(); main_queue->postTo( mTexUpdateQueue, // Worker thread queue - [=]() // work done on update worker thread + [=, this]() // work done on update worker thread { #if LL_IMAGEGL_THREAD_CHECK media_tex->getGLTexture()->mActiveThread = LLThread::currentID(); #endif doMediaTexUpdate(media_tex, data, data_width, data_height, x_pos, y_pos, width, height, true); }, - [=]() // callback to main thread + [=, this]() // callback to main thread { #if LL_IMAGEGL_THREAD_CHECK media_tex->getGLTexture()->mActiveThread = LLThread::currentID(); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 39aa85beea..68c38c3692 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -117,6 +117,7 @@ #include "lltoolmgr.h" #include "lltoolpie.h" #include "lltoolselectland.h" +#include "llterrainpaintmap.h" #include "lltrans.h" #include "llviewerdisplay.h" //for gWindowResized #include "llviewergenericmessage.h" @@ -151,6 +152,7 @@ #include "llviewershadermgr.h" #include "gltfscenemanager.h" #include "gltf/asset.h" +#include "rlvcommon.h" using namespace LLAvatarAppearanceDefines; @@ -1428,16 +1430,21 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t return false; } + // This calls gLocalTerrainMaterials.setPaintType + // It also ensures the terrain bake shader is compiled (handleSetShaderChanged), so call this first + // *TODO: Fix compile errors in shader so it can be used for all platforms. Then we can unhide the shader from behind this setting and remove the hook to handleSetShaderChanged. This advanced setting is intended to be used as a local setting for testing terrain, not a feature flag, but it is currently used like a feature flag as a temporary hack. + // *TODO: Ideally we would call setPaintType *after* the paint map is well-defined. The terrain draw pool should be able to handle an undefined paint map in the meantime. + gSavedSettings.setBOOL("LocalTerrainPaintEnabled", true); + U16 dim = (U16)gSavedSettings.getU32("TerrainPaintResolution"); // Ensure a reasonable image size of power two const U32 max_resolution = gSavedSettings.getU32("RenderMaxTextureResolution"); dim = llclamp(dim, 16, max_resolution); dim = 1 << U32(std::ceil(std::log2(dim))); + // TODO: Could probably get away with not using image raw here, now that we aren't writing bits via the CPU (see load_exr for example) LLPointer<LLImageRaw> image_raw = new LLImageRaw(dim,dim,3); LLPointer<LLViewerTexture> tex = LLViewerTextureManager::getLocalTexture(image_raw.get(), true); const bool success = LLTerrainPaintMap::bakeHeightNoiseIntoPBRPaintMapRGB(*region, *tex); - // This calls gLocalTerrainMaterials.setPaintType - gSavedSettings.setBOOL("LocalTerrainPaintEnabled", true); // If baking the paintmap failed, set the paintmap to nullptr. This // causes LLDrawPoolTerrain to use a blank paintmap instead. if (!success) { tex = nullptr; } @@ -1447,6 +1454,92 @@ class LLAdvancedTerrainCreateLocalPaintMap : public view_listener_t } }; +class LLAdvancedTerrainEditLocalPaintMap : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLViewerTexture* tex = gLocalTerrainMaterials.getPaintMap(); + if (!tex) + { + LL_WARNS() << "No local paint map available to edit" << LL_ENDL; + return false; + } + + LLTerrainBrushQueue& brush_queue = gLocalTerrainMaterials.getBrushQueue(); + LLTerrainPaintQueue& paint_request_queue = gLocalTerrainMaterials.getPaintRequestQueue(); + + const LLViewerRegion* region = gAgent.getRegion(); + if (!region) + { + LL_WARNS() << "No current region for calculating paint operations" << LL_ENDL; + return false; + } + // TODO: Create the brush + // Just a dab for now + LLTerrainBrush::ptr_t brush = std::make_shared<LLTerrainBrush>(); + brush->mBrushSize = 16.0f; + brush->mPath.emplace_back(17.0f, 17.0f); + brush->mPathOffset = 0; + brush->mPathEnd = true; + brush_queue.enqueue(brush); + LLTerrainPaintQueue brush_as_paint_queue = LLTerrainPaintMap::convertBrushQueueToPaintRGB(*region, *tex, brush_queue); + //paint_send_queue.enqueue(brush_as_paint_queue); // TODO: What was this line for? (it might also be a leftover line from an unfinished edit) + + // TODO: Keep this around for later testing (i.e. when reducing framebuffer size and the offsets that requires) +#if 0 + // Enqueue a paint + // Modifies a subsection of the region paintmap with the material in + // the last slot. + // It is currently the responsibility of the paint queue to convert + // incoming bits to the right bit depth for the paintmap (this could + // change in the future). + LLTerrainPaint::ptr_t paint = std::make_shared<LLTerrainPaint>(); + const U16 width = 33; + paint->mStartX = 1; + paint->mStartY = 1; + paint->mWidthX = width; + paint->mWidthY = width; + constexpr U8 bit_depth = 5; + paint->mBitDepth = bit_depth; + constexpr U8 max_value = (1 << bit_depth) - 1; + const size_t pixel_count = width * width; + const U8 components = LLTerrainPaint::RGBA; + paint->mComponents = components; + paint->mData.resize(components * pixel_count); + for (size_t h = 0; h < paint->mWidthY; ++h) + { + for (size_t w = 0; w < paint->mWidthX; ++w) + { + const size_t pixel = (h * paint->mWidthX) + w; + // Solid blue color + paint->mData[(components*pixel) + components - 2] = max_value; // blue + //// Alpha grid: 1.0 if odd for either dimension, 0.0 otherwise + //const U8 alpha = U8(F32(max_value) * F32(bool(w % 2) || bool(h % 2))); + //paint->mData[(components*pixel) + components - 1] = alpha; // alpha + // Alpha "frame" + const bool edge = w == 0 || h == 0 || w == (paint->mWidthX - 1) || h == (paint->mWidthY - 1); + const bool near_edge_frill = ((w == 1 || w == (paint->mWidthX - 2)) && (h % 2 == 0)) || + ((h == 1 || h == (paint->mWidthY - 2)) && (w % 2 == 0)); + const U8 alpha = U8(F32(max_value) * F32(edge || near_edge_frill)); + paint->mData[(components*pixel) + components - 1] = alpha; // alpha + } + } + paint_request_queue.enqueue(paint); +#endif + + // Apply the paint queues ad-hoc right here for now. + // *TODO: Eventually the paint queue(s) should be applied at a + // predictable time in the viewer frame loop. + // TODO: In hindsight... maybe we *should* bind the paintmap to the render buffer. That makes a lot more sense, and we wouldn't have to reduce its resolution by settling for the bake buffer. If we do that, make a comment above convertPaintQueueRGBAToRGB that the texture is modified! + LLTerrainPaintQueue paint_send_queue = LLTerrainPaintMap::convertPaintQueueRGBAToRGB(*tex, paint_request_queue); + LLTerrainPaintQueue& paint_map_queue = gLocalTerrainMaterials.getPaintMapQueue(); + paint_map_queue.enqueue(paint_send_queue); + LLTerrainPaintMap::applyPaintQueueRGB(*tex, paint_map_queue); + + return true; + } +}; + class LLAdvancedTerrainDeleteLocalPaintMap : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -6280,6 +6373,8 @@ void show_debug_menus() gMenuBarView->setItemVisible("Advanced", debug); // gMenuBarView->setItemEnabled("Advanced", debug); // Don't disable Advanced keyboard shortcuts when hidden + Rlv::Util::menuToggleVisible(); + gMenuBarView->setItemVisible("Debug", qamode); gMenuBarView->setItemEnabled("Debug", qamode); @@ -7713,15 +7808,29 @@ class LLAvatarSendIM : public view_listener_t class LLAvatarCall : public view_listener_t { + static LLVOAvatar* findAvatar() + { + return find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); + } + bool handleEvent(const LLSD& userdata) { - LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); - if(avatar) + if (LLVOAvatar* avatar = findAvatar()) { LLAvatarActions::startCall(avatar->getID()); } return true; } + +public: + static bool isAvailable() + { + if (LLVOAvatar* avatar = findAvatar()) + { + return LLAvatarActions::canCallTo(avatar->getID()); + } + return LLAvatarActions::canCall(); + } }; namespace @@ -9624,8 +9733,6 @@ void initialize_menus() registrar.add("Agent.ToggleMicrophone", boost::bind(&LLAgent::toggleMicrophone, _2), cb_info::UNTRUSTED_BLOCK); enable.add("Agent.IsMicrophoneOn", boost::bind(&LLAgent::isMicrophoneOn, _2)); enable.add("Agent.IsActionAllowed", boost::bind(&LLAgent::isActionAllowed, _2)); - registrar.add("Agent.ToggleHearMediaSoundFromAvatar", boost::bind(&LLAgent::toggleHearMediaSoundFromAvatar), cb_info::UNTRUSTED_BLOCK); - registrar.add("Agent.ToggleHearVoiceFromAvatar", boost::bind(&LLAgent::toggleHearVoiceFromAvatar), cb_info::UNTRUSTED_BLOCK); // File menu init_menu_file(); @@ -9810,6 +9917,7 @@ void initialize_menus() // Develop > Terrain view_listener_t::addMenu(new LLAdvancedRebuildTerrain(), "Advanced.RebuildTerrain"); view_listener_t::addMenu(new LLAdvancedTerrainCreateLocalPaintMap(), "Advanced.TerrainCreateLocalPaintMap"); + view_listener_t::addMenu(new LLAdvancedTerrainEditLocalPaintMap(), "Advanced.TerrainEditLocalPaintMap"); view_listener_t::addMenu(new LLAdvancedTerrainDeleteLocalPaintMap(), "Advanced.TerrainDeleteLocalPaintMap"); // Advanced > UI @@ -9981,7 +10089,7 @@ void initialize_menus() registrar.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", cb_info::UNTRUSTED_BLOCK); - enable.add("Avatar.EnableCall", boost::bind(&LLAvatarActions::canCall)); + enable.add("Avatar.EnableCall", boost::bind(&LLAvatarCall::isAvailable)); view_listener_t::addMenu(new LLAvatarReportAbuse(), "Avatar.ReportAbuse", cb_info::UNTRUSTED_THROTTLE); view_listener_t::addMenu(new LLAvatarToggleMyProfile(), "Avatar.ToggleMyProfile"); view_listener_t::addMenu(new LLAvatarTogglePicks(), "Avatar.TogglePicks"); diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 2a871a24af..14c9c53a9c 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -137,10 +137,10 @@ std::queue<LLFilePickerThread*> LLFilePickerThread::sDeadQ; void LLFilePickerThread::getFile() { -#if LL_WINDOWS +#if LL_WINDOWS || (LL_NFD && !LL_DARWIN) // Todo: get rid of LLFilePickerThread and make this modeless start(); -#elif LL_DARWIN +#elif LL_DARWIN && !LL_NFD runModeless(); #else run(); @@ -150,7 +150,7 @@ void LLFilePickerThread::getFile() //virtual void LLFilePickerThread::run() { -#if LL_WINDOWS +#if LL_WINDOWS || (LL_NFD && !LL_DARWIN) bool blocking = false; #else bool blocking = true; // modal diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index da019d60d8..d722851851 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -118,6 +118,8 @@ #include "llpanelplaceprofile.h" #include "llviewerregion.h" #include "llfloaterregionrestarting.h" +#include "rlvactions.h" +#include "rlvhandler.h" #include "llnotificationmanager.h" // #include "llexperiencecache.h" @@ -2137,6 +2139,21 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) EInstantMessage dialog = (EInstantMessage)d; LLHost sender = msg->getSender(); + LLSD metadata; + if (msg->getNumberOfBlocksFast(_PREHASH_MetaData) > 0) + { + S32 metadata_size = msg->getSizeFast(_PREHASH_MetaData, 0, _PREHASH_Data); + std::string metadata_buffer; + metadata_buffer.resize(metadata_size, 0); + + msg->getBinaryDataFast(_PREHASH_MetaData, _PREHASH_Data, &metadata_buffer[0], metadata_size, 0, metadata_size ); + std::stringstream metadata_stream(metadata_buffer); + if (LLSDSerialize::fromBinary(metadata, metadata_stream, metadata_size) == LLSDParser::PARSE_FAILURE) + { + metadata.clear(); + } + } + LLIMProcessing::processNewMessage(from_id, from_group, to_id, @@ -2151,7 +2168,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) position, binary_bucket, binary_bucket_size, - sender); + sender, + metadata); } void send_do_not_disturb_message (LLMessageSystem* msg, const LLUUID& from_id, const LLUUID& session_id) @@ -2382,15 +2400,16 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) } bool is_audible = (CHAT_AUDIBLE_FULLY == chat.mAudible); + bool show_script_chat_particles = chat.mSourceType == CHAT_SOURCE_OBJECT + && chat.mChatType != CHAT_TYPE_DEBUG_MSG + && gSavedSettings.getBOOL("EffectScriptChatParticles"); 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") ) + if (show_script_chat_particles && (!RlvActions::isRlvEnabled() || CHAT_TYPE_OWNER != chat.mChatType) ) { LLPointer<LLViewerPartSourceChat> psc = new LLViewerPartSourceChat(chatter->getPositionAgent()); psc->setSourceObject(chatter); @@ -2482,8 +2501,25 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) case CHAT_TYPE_SHOUT: prefix += LLTrans::getString("shout") + " "; break; - case CHAT_TYPE_DEBUG_MSG: case CHAT_TYPE_OWNER: + if (RlvActions::isRlvEnabled()) + { + if (RlvHandler::instance().handleSimulatorChat(mesg, chat, chatter)) + { + break; + } + else if (show_script_chat_particles) + { + LLPointer<LLViewerPartSourceChat> 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); + } + } + case CHAT_TYPE_DEBUG_MSG: case CHAT_TYPE_NORMAL: case CHAT_TYPE_DIRECT: break; @@ -6623,7 +6659,6 @@ void process_initiate_download(LLMessageSystem* msg, void**) (void**)new std::string(viewer_filename)); } - void process_script_teleport_request(LLMessageSystem* msg, void**) { if (!gSavedSettings.getBOOL("ScriptsCanShowUI")) return; @@ -6637,6 +6672,11 @@ void process_script_teleport_request(LLMessageSystem* msg, void**) msg->getString("Data", "SimName", sim_name); msg->getVector3("Data", "SimPosition", pos); msg->getVector3("Data", "LookAt", look_at); + U32 flags = (BEACON_SHOW_MAP | BEACON_FOCUS_MAP); + if (msg->has("Options")) + { + msg->getU32("Options", "Flags", flags); + } LLFloaterWorldMap* instance = LLFloaterWorldMap::getInstance(); if(instance) @@ -6647,7 +6687,13 @@ void process_script_teleport_request(LLMessageSystem* msg, void**) << LL_ENDL; instance->trackURL(sim_name, (S32)pos.mV[VX], (S32)pos.mV[VY], (S32)pos.mV[VZ]); - LLFloaterReg::showInstance("world_map", "center"); + if (flags & BEACON_SHOW_MAP) + { + bool old_auto_focus = instance->getAutoFocus(); + instance->setAutoFocus(flags & BEACON_FOCUS_MAP); + instance->openFloater("center"); + instance->setAutoFocus(old_auto_focus); + } } // remove above two lines and replace with below line diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 8c24b2438b..d92341eb96 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -1750,6 +1750,8 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use { instance->mTeleportFinishedSignal(instance->mTeleportInProgressPosition, false); } + instance->postTeleportFinished(instance->mTeleportWithinRegion); + instance->mTeleportWithinRegion = false; } parcel->setParcelEnvironmentVersion(parcel_environment_version); LL_DEBUGS("ENVIRONMENT") << "Parcel environment version is " << parcel->getParcelEnvironmentVersion() << LL_ENDL; @@ -2709,6 +2711,8 @@ void LLViewerParcelMgr::onTeleportFinished(bool local, const LLVector3d& new_pos // Local teleport. We already have the agent parcel data. // Emit the signal immediately. getInstance()->mTeleportFinishedSignal(new_pos, local); + + postTeleportFinished(true); } else { @@ -2717,12 +2721,14 @@ void LLViewerParcelMgr::onTeleportFinished(bool local, const LLVector3d& new_pos // Let's wait for the update and then emit the signal. mTeleportInProgressPosition = new_pos; mTeleportInProgress = true; + mTeleportWithinRegion = local; } } void LLViewerParcelMgr::onTeleportFailed() { mTeleportFailedSignal(); + LLEventPumps::instance().obtain("LLTeleport").post(llsd::map("success", false)); } bool LLViewerParcelMgr::getTeleportInProgress() @@ -2730,3 +2736,17 @@ bool LLViewerParcelMgr::getTeleportInProgress() return mTeleportInProgress // case where parcel data arrives after teleport || gAgent.getTeleportState() > LLAgent::TELEPORT_NONE; // For LOCAL, no mTeleportInProgress } + +void LLViewerParcelMgr::postTeleportFinished(bool local) +{ + auto post = []() { LLEventPumps::instance().obtain("LLTeleport").post(llsd::map("success", true)); }; + if (local) + { + static LLCachedControl<F32> teleport_local_delay(gSavedSettings, "TeleportLocalDelay"); + LL::Timers::instance().scheduleAfter(post, teleport_local_delay + 0.5f); + } + else + { + post(); + } +} diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h index 974ea39359..4d54593c1e 100644 --- a/indra/newview/llviewerparcelmgr.h +++ b/indra/newview/llviewerparcelmgr.h @@ -295,6 +295,8 @@ public: void onTeleportFailed(); bool getTeleportInProgress(); + void postTeleportFinished(bool local); + static bool isParcelOwnedByAgent(const LLParcel* parcelp, U64 group_proxy_power); static bool isParcelModifiableByAgent(const LLParcel* parcelp, U64 group_proxy_power); @@ -344,8 +346,11 @@ private: std::vector<LLParcelObserver*> mObservers; + // Used to communicate between onTeleportFinished() and processParcelProperties() bool mTeleportInProgress; + bool mTeleportWithinRegion{ false }; LLVector3d mTeleportInProgressPosition; + teleport_finished_signal_t mTeleportFinishedSignal; teleport_failed_signal_t mTeleportFailedSignal; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 03d5563c65..739821d2f5 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -38,7 +38,6 @@ #include "llregionhandle.h" #include "llsurface.h" #include "message.h" -//#include "vmath.h" #include "v3math.h" #include "v4math.h" @@ -3156,7 +3155,7 @@ void LLViewerRegion::unpackRegionHandshake() std::string cap = getCapability("ModifyRegion"); // needed for queueQuery if (cap.empty()) { - LLFloaterRegionInfo::sRefreshFromRegion(this); + LLFloaterRegionInfo::refreshFromRegion(this); } else { @@ -3168,7 +3167,7 @@ void LLViewerRegion::unpackRegionHandshake() LLVLComposition* compp = region->getComposition(); if (!compp) { return; } compp->apply(composition_changes); - LLFloaterRegionInfo::sRefreshFromRegion(region); + LLFloaterRegionInfo::refreshFromRegion(region); }); } } diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index a8fe221d98..c219b0575e 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -100,6 +100,7 @@ LLGLSLShader gReflectionProbeDisplayProgram; LLGLSLShader gCopyProgram; LLGLSLShader gCopyDepthProgram; LLGLSLShader gPBRTerrainBakeProgram; +LLGLSLShader gTerrainStampProgram; //object shaders LLGLSLShader gObjectPreviewProgram; @@ -984,7 +985,10 @@ bool LLViewerShaderMgr::loadShadersWater() return loadShadersWater(); } - LLWorld::getInstance()->updateWaterObjects(); + if (LLWorld::instanceExists()) + { + LLWorld::getInstance()->updateWaterObjects(); + } return true; } @@ -3170,6 +3174,34 @@ bool LLViewerShaderMgr::loadShadersInterface() } } + if (gSavedSettings.getBOOL("LocalTerrainPaintEnabled")) + { + if (success) + { + LLGLSLShader* shader = &gTerrainStampProgram; + U32 bit_depth = gSavedSettings.getU32("TerrainPaintBitDepth"); + // LLTerrainPaintMap currently uses an RGB8 texture internally + bit_depth = llclamp(bit_depth, 1, 8); + shader->mName = llformat("Terrain Stamp Shader RGB%o", bit_depth); + + shader->mShaderFiles.clear(); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampV.glsl", GL_VERTEX_SHADER)); + shader->mShaderFiles.push_back(make_pair("interface/terrainStampF.glsl", GL_FRAGMENT_SHADER)); + shader->mShaderLevel = mShaderLevel[SHADER_INTERFACE]; + const U32 value_range = (1 << bit_depth) - 1; + shader->addPermutation("TERRAIN_PAINT_PRECISION", llformat("%d", value_range)); + success = success && shader->createShader(); + //llassert(success); + if (!success) + { + LL_WARNS() << "Failed to create shader '" << shader->mName << "', disabling!" << LL_ENDL; + gSavedSettings.setBOOL("RenderCanUseTerrainBakeShaders", false); + // continue as if this shader never happened + success = true; + } + } + } + if (success) { gAlphaMaskProgram.mName = "Alpha Mask Shader"; diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index b08796025a..e654967c46 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -175,6 +175,7 @@ extern LLGLSLShader gReflectionProbeDisplayProgram; extern LLGLSLShader gCopyProgram; extern LLGLSLShader gCopyDepthProgram; extern LLGLSLShader gPBRTerrainBakeProgram; +extern LLGLSLShader gTerrainStampProgram; //output tex0[tc0] - tex1[tc1] extern LLGLSLShader gTwoTextureCompareProgram; diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 9d4c072909..a4b06b1e1a 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -67,6 +67,7 @@ #include "lluiusage.h" #include "lltranslate.h" #include "llluamanager.h" +#include "scope_exit.h" // "Minimal Vulkan" to get max API Version @@ -510,7 +511,6 @@ void send_viewer_stats(bool include_preferences) return; } - LLSD body; std::string url = gAgent.getRegion()->getCapability("ViewerStats"); if (url.empty()) { @@ -518,8 +518,18 @@ void send_viewer_stats(bool include_preferences) return; } - LLViewerStats::instance().getRecording().pause(); + LLSD body = capture_viewer_stats(include_preferences); + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, body, + "Statistics posted to sim", "Failed to post statistics to sim"); +} + +LLSD capture_viewer_stats(bool include_preferences) +{ + LLViewerStats& vstats{ LLViewerStats::instance() }; + vstats.getRecording().pause(); + LL::scope_exit cleanup([&vstats]{ vstats.getRecording().resume(); }); + LLSD body; LLSD &agent = body["agent"]; time_t ltime; @@ -791,10 +801,7 @@ void send_viewer_stats(bool include_preferences) body["session_id"] = gAgentSessionID; LLViewerStats::getInstance()->addToMessage(body); - - LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, body, - "Statistics posted to sim", "Failed to post statistics to sim"); - LLViewerStats::instance().getRecording().resume(); + return body; } LLTimer& LLViewerStats::PhaseMap::getPhaseTimer(const std::string& phase_name) diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index 8aed1c537e..dd66fee436 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -281,6 +281,7 @@ static const F32 SEND_STATS_PERIOD = 300.0f; // The following are from (older?) statistics code found in appviewer. void update_statistics(); +LLSD capture_viewer_stats(bool include_preferences); void send_viewer_stats(bool include_preferences); void update_texture_time(); diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 0f9c65893d..82fefde0a7 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -43,6 +43,7 @@ #include "message.h" #include "lltimer.h" #include "v4coloru.h" +#include "llnotificationsutil.h" // viewer includes #include "llimagegl.h" @@ -505,8 +506,12 @@ void LLViewerTexture::updateClass() F32 budget = max_vram_budget == 0 ? (F32)gGLManager.mVRAM : (F32)max_vram_budget; - // try to leave half a GB for everyone else, but keep at least 768MB for ourselves - F32 target = llmax(budget - 512.f, MIN_VRAM_BUDGET); + // Try to leave at least half a GB for everyone else and for bias, + // but keep at least 768MB for ourselves + // Viewer can 'overshoot' target when scene changes, if viewer goes over budget it + // can negatively impact performance, so leave 20% of a breathing room for + // 'bias' calculation to kick in. + F32 target = llmax(llmin(budget - 512.f, budget * 0.8f), MIN_VRAM_BUDGET); sFreeVRAMMegabytes = target - used; F32 over_pct = (used - target) / target; @@ -522,7 +527,7 @@ void LLViewerTexture::updateClass() // slam to 1.5 bias the moment we hit low memory (discards off screen textures immediately) sDesiredDiscardBias = llmax(sDesiredDiscardBias, 1.5f); - if (is_sys_low) + if (is_sys_low || over_pct > 2.f) { // if we're low on system memory, emergency purge off screen textures to avoid a death spiral LL_WARNS() << "Low system memory detected, emergency downrezzing off screen textures" << LL_ENDL; for (auto& image : gTextureList) @@ -542,7 +547,6 @@ void LLViewerTexture::updateClass() { static LLCachedControl<F32> low_mem_min_discard_increment(gSavedSettings, "RenderLowMemMinDiscardIncrement", .1f); sDesiredDiscardBias += (F32) low_mem_min_discard_increment * (F32) gFrameIntervalSeconds; - sEvaluationTimer.reset(); } } else @@ -558,20 +562,49 @@ void LLViewerTexture::updateClass() } // set to max discard bias if the window has been backgrounded for a while + static F32 last_desired_discard_bias = 1.f; static bool was_backgrounded = false; static LLFrameTimer backgrounded_timer; + static LLCachedControl<F32> minimized_discard_time(gSavedSettings, "TextureDiscardMinimizedTime", 1.f); + static LLCachedControl<F32> backgrounded_discard_time(gSavedSettings, "TextureDiscardBackgroundedTime", 60.f); bool in_background = (gViewerWindow && !gViewerWindow->getWindow()->getVisible()) || !gFocusMgr.getAppHasFocus(); - + bool is_minimized = gViewerWindow && gViewerWindow->getWindow()->getMinimized() && in_background; if (in_background) { - if (backgrounded_timer.getElapsedTimeF32() > 10.f) + F32 discard_time = is_minimized ? minimized_discard_time : backgrounded_discard_time; + if (discard_time > 0.f && backgrounded_timer.getElapsedTimeF32() > discard_time) { if (!was_backgrounded) { - LL_INFOS() << "Viewer is backgrounded, freeing up video memory." << LL_ENDL; + std::string notification_name; + std::string setting; + if (is_minimized) + { + notification_name = "TextureDiscardMinimized"; + setting = "TextureDiscardMinimizedTime"; + } + else + { + notification_name = "TextureDiscardBackgrounded"; + setting = "TextureDiscardBackgroundedTime"; + } + + LL_INFOS() << "Viewer was " << (is_minimized ? "minimized" : "backgrounded") << " for " << discard_time + << "s, freeing up video memory." << LL_ENDL; + + LLNotificationsUtil::add(notification_name, llsd::map("DELAY", discard_time), LLSD(), + [=](const LLSD& notification, const LLSD& response) + { + if (response["Cancel_okcancelignore"].asBoolean()) + { + LL_INFOS() << "User chose to disable texture discard on " << (is_minimized ? "minimizing." : "backgrounding.") << LL_ENDL; + gSavedSettings.setF32(setting, -1.f); + } + }); + last_desired_discard_bias = sDesiredDiscardBias; + was_backgrounded = true; } - was_backgrounded = true; sDesiredDiscardBias = 4.f; } } @@ -580,9 +613,9 @@ void LLViewerTexture::updateClass() backgrounded_timer.reset(); if (was_backgrounded) { // if the viewer was backgrounded - LL_INFOS() << "Viewer is no longer backgrounded, resuming normal texture usage." << LL_ENDL; + LL_INFOS() << "Viewer is no longer backgrounded or minimized, resuming normal texture usage." << LL_ENDL; was_backgrounded = false; - sDesiredDiscardBias = 1.f; + sDesiredDiscardBias = last_desired_discard_bias; } } @@ -1658,6 +1691,7 @@ void LLViewerFetchedTexture::processTextureStats() if(mDesiredDiscardLevel > mMinDesiredDiscardLevel)//need to load more { mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, mMinDesiredDiscardLevel); + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); mFullyLoaded = false; } //setDebugText("fully loaded"); @@ -1707,6 +1741,7 @@ void LLViewerFetchedTexture::processTextureStats() log((F32)mFullHeight / mKnownDrawHeight) / log_2); mDesiredDiscardLevel = llclamp(mDesiredDiscardLevel, (S8)0, (S8)getMaxDiscardLevel()); mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, mMinDesiredDiscardLevel); + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); } mKnownDrawSizeChanged = false; @@ -2431,6 +2466,7 @@ bool LLViewerFetchedTexture::doLoadedCallbacks() if (mIsRawImageValid) { // If we have an existing raw image, we have a baseline for the raw and auxiliary quality levels. + current_raw_discard = mRawDiscardLevel; best_raw_discard = llmin(best_raw_discard, mRawDiscardLevel); best_aux_discard = llmin(best_aux_discard, mRawDiscardLevel); // We always decode the aux when we decode the base raw current_aux_discard = llmin(current_aux_discard, best_aux_discard); @@ -2889,10 +2925,12 @@ void LLViewerLODTexture::processTextureStats() { // If the image has not been significantly visible in a while, we don't want it mDesiredDiscardLevel = llmin(mMinDesiredDiscardLevel, (S8)(MAX_DISCARD_LEVEL + 1)); + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); } else if (!mFullWidth || !mFullHeight) { mDesiredDiscardLevel = getMaxDiscardLevel(); + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); } else { @@ -2962,6 +3000,7 @@ void LLViewerLODTexture::processTextureStats() // stop requesting more mDesiredDiscardLevel = current_discard; } + mDesiredDiscardLevel = llmin(mDesiredDiscardLevel, (S32)mLoadedCallbackDesiredDiscardLevel); } if(mForceToSaveRawImage && mDesiredSavedRawDiscardLevel >= 0) diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 7b89ae4e44..489e5980d6 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -364,10 +364,31 @@ void LLViewerTextureList::shutdown() mInitialized = false ; //prevent loading textures again. } +namespace +{ + +std::string tex_name_as_string(const LLViewerFetchedTexture* image) +{ + if (!image->getGLTexture()) { return std::string("N/A"); } + return std::to_string(image->getGLTexture()->getTexName()); +} + +const std::string& tex_label_as_string(const LLViewerFetchedTexture* image, std::string& label) +{ + bool error; + image->getGLObjectLabel(label, error); + if (error) { label.assign("N/A"); } + return label; +} + +}; + void LLViewerTextureList::dump() { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - LL_INFOS() << "LLViewerTextureList::dump()" << LL_ENDL; + LL_INFOS() << __FUNCTION__ << "()" << LL_ENDL; + + std::string label; for (image_list_t::iterator it = mImageList.begin(); it != mImageList.end(); ++it) { LLViewerFetchedTexture* image = *it; @@ -378,6 +399,9 @@ void LLViewerTextureList::dump() << " discard " << image->getDiscardLevel() << " desired " << image->getDesiredDiscardLevel() << " http://asset.siva.lindenlab.com/" << image->getID() << ".texture" + << " faces " << image->getTotalNumFaces() + << " texname " << tex_name_as_string(image) + << " label \"" << tex_label_as_string(image, label) << "\"" << LL_ENDL; } } @@ -422,7 +446,12 @@ LLViewerFetchedTexture* LLViewerTextureList::getImageFromFile(const std::string& std::string url = "file://" + full_path; - return getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id); + LLViewerFetchedTexture* tex = getImageFromUrl(url, f_type, usemipmaps, boost_priority, texture_type, internal_format, primary_format, force_id); + if (gDebugTextureLabelLocalFilesSession) + { + gTextureList.mNameTextureList.push_back(LLViewerTextureList::NameElement(tex, filename)); + } + return tex; } LLViewerFetchedTexture* LLViewerTextureList::getImageFromUrl(const std::string& url, @@ -844,6 +873,10 @@ void LLViewerTextureList::updateImages(F32 max_time) //handle results from decode threads updateImagesCreateTextures(remaining_time); + // Label all images (if enabled) + updateImagesNameTextures(); + labelAll(); + bool didone = false; for (image_list_t::iterator iter = mCallbackList.begin(); iter != mCallbackList.end(); ) @@ -1113,6 +1146,73 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time) return create_timer.getElapsedTimeF32(); } +void LLViewerTextureList::updateImagesNameTextures() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + if (gGLManager.mIsDisabled) { return; } + static LLCachedControl<bool> debug_texture_label(gSavedSettings, "RenderDebugTextureLabel", false); + if (!debug_texture_label()) { return; } + + static GLsizei max_length = 0; + if (max_length == 0) { glGetIntegerv(GL_MAX_LABEL_LENGTH, &max_length); } + + auto it = mNameTextureList.begin(); + while (it != mNameTextureList.end()) // For ALL textures needing names + { + LLViewerFetchedTexture* tex = it->mTex; + // Check that the texture is in the list first (otherwise it may be a dead pointer) + // A raw pointer ensures textures are cleaned up when this code isn't running. + const bool alive = mImageList.find(tex) != mImageList.end(); + + if (alive) + { + if (tex->hasGLTexture()) + { + if(tex->getTexName()) + { + tex->setGLObjectLabel(it->mPrefix, true); + it = mNameTextureList.erase(it); // Assume no rename needed + } + else + { + ++it; // Not ready + } + } + else + { + ++it; // Not ready + } + } + else + { + it = mNameTextureList.erase(it); // Remove dead pointer + } + } +} + +void LLViewerTextureList::labelAll() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + static LLCachedControl<bool> debug_texture_label(gSavedSettings, "RenderDebugTextureLabel", false); + if (!debug_texture_label()) { return; } + + static const std::string local_prefix = "lltexlocal"; + static const std::string other_prefix = "lltexother"; + + std::string label; + bool error; + for (LLViewerFetchedTexture* image : mImageList) + { + image->getGLObjectLabel(label, error); + if (!error && label.empty()) + { + const S32 category = image->getGLTexture()->getCategory(); + const std::string& new_prefix = category == LLGLTexture::LOCAL ? local_prefix : other_prefix; + image->setGLObjectLabel(new_prefix, true); + } + } +} + F32 LLViewerTextureList::updateImagesLoadingFastCache(F32 max_time) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; @@ -1169,6 +1269,11 @@ F32 LLViewerTextureList::updateImagesFetchTextures(F32 max_time) //update MIN_UPDATE_COUNT or 5% of other textures, whichever is greater update_count = llmax((U32) MIN_UPDATE_COUNT, (U32) mUUIDMap.size()/20); + if (LLViewerTexture::sDesiredDiscardBias > 1.f) + { + // we are over memory target, update more agresively + update_count = (S32)(update_count * LLViewerTexture::sDesiredDiscardBias); + } update_count = llmin(update_count, (U32) mUUIDMap.size()); { // copy entries out of UUID map to avoid iterator invalidation from deletion inside updateImageDecodeProiroty or updateFetch below @@ -1295,6 +1400,10 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) max_time = llmax(max_time, .001f); F32 create_time = updateImagesCreateTextures(max_time); + // Label all images (if enabled) + updateImagesNameTextures(); + labelAll(); + LL_DEBUGS("ViewerImages") << "decodeAllImages() took " << timer.getElapsedTimeF32() << " seconds. " << " fetch_pending " << fetch_pending << " create_time " << create_time @@ -1318,6 +1427,11 @@ bool LLViewerTextureList::createUploadFile(LLPointer<LLImageRaw> raw_image, raw_image->getComponents()); LLPointer<LLImageJ2C> compressedImage = LLViewerTextureList::convertToUploadFile(scale_image, max_image_dimentions); + if (compressedImage.isNull()) + { + LL_INFOS() << "Couldn't convert to j2c, file : " << out_filename << LL_ENDL; + return false; + } if (compressedImage->getWidth() < min_image_dimentions || compressedImage->getHeight() < min_image_dimentions) { std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx", @@ -1328,12 +1442,6 @@ bool LLViewerTextureList::createUploadFile(LLPointer<LLImageRaw> raw_image, compressedImage->setLastError(reason); return false; } - if (compressedImage.isNull()) - { - compressedImage->setLastError("Couldn't convert the image to jpeg2000."); - LL_INFOS() << "Couldn't convert to j2c, file : " << out_filename << LL_ENDL; - return false; - } if (!compressedImage->save(out_filename)) { compressedImage->setLastError("Couldn't create the jpeg2000 image for upload."); diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index 7c7112f4cf..08dd2d0f7f 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -153,6 +153,9 @@ private: void updateImagesUpdateStats(); F32 updateImagesLoadingFastCache(F32 max_time); + void updateImagesNameTextures(); + void labelAll(); + void addImage(LLViewerFetchedTexture *image, ETexListType tex_type); void deleteImage(LLViewerFetchedTexture *image); @@ -214,6 +217,14 @@ public: // images that have been loaded but are waiting to be uploaded to GL image_queue_t mCreateTextureList; + struct NameElement + { + NameElement(LLViewerFetchedTexture* tex, const std::string& prefix) : mTex(tex), mPrefix(prefix) {} + LLViewerFetchedTexture* mTex; + std::string mPrefix; + }; + std::vector<NameElement> mNameTextureList; + // images that must be downscaled quickly so we don't run out of memory image_queue_t mDownScaleQueue; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index c747319940..d339d0c37c 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -268,9 +268,6 @@ static const F32 MIN_DISPLAY_SCALE = 0.75f; static const char KEY_MOUSELOOK = 'M'; -static LLCachedControl<std::string> sSnapshotBaseName(LLCachedControl<std::string>(gSavedPerAccountSettings, "SnapshotBaseName", "Snapshot")); -static LLCachedControl<std::string> sSnapshotDir(LLCachedControl<std::string>(gSavedPerAccountSettings, "SnapshotBaseDir", "")); - LLTrace::SampleStatHandle<> LLViewerWindow::sMouseVelocityStat("Mouse Velocity"); @@ -1330,7 +1327,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi // Check the whitelist, if there's media (otherwise just show it) if (te->getMediaData() == NULL || te->getMediaData()->checkCandidateUrl(url)) { - if ( obj != mDragHoveredObject) + if (obj != mDragHoveredObject) { // Highlight the dragged object LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); @@ -1935,6 +1932,11 @@ LLViewerWindow::LLViewerWindow(const Params& p) } LLFontManager::initClass(); + + // fonts use an GL_UNSIGNED_BYTE image format, + // so they need convertion, init buffers if needed + LLImageGL::allocateConversionBuffer(); + // Init font system, load default fonts and generate basic glyphs // currently it takes aprox. 0.5 sec and we would load these fonts anyway // before login screen. @@ -2007,6 +2009,7 @@ LLViewerWindow::LLViewerWindow(const Params& p) std::string LLViewerWindow::getLastSnapshotDir() { + static LLCachedControl<std::string> sSnapshotDir(gSavedPerAccountSettings, "SnapshotBaseDir", ""); return sSnapshotDir; } @@ -3777,7 +3780,7 @@ void LLViewerWindow::updateLayout() { LLToolMgr* tool_mgr = LLToolMgr::getInstance(); LLTool* tool = tool_mgr->getCurrentTool(); - LLCachedControl<bool> freeze_time(gSavedSettings, "FreezeTime"); + static LLCachedControl<bool> freeze_time(gSavedSettings, "FreezeTime"); if (gFloaterTools != NULL && tool != NULL && tool != gToolNull @@ -4706,6 +4709,7 @@ void LLViewerWindow::saveImageNumbered(LLImageFormatted *image, bool force_picke // Get a base file location if needed. if (force_picker || !isSnapshotLocSet()) { + static LLCachedControl<std::string> sSnapshotBaseName(gSavedPerAccountSettings, "SnapshotBaseName", "Snapshot"); std::string proposed_name(sSnapshotBaseName); // getSaveFile will append an appropriate extension to the proposed name, based on the ESaveFilter constant passed in. @@ -4751,6 +4755,9 @@ void LLViewerWindow::onSelectionFailure(const snapshot_saved_signal_t::slot_type void LLViewerWindow::saveImageLocal(LLImageFormatted *image, const snapshot_saved_signal_t::slot_type& success_cb, const snapshot_saved_signal_t::slot_type& failure_cb) { + static LLCachedControl<std::string> sSnapshotBaseName(gSavedPerAccountSettings, "SnapshotBaseName", "Snapshot"); + static LLCachedControl<std::string> sSnapshotDir(gSavedPerAccountSettings, "SnapshotBaseDir", ""); + std::string lastSnapshotDir = LLViewerWindow::getLastSnapshotDir(); if (lastSnapshotDir.empty()) { @@ -4902,8 +4909,8 @@ void LLViewerWindow::playSnapshotAnimAndSound() bool LLViewerWindow::isSnapshotLocSet() const { - std::string snapshot_dir = sSnapshotDir; - return !snapshot_dir.empty(); + static LLCachedControl<std::string> sSnapshotDir(gSavedPerAccountSettings, "SnapshotBaseDir", ""); + return !sSnapshotDir().empty(); } void LLViewerWindow::resetSnapshotLoc() const diff --git a/indra/newview/llviewerwindowlistener.cpp b/indra/newview/llviewerwindowlistener.cpp index 52f413792a..45b4da0aac 100644 --- a/indra/newview/llviewerwindowlistener.cpp +++ b/indra/newview/llviewerwindowlistener.cpp @@ -35,6 +35,7 @@ // std headers // external library headers // other Linden headers +#include "llcallbacklist.h" #include "llviewerwindow.h" LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow): @@ -46,8 +47,7 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow): add("saveSnapshot", "Save screenshot: [\"filename\"] (extension may be specified: bmp, jpeg, png)\n" "[\"width\"], [\"height\"], [\"showui\"], [\"showhud\"], [\"rebuild\"], [\"type\"]\n" - "type: \"COLOR\", \"DEPTH\"\n" - "Post on [\"reply\"] an event containing [\"result\"]", + "type: \"COLOR\", \"DEPTH\"\n", &LLViewerWindowListener::saveSnapshot, llsd::map("filename", LLSD::String(), "reply", LLSD())); add("requestReshape", @@ -57,8 +57,6 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow): void LLViewerWindowListener::saveSnapshot(const LLSD& event) const { - Response response(LLSD(), event); - typedef std::map<LLSD::String, LLSnapshotModel::ESnapshotLayerType> TypeMap; TypeMap types; #define tp(name) types[#name] = LLSnapshotModel::SNAPSHOT_TYPE_##name @@ -88,7 +86,8 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const TypeMap::const_iterator found = types.find(event["type"]); if (found == types.end()) { - return response.error(stringize("Unrecognized type ", std::quoted(event["type"].asString()), " [\"COLOR\"] or [\"DEPTH\"] is expected.")); + sendReply(llsd::map("error", stringize("Unrecognized type ", std::quoted(event["type"].asString()), " [\"COLOR\"] or [\"DEPTH\"] is expected.")), event); + return; } type = found->second; } @@ -96,7 +95,8 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const std::string filename(event["filename"]); if (filename.empty()) { - return response.error(stringize("File path is empty.")); + sendReply(llsd::map("error", stringize("File path is empty.")), event); + return; } LLSnapshotModel::ESnapshotFormat format(LLSnapshotModel::SNAPSHOT_FORMAT_BMP); @@ -115,9 +115,13 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const } else if (ext != "bmp") { - return response.error(stringize("Unrecognized format. [\"png\"], [\"jpeg\"] or [\"bmp\"] is expected.")); + sendReply(llsd::map("error", stringize("Unrecognized format. [\"png\"], [\"jpeg\"] or [\"bmp\"] is expected.")), event); + return; } - response["result"] = mViewerWindow->saveSnapshot(filename, width, height, showui, showhud, rebuild, type, format); + // take snapshot on the main coro + doOnIdleOneTime([this, event, filename, width, height, showui, showhud, rebuild, type, format]() + { sendReply(llsd::map("result", mViewerWindow->saveSnapshot(filename, width, height, showui, showhud, rebuild, type, format)), event); }); + } void LLViewerWindowListener::requestReshape(LLSD const & event_data) const diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp index 077e6e6cb1..ca76d93cd7 100644 --- a/indra/newview/llvlcomposition.cpp +++ b/indra/newview/llvlcomposition.cpp @@ -320,7 +320,11 @@ LLViewerTexture* LLTerrainMaterials::getPaintMap() void LLTerrainMaterials::setPaintMap(LLViewerTexture* paint_map) { llassert(!paint_map || mPaintType == TERRAIN_PAINT_TYPE_PBR_PAINTMAP); + const bool changed = paint_map != mPaintMap; mPaintMap = paint_map; + // The paint map has changed, so edits are no longer valid + mPaintRequestQueue.clear(); + mPaintMapQueue.clear(); } // Boost the texture loading priority diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h index f15f9bff6a..21fd484375 100644 --- a/indra/newview/llvlcomposition.h +++ b/indra/newview/llvlcomposition.h @@ -31,6 +31,7 @@ #include "llviewershadermgr.h" #include "llviewertexture.h" #include "llpointer.h" +#include "llterrainpaintmap.h" #include "llimage.h" @@ -87,6 +88,18 @@ public: void setPaintType(U32 paint_type) { mPaintType = paint_type; } LLViewerTexture* getPaintMap(); void setPaintMap(LLViewerTexture* paint_map); + // Queue of client-triggered brush operations that need to be converted + // into a form that can be sent to the server. + // TODO: Consider getting rid of mPaintRequestQueue, as it's not really needed (brushes go directly to RGB queue) + LLTerrainBrushQueue& getBrushQueue() { return mBrushQueue; } + // Queue of client-triggered paint operations that need to be converted + // into a form that can be sent to the server. + // Paints in this queue are in RGBA format. + LLTerrainPaintQueue& getPaintRequestQueue() { return mPaintRequestQueue; } + // Paint queue for current paint map - this queue gets applied directly to + // the paint map. Paints within are assumed to have already been sent to + // the server. Paints in this queue are in RGB format. + LLTerrainPaintQueue& getPaintMapQueue() { return mPaintMapQueue; } protected: void unboost(); @@ -105,6 +118,9 @@ protected: U32 mPaintType = TERRAIN_PAINT_TYPE_HEIGHTMAP_WITH_NOISE; LLPointer<LLViewerTexture> mPaintMap; + LLTerrainBrushQueue mBrushQueue; + LLTerrainPaintQueue mPaintRequestQueue{U8(4)}; + LLTerrainPaintQueue mPaintMapQueue{U8(3)}; }; // Local materials to override all regions diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index c8a4e4c205..bfed8b9d39 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -687,7 +687,6 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mVisualComplexity(VISUAL_COMPLEXITY_UNKNOWN), mLoadedCallbacksPaused(false), mLoadedCallbackTextures(0), - mRenderUnloadedAvatar(LLCachedControl<bool>(gSavedSettings, "RenderUnloadedAvatar", false)), mLastRezzedStatus(-1), mIsEditingAppearance(false), mUseLocalAppearance(false), @@ -3767,6 +3766,7 @@ LLVector3 LLVOAvatar::idleCalcNameTagPosition(const LLVector3 &root_pos_last) // Avoid of crossing the name tag by the water surface if (mNameText) { + bool hidden = false; F32 water_height = getRegion()->getWaterHeight(); static const F32 WATER_HEIGHT_ABOVE_DELTA = 0.25; if (name_position[VZ] < water_height + WATER_HEIGHT_ABOVE_DELTA) @@ -3780,8 +3780,19 @@ LLVector3 LLVOAvatar::idleCalcNameTagPosition(const LLVector3 &root_pos_last) { name_position[VZ] = water_height + WATER_HEIGHT_ABOVE_DELTA; } + else + { + // Hide the name tag when the camera is above the water + // but the avatar is deep enough under the water surface + hidden = true; + } } } + + if (mNameText->getHidden() != hidden) + { + mNameText->setHidden(hidden); + } } return name_position; @@ -8511,7 +8522,8 @@ bool LLVOAvatar::processFullyLoadedChange(bool loading) bool LLVOAvatar::isFullyLoaded() const { - return (mRenderUnloadedAvatar || mFullyLoaded); + static LLCachedControl<bool> render_unloaded_avatars(gSavedSettings, "RenderUnloadedAvatar", false); + return (render_unloaded_avatars || mFullyLoaded); } bool LLVOAvatar::isTooComplex() const diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index dd1725c322..a9dea1f1e3 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -668,8 +668,6 @@ private: LLVector3 mLastAnimExtents[2]; LLVector3 mLastAnimBasePos; - LLCachedControl<bool> mRenderUnloadedAvatar; - //-------------------------------------------------------------------- // Wind rippling in clothes //-------------------------------------------------------------------- @@ -1014,7 +1012,7 @@ public: void startTyping() { mTyping = true; mTypingTimer.reset(); } void stopTyping() { mTyping = false; } private: - bool mVisibleChat; + bool mVisibleChat = false; //-------------------------------------------------------------------- // Lip synch morphs @@ -1196,7 +1194,7 @@ public: static F32 sGreyUpdateTime; // Last time stats were updated (to prevent multiple updates per frame) protected: S32 getUnbakedPixelAreaRank(); - bool mHasGrey; + bool mHasGrey = false; private: F32 mMinPixelArea; F32 mMaxPixelArea; diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 1a35a71706..243cba6ffd 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -391,12 +391,21 @@ void LLVoiceClient::setRenderDevice(const std::string& name) LLWebRTCVoiceClient::getInstance()->setRenderDevice(name); } +bool LLVoiceClient::isCaptureNoDevice() +{ + return LLWebRTCVoiceClient::getInstance()->isCaptureNoDevice(); +} + +bool LLVoiceClient::isRenderNoDevice() +{ + return LLWebRTCVoiceClient::getInstance()->isRenderNoDevice(); +} + const LLVoiceDeviceList& LLVoiceClient::getCaptureDevices() { return LLWebRTCVoiceClient::getInstance()->getCaptureDevices(); } - const LLVoiceDeviceList& LLVoiceClient::getRenderDevices() { return LLWebRTCVoiceClient::getInstance()->getRenderDevices(); diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index d53f512d82..2731b0cc7f 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -192,6 +192,9 @@ public: virtual LLVoiceDeviceList& getCaptureDevices()=0; virtual LLVoiceDeviceList& getRenderDevices()=0; + virtual bool isCaptureNoDevice() = 0; + virtual bool isRenderNoDevice() = 0; + virtual void getParticipantList(std::set<LLUUID> &participants)=0; virtual bool isParticipant(const LLUUID& speaker_id)=0; //@} @@ -392,6 +395,8 @@ public: void setCaptureDevice(const std::string& name); void setRenderDevice(const std::string& name); + bool isCaptureNoDevice(); + bool isRenderNoDevice(); void setHidden(bool hidden); const LLVoiceDeviceList& getCaptureDevices(); diff --git a/indra/newview/llvoicevisualizer.cpp b/indra/newview/llvoicevisualizer.cpp index 7691ac54f3..c5704982b8 100644 --- a/indra/newview/llvoicevisualizer.cpp +++ b/indra/newview/llvoicevisualizer.cpp @@ -337,7 +337,7 @@ void LLVoiceVisualizer::lipSyncOohAah( F32& ooh, F32& aah ) //--------------------------------------------------- void LLVoiceVisualizer::render() { - static LLCachedControl<bool> show_visualizer(gSavedSettings, "VoiceVisualizerEnabled", false); + static LLCachedControl<bool> show_visualizer(gSavedSettings, "VoiceVisualizerEnabled", true); if (!mVoiceEnabled || !show_visualizer) { return; diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 4659fa5f71..b8ddc1f255 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -2775,6 +2775,7 @@ void LLVivoxVoiceClient::setCaptureDevice(const std::string& name) } } } + void LLVivoxVoiceClient::setDevicesListUpdated(bool state) { mDevicesListUpdated = state; @@ -6637,13 +6638,13 @@ void LLVivoxVoiceClient::expireVoiceFonts() } } - LLSD args; - args["URL"] = LLTrans::getString("voice_morphing_url"); - args["PREMIUM_URL"] = LLTrans::getString("premium_voice_morphing_url"); - // Give a notification if any voice fonts have expired. if (have_expired) { + LLSD args; + args["URL"] = LLTrans::getString("voice_morphing_url"); + args["PREMIUM_URL"] = LLTrans::getString("premium_voice_morphing_url"); + if (expired_in_use) { LLNotificationsUtil::add("VoiceEffectsExpiredInUse", args); diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 3167705528..cec8b71442 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -121,6 +121,9 @@ public: void setCaptureDevice(const std::string& name) override; void setRenderDevice(const std::string& name) override; + bool isCaptureNoDevice() override { return false; }; + bool isRenderNoDevice() override { return false; }; + LLVoiceDeviceList& getCaptureDevices() override; LLVoiceDeviceList& getRenderDevices() override; //@} diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 7161f5af5e..6d4f3fe356 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -203,6 +203,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mTuningMode(false), mTuningMicGain(0.0), mTuningSpeakerVolume(50), // Set to 50 so the user can hear themselves when he sets his mic volume + mDeviceSettingsAvailable(false), mDevicesListUpdated(false), mSpatialCoordsDirty(false), @@ -218,7 +219,7 @@ LLWebRTCVoiceClient::LLWebRTCVoiceClient() : mAvatarNameCacheConnection(), mIsInTuningMode(false), mIsProcessingChannels(false), - mIsCoroutineActive(false), + mIsTimerActive(false), mWebRTCPump("WebRTCClientPump"), mWebRTCDeviceInterface(nullptr) { @@ -295,6 +296,20 @@ void LLWebRTCVoiceClient::cleanUp() mNeighboringRegions.clear(); sessionState::for_each(boost::bind(predShutdownSession, _1)); LL_DEBUGS("Voice") << "Exiting" << LL_ENDL; + stopTimer(); +} + +void LLWebRTCVoiceClient::stopTimer() +{ + if (mIsTimerActive) + { + LLMuteList::instanceExists(); + { + LLMuteList::getInstance()->removeObserver(this); + } + mIsTimerActive = false; + LL::Timers::instance().cancel(mVoiceTimerHandle); + } } void LLWebRTCVoiceClient::LogMessage(llwebrtc::LLWebRTCLogCallback::LogLevel level, const std::string& message) @@ -443,8 +458,7 @@ void LLWebRTCVoiceClient::removeObserver(LLFriendObserver *observer) //--------------------------------------------------- // Primary voice loop. -// This voice loop is called every 100ms plus the time it -// takes to process the various functions called in the loop +// This voice loop is called every 100ms // The loop does the following: // * gates whether we do channel processing depending on // whether we're running a WebRTC voice channel or @@ -456,102 +470,94 @@ void LLWebRTCVoiceClient::removeObserver(LLFriendObserver *observer) // connection to various voice channels. // * Sends updates to the voice server when this agent's // voice levels, or positions have changed. -void LLWebRTCVoiceClient::voiceConnectionCoro() +void LLWebRTCVoiceClient::connectionTimer() { - LL_DEBUGS("Voice") << "starting" << LL_ENDL; - mIsCoroutineActive = true; - LLCoros::set_consuming(true); + LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE; try { - LLMuteList::getInstance()->addObserver(this); - while (!sShuttingDown) - { - LL_PROFILE_ZONE_NAMED_CATEGORY_VOICE("voiceConnectionCoroLoop") - // TODO: Doing some measurement and calculation here, - // we could reduce the timeout to take into account the - // time spent on the previous loop to have the loop - // cycle at exactly 100ms, instead of 100ms + loop - // execution time. - // Could help with voice updates making for smoother - // voice when we're busy. - llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); - if (sShuttingDown) return; // 'this' migh already be invalid - bool voiceEnabled = mVoiceEnabled; - - if (!isAgentAvatarValid()) + bool voiceEnabled = mVoiceEnabled; + + if (!isAgentAvatarValid()) + { + if (sShuttingDown) { - continue; + cleanUp(); } + return; + } - LLViewerRegion *regionp = gAgent.getRegion(); - if (!regionp) + LLViewerRegion* regionp = gAgent.getRegion(); + if (!regionp) + { + if (sShuttingDown) { - continue; + cleanUp(); } + return; + } - if (!mProcessChannels) + if (!mProcessChannels) + { + // we've switched away from webrtc voice, so shut all channels down. + // leave channel can be called again and again without adverse effects. + // it merely tells channels to shut down if they're not already doing so. + leaveChannel(false); + } + else if (inSpatialChannel()) + { + bool useEstateVoice = true; + // add session for region or parcel voice. + if (!regionp || regionp->getRegionID().isNull()) { - // we've switched away from webrtc voice, so shut all channels down. - // leave channel can be called again and again without adverse effects. - // it merely tells channels to shut down if they're not already doing so. - leaveChannel(false); + // no region, no voice. + return; } - else if (inSpatialChannel()) - { - bool useEstateVoice = true; - // add session for region or parcel voice. - if (!regionp || regionp->getRegionID().isNull()) - { - // no region, no voice. - continue; - } - voiceEnabled = voiceEnabled && regionp->isVoiceEnabled(); + voiceEnabled = voiceEnabled && regionp->isVoiceEnabled(); - if (voiceEnabled) + if (voiceEnabled) + { + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + // check to see if parcel changed. + if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) { - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - // check to see if parcel changed. - if (parcel && parcel->getLocalID() != INVALID_PARCEL_ID) + // parcel voice + if (!parcel->getParcelFlagAllowVoice()) { - // parcel voice - if (!parcel->getParcelFlagAllowVoice()) - { - voiceEnabled = false; - } - else if (!parcel->getParcelFlagUseEstateVoiceChannel()) - { - // use the parcel-specific voice channel. - S32 parcel_local_id = parcel->getLocalID(); - std::string channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); - - useEstateVoice = false; - if (!inOrJoiningChannel(channelID)) - { - startParcelSession(channelID, parcel_local_id); - } - } + voiceEnabled = false; } - if (voiceEnabled && useEstateVoice && !inEstateChannel()) + else if (!parcel->getParcelFlagUseEstateVoiceChannel()) { - // estate voice - startEstateSession(); + // use the parcel-specific voice channel. + S32 parcel_local_id = parcel->getLocalID(); + std::string channelID = regionp->getRegionID().asString() + "-" + std::to_string(parcel->getLocalID()); + + useEstateVoice = false; + if (!inOrJoiningChannel(channelID)) + { + startParcelSession(channelID, parcel_local_id); + } } } - if (!voiceEnabled) + if (voiceEnabled && useEstateVoice && !inEstateChannel()) { - // voice is disabled, so leave and disable PTT - leaveChannel(true); - } - else - { - // we're in spatial voice, and voice is enabled, so determine positions in order - // to send position updates. - updatePosition(); + // estate voice + startEstateSession(); } } + if (!voiceEnabled) + { + // voice is disabled, so leave and disable PTT + leaveChannel(true); + } + else + { + // we're in spatial voice, and voice is enabled, so determine positions in order + // to send position updates. + updatePosition(); + } LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (sShuttingDown) { return; @@ -565,10 +571,6 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() }); } } - catch (const LLCoros::Stop&) - { - LL_DEBUGS("LLWebRTCVoiceClient") << "Received a shutdown exception" << LL_ENDL; - } catch (const LLContinueError&) { LOG_UNHANDLED_EXCEPTION("LLWebRTCVoiceClient"); @@ -582,7 +584,10 @@ void LLWebRTCVoiceClient::voiceConnectionCoro() throw; } - cleanUp(); + if (sShuttingDown) + { + cleanUp(); + } } // For spatial, determine which neighboring regions to connect to @@ -640,12 +645,14 @@ void LLWebRTCVoiceClient::leaveAudioSession() void LLWebRTCVoiceClient::clearCaptureDevices() { LL_DEBUGS("Voice") << "called" << LL_ENDL; + mDeviceSettingsAvailable = false; mCaptureDevices.clear(); } void LLWebRTCVoiceClient::addCaptureDevice(const LLVoiceDevice& device) { LL_INFOS("Voice") << "Voice Capture Device: '" << device.display_name << "' (" << device.full_name << ")" << LL_ENDL; + mDeviceSettingsAvailable = false; mCaptureDevices.push_back(device); } @@ -658,6 +665,7 @@ void LLWebRTCVoiceClient::setCaptureDevice(const std::string& name) { mWebRTCDeviceInterface->setCaptureDevice(name); } + void LLWebRTCVoiceClient::setDevicesListUpdated(bool state) { mDevicesListUpdated = state; @@ -669,7 +677,7 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi { LL::WorkQueue::postMaybe(mMainQueue, - [=] + [=, this] { OnDevicesChangedImpl(render_devices, capture_devices); }); @@ -703,20 +711,22 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi } setCaptureDevice(inputDevice); + mDeviceSettingsAvailable = true; setDevicesListUpdated(true); } void LLWebRTCVoiceClient::clearRenderDevices() { LL_DEBUGS("Voice") << "called" << LL_ENDL; + mDeviceSettingsAvailable = false; mRenderDevices.clear(); } void LLWebRTCVoiceClient::addRenderDevice(const LLVoiceDevice& device) { LL_INFOS("Voice") << "Voice Render Device: '" << device.display_name << "' (" << device.full_name << ")" << LL_ENDL; + mDeviceSettingsAvailable = false; mRenderDevices.push_back(device); - } LLVoiceDeviceList& LLWebRTCVoiceClient::getRenderDevices() @@ -729,6 +739,16 @@ void LLWebRTCVoiceClient::setRenderDevice(const std::string& name) mWebRTCDeviceInterface->setRenderDevice(name); } +bool LLWebRTCVoiceClient::isCaptureNoDevice() +{ + return mCaptureDevices.empty() || mWebRTCDeviceInterface->isCaptureNoDevice(); +} + +bool LLWebRTCVoiceClient::isRenderNoDevice() +{ + return mRenderDevices.empty() || mWebRTCDeviceInterface->isRenderNoDevice(); +} + void LLWebRTCVoiceClient::tuningStart() { if (!mIsInTuningMode) @@ -754,11 +774,15 @@ bool LLWebRTCVoiceClient::inTuningMode() void LLWebRTCVoiceClient::tuningSetMicVolume(float volume) { - mTuningMicGain = volume; + mTuningMicGain = volume; } void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) { + if (isRenderNoDevice()) + { + volume = 0; + } if (volume != mTuningSpeakerVolume) { @@ -768,14 +792,17 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume) float LLWebRTCVoiceClient::getAudioLevel() { - if (mIsInTuningMode) + if (isCaptureNoDevice()) { - return (1.0f - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1f; + return 0; } - else + + if (mIsInTuningMode) { - return (1.0f - mWebRTCDeviceInterface->getPeerConnectionAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1f; + return (1.0f - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1f; } + + return (1.0f - mWebRTCDeviceInterface->getPeerConnectionAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1f; } float LLWebRTCVoiceClient::tuningGetEnergy(void) @@ -783,15 +810,6 @@ float LLWebRTCVoiceClient::tuningGetEnergy(void) return getAudioLevel(); } -bool LLWebRTCVoiceClient::deviceSettingsAvailable() -{ - bool result = true; - - if(mRenderDevices.empty() || mCaptureDevices.empty()) - result = false; - - return result; -} bool LLWebRTCVoiceClient::deviceSettingsUpdated() { bool updated = mDevicesListUpdated; @@ -801,7 +819,7 @@ bool LLWebRTCVoiceClient::deviceSettingsUpdated() void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) { - if(clearCurrentList) + if (clearCurrentList) { clearCaptureDevices(); clearRenderDevices(); @@ -809,7 +827,6 @@ void LLWebRTCVoiceClient::refreshDeviceLists(bool clearCurrentList) mWebRTCDeviceInterface->refreshDevices(); } - void LLWebRTCVoiceClient::setHidden(bool hidden) { mHidden = hidden; @@ -1340,7 +1357,7 @@ bool LLWebRTCVoiceClient::isVoiceWorking() const // webrtc is working if the coroutine is active in the case of // webrtc. WebRTC doesn't need to connect to a secondary process // or a login server to become active. - return mIsCoroutineActive; + return mIsTimerActive; } // Returns true if calling back the session URI after the session has closed is possible. @@ -1523,9 +1540,7 @@ void LLWebRTCVoiceClient::setVoiceVolume(F32 volume) { if (volume != mSpeakerVolume) { - { - mSpeakerVolume = volume; - } + mSpeakerVolume = volume; sessionState::for_each(boost::bind(predSetSpeakerVolume, _1, volume)); } } @@ -1544,7 +1559,6 @@ void LLWebRTCVoiceClient::setMicGain(F32 gain) } } - void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOICE; @@ -1552,7 +1566,7 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) LL_DEBUGS("Voice") << "( " << (enabled ? "enabled" : "disabled") << " )" << " was "<< (mVoiceEnabled ? "enabled" : "disabled") - << " coro "<< (mIsCoroutineActive ? "active" : "inactive") + << " coro "<< (mIsTimerActive ? "active" : "inactive") << LL_ENDL; if (enabled != mVoiceEnabled) @@ -1569,10 +1583,12 @@ void LLWebRTCVoiceClient::setVoiceEnabled(bool enabled) status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED; mSpatialCoordsDirty = true; updatePosition(); - if (!mIsCoroutineActive) + if (!mIsTimerActive) { - LLCoros::instance().launch("LLWebRTCVoiceClient::voiceConnectionCoro", - boost::bind(&LLWebRTCVoiceClient::voiceConnectionCoro, LLWebRTCVoiceClient::getInstance())); + LL_DEBUGS("Voice") << "Starting" << LL_ENDL; + mIsTimerActive = true; + LLMuteList::getInstance()->addObserver(this); + mVoiceTimerHandle = LL::Timers::instance().scheduleEvery([this]() { connectionTimer(); return false; }, UPDATE_THROTTLE_SECONDS); } else { @@ -2205,7 +2221,7 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState state) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL; switch (state) @@ -2228,7 +2244,7 @@ void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObs // callback from llwebrtc void LLVoiceWebRTCConnection::OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate& candidate) { - LL::WorkQueue::postMaybe(mMainQueue, [=] { mIceCandidates.push_back(candidate); }); + LL::WorkQueue::postMaybe(mMainQueue, [=, this] { mIceCandidates.push_back(candidate); }); } void LLVoiceWebRTCConnection::processIceUpdates() @@ -2346,7 +2362,7 @@ void LLVoiceWebRTCConnection::processIceUpdatesCoro(connectionPtr_t connection) void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; @@ -2373,7 +2389,7 @@ void LLVoiceWebRTCConnection::OnOfferAvailable(const std::string &sdp) void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface* audio_interface) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; @@ -2395,7 +2411,7 @@ void LLVoiceWebRTCConnection::OnAudioEstablished(llwebrtc::LLWebRTCAudioInterfac void LLVoiceWebRTCConnection::OnRenegotiationNeeded() { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Voice channel requires renegotiation." << LL_ENDL; if (!mShutDown) { @@ -2409,7 +2425,7 @@ void LLVoiceWebRTCConnection::OnRenegotiationNeeded() void LLVoiceWebRTCConnection::OnPeerConnectionClosed() { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { LL_DEBUGS("Voice") << "Peer connection has closed." << LL_ENDL; if (mVoiceConnectionState == VOICE_STATE_WAIT_FOR_CLOSE) { @@ -2881,7 +2897,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine() // llwebrtc callback void LLVoiceWebRTCConnection::OnDataReceived(const std::string& data, bool binary) { - LL::WorkQueue::postMaybe(mMainQueue, [=] { LLVoiceWebRTCConnection::OnDataReceivedImpl(data, binary); }); + LL::WorkQueue::postMaybe(mMainQueue, [=, this] { LLVoiceWebRTCConnection::OnDataReceivedImpl(data, binary); }); } // @@ -3037,7 +3053,7 @@ void LLVoiceWebRTCConnection::OnDataReceivedImpl(const std::string &data, bool b void LLVoiceWebRTCConnection::OnDataChannelReady(llwebrtc::LLWebRTCDataInterface *data_interface) { LL::WorkQueue::postMaybe(mMainQueue, - [=] { + [=, this] { if (mShutDown) { return; diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index ff82d2739d..88ead98950 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -30,6 +30,7 @@ class LLWebRTCProtocolParser; #include "lliopipe.h" #include "llpumpio.h" +#include "llcallbacklist.h" #include "llchainio.h" #include "lliosocket.h" #include "v3math.h" @@ -113,7 +114,7 @@ public: /// @name Devices //@{ // This returns true when it's safe to bring up the "device settings" dialog in the prefs. - bool deviceSettingsAvailable() override; + bool deviceSettingsAvailable() override { return mDeviceSettingsAvailable; } bool deviceSettingsUpdated() override; //return if the list has been updated and never fetched, only to be called from the voicepanel. // Requery the WebRTC daemon for the current list of input/output devices. @@ -125,6 +126,9 @@ public: void setCaptureDevice(const std::string& name) override; void setRenderDevice(const std::string& name) override; + bool isCaptureNoDevice() override; + bool isRenderNoDevice() override; + LLVoiceDeviceList& getCaptureDevices() override; LLVoiceDeviceList& getRenderDevices() override; //@} @@ -447,19 +451,23 @@ private: // Coroutine support methods //--- - void voiceConnectionCoro(); + void connectionTimer(); //--- /// Clean up objects created during a voice session. void cleanUp(); + // stop state machine + void stopTimer(); + LL::WorkQueue::weak_t mMainQueue; bool mTuningMode; F32 mTuningMicGain; int mTuningSpeakerVolume; + bool mDeviceSettingsAvailable; bool mDevicesListUpdated; // set to true when the device list has been updated - // and false when the panelvoicedevicesettings has queried for an update status. + // and false when the panelvoicedevicesettings has queried for an update status. std::string mSpatialSessionCredentials; std::string mMainSessionGroupHandle; // handle of the "main" session group. @@ -534,7 +542,8 @@ private: bool mIsInTuningMode; bool mIsProcessingChannels; - bool mIsCoroutineActive; + bool mIsTimerActive; + LL::Timers::handle_t mVoiceTimerHandle; // These variables can last longer than WebRTC in coroutines so we need them as static static bool sShuttingDown; diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 31f1bbcc21..39728b6818 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -786,7 +786,24 @@ void LLVOVolume::updateTextureVirtualSize(bool forced) LLFace* face = mDrawable->getFace(i); if (!face) continue; const LLTextureEntry *te = face->getTextureEntry(); - LLViewerTexture *imagep = face->getTexture(); + LLViewerTexture *imagep = nullptr; + U32 ch_min; + U32 ch_max; + if (!te->getGLTFRenderMaterial()) + { + ch_min = LLRender::DIFFUSE_MAP; + ch_max = LLRender::SPECULAR_MAP; + } + else + { + ch_min = LLRender::BASECOLOR_MAP; + ch_max = LLRender::EMISSIVE_MAP; + } + for (U32 ch = ch_min; (!imagep && ch <= ch_max); ++ch) + { + // Get _a_ non-null texture if possible (usually diffuse/basecolor, but could be something else) + imagep = face->getTexture(ch); + } if (!imagep || !te || face->mExtents[0].equals3(face->mExtents[1])) { diff --git a/indra/newview/llwatchdog.cpp b/indra/newview/llwatchdog.cpp index bf171fe954..d9fcd5811d 100644 --- a/indra/newview/llwatchdog.cpp +++ b/indra/newview/llwatchdog.cpp @@ -27,6 +27,7 @@ #include "llviewerprecompiledheaders.h" #include "llwatchdog.h" +#include "llmutex.h" #include "llthread.h" constexpr U32 WATCHDOG_SLEEP_TIME_USEC = 1000000U; diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp index 7962c28e6d..2fd45337fc 100644 --- a/indra/newview/llworldmap.cpp +++ b/indra/newview/llworldmap.cpp @@ -32,6 +32,7 @@ #include "message.h" #include "lltracker.h" #include "lluistring.h" +#include "llviewercontrol.h" #include "llviewertexturelist.h" #include "lltrans.h" #include "llgltexture.h" @@ -117,6 +118,7 @@ LLVector3d LLSimInfo::getGlobalOrigin() const { return from_region_handle(mHandle); } + LLVector3 LLSimInfo::getLocalPos(LLVector3d global_pos) const { LLVector3d sim_origin = from_region_handle(mHandle); @@ -492,9 +494,20 @@ bool LLWorldMap::insertItem(U32 x_world, U32 y_world, std::string& name, LLUUID& case MAP_ITEM_MATURE_EVENT: case MAP_ITEM_ADULT_EVENT: { - std::string timeStr = "["+ LLTrans::getString ("TimeHour")+"]:[" - +LLTrans::getString ("TimeMin")+"] [" - +LLTrans::getString ("TimeAMPM")+"]"; + std::string timeStr; + + static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + if (use_24h) + { + std::string timeStr = "[" + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + } + else + { + std::string timeStr = "[" + LLTrans::getString("TimeHour12") + "]:[" + + LLTrans::getString("TimeMin") + "] [" + + LLTrans::getString("TimeAMPM") + "]"; + } LLSD substitution; substitution["datetime"] = (S32) extra; LLStringUtil::format (timeStr, substitution); diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index 6b2bd3e6fb..1be6a6cfff 100755 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -517,26 +517,38 @@ void LLWorldMapView::draw() // Draw the region name in the lower left corner if (mMapScale >= DRAW_TEXT_THRESHOLD) { - std::string mesg; + static LLCachedControl<bool> print_coords(gSavedSettings, "MapShowGridCoords"); + static LLFontGL* font = LLFontGL::getFontSansSerifSmallBold(); + + auto print = [&](std::string text, F32 x, F32 y, bool use_ellipses) + { + font->renderUTF8(text, 0, + (F32)llfloor(left + x), (F32)llfloor(bottom + y), + LLColor4::white, + LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW, + S32_MAX, //max_chars + (S32)mMapScale, //max_pixels + NULL, + use_ellipses); + }; + + std::string grid_name = info->getName(); if (info->isDown()) { - mesg = llformat( "%s (%s)", info->getName().c_str(), sStringsMap["offline"].c_str()); + grid_name += " (" + sStringsMap["offline"] + ")"; } - else + + if (print_coords) { - mesg = info->getName(); + print(grid_name, 3, 14, true); + // Obtain and print the grid map coordinates + LLVector3d region_pos = info->getGlobalOrigin(); + std::string grid_coords = llformat("[%.0f, %.0f]", region_pos[VX] / 256, region_pos[VY] / 256); + print(grid_coords, 3, 2, false); } - if (!mesg.empty()) + else { - LLFontGL::getFontSansSerifSmallBold()->renderUTF8( - mesg, 0, - (F32)llfloor(left + 3), (F32)llfloor(bottom + 2), - LLColor4::white, - LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::DROP_SHADOW, - S32_MAX, //max_chars - (S32)mMapScale, //max_pixels - NULL, - /*use_ellipses*/true); + print(grid_name, 3, 2, true); } } } diff --git a/indra/newview/llworldmipmap.cpp b/indra/newview/llworldmipmap.cpp index d8ea2b884f..d198f7a33b 100644 --- a/indra/newview/llworldmipmap.cpp +++ b/indra/newview/llworldmipmap.cpp @@ -30,7 +30,7 @@ #include "llviewercontrol.h" // LLControlGroup #include "llviewertexturelist.h" -#include "math.h" // log() +#include <cmath> // log() // Turn this on to output tile stats in the standard output #define DEBUG_TILES_STAT 0 diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 0f956d8350..5b24d46bb0 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -313,6 +313,8 @@ bool LLXMLRPCTransaction::Impl::process() if (mHasResponse && !mResponseParsed) { LLXMLNodePtr root; + bool strip_escaped_strings = LLXMLNode::sStripEscapedStrings; + LLXMLNode::sStripEscapedStrings = false; if (!LLXMLNode::parseBuffer(mResponseText.data(), mResponseText.size(), root, nullptr)) { @@ -329,6 +331,7 @@ bool LLXMLRPCTransaction::Impl::process() LL_WARNS() << "XMLRPC response parsing failed; request URI: " << mURI << LL_ENDL; } + LLXMLNode::sStripEscapedStrings = strip_escaped_strings; mResponseParsed = true; } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 84f026699e..0b4b98d674 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -11248,21 +11248,24 @@ public: } }; - +// Called from LLViewHighlightTransparent when "Highlight Transparent" is toggled void LLPipeline::rebuildDrawInfo() { - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) + const U32 types_to_traverse[] = { - LLViewerRegion* region = *iter; - - LLOctreeDirty dirty; - - LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_VOLUME); - dirty.traverse(part->mOctree); + LLViewerRegion::PARTITION_VOLUME, + LLViewerRegion::PARTITION_BRIDGE, + LLViewerRegion::PARTITION_AVATAR + }; - part = region->getSpatialPartition(LLViewerRegion::PARTITION_BRIDGE); - dirty.traverse(part->mOctree); + LLOctreeDirty dirty; + for (LLViewerRegion* region : LLWorld::getInstance()->getRegionList()) + { + for (U32 type : types_to_traverse) + { + LLSpatialPartition* part = region->getSpatialPartition(type); + dirty.traverse(part->mOctree); + } } } diff --git a/indra/newview/rlvactions.cpp b/indra/newview/rlvactions.cpp new file mode 100644 index 0000000000..110beeafc0 --- /dev/null +++ b/indra/newview/rlvactions.cpp @@ -0,0 +1,42 @@ +/** + * @file rlvactions.cpp + * @author Kitty Barnett + * @brief RLVa public facing helper class to easily make RLV checks + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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 "rlvactions.h" +#include "rlvhandler.h" + +// ============================================================================ +// Helper functions +// + +bool RlvActions::isRlvEnabled() +{ + return RlvHandler::isEnabled(); +} + +// ============================================================================ diff --git a/indra/newview/rlvactions.h b/indra/newview/rlvactions.h new file mode 100644 index 0000000000..cb0df95e37 --- /dev/null +++ b/indra/newview/rlvactions.h @@ -0,0 +1,46 @@ +/** + * @file rlvactions.h + * @author Kitty Barnett + * @brief RLVa public facing helper class to easily make RLV checks + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#pragma once + +// ============================================================================ +// RlvActions - developer-friendly non-RLVa code facing class, use in lieu of RlvHandler whenever possible +// + +class RlvActions +{ + // ================ + // Helper functions + // ================ +public: + /* + * Convenience function to check if RLVa is enabled without having to include rlvhandler.h + */ + static bool isRlvEnabled(); +}; + +// ============================================================================ diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp new file mode 100644 index 0000000000..4140659715 --- /dev/null +++ b/indra/newview/rlvcommon.cpp @@ -0,0 +1,134 @@ +/** + * @file rlvcommon.h + * @author Kitty Barnett + * @brief RLVa helper functions and constants used throughout the viewer + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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 "llagent.h" +#include "llchat.h" +#include "lldbstrings.h" +#include "llversioninfo.h" +#include "llviewermenu.h" +#include "llviewerstats.h" +#include "message.h" +#include <boost/algorithm/string.hpp> + +#include "rlvcommon.h" + +#include "llviewercontrol.h" +#include "rlvhandler.h" + +using namespace Rlv; + +// ============================================================================ +// RlvStrings +// + +std::string Strings::getVersion(bool wants_legacy) +{ + return llformat("%s viewer v%d.%d.%d (RLVa %d.%d.%d)", + !wants_legacy ? "RestrainedLove" : "RestrainedLife", + SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, + ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch); +} + +std::string Strings::getVersionAbout() +{ + return llformat("RLV v%d.%d.%d / RLVa v%d.%d.%d.%d", + SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, + ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch, LLVersionInfo::instance().getBuild()); +} + +std::string Strings::getVersionNum() +{ + return llformat("%d%02d%02d%02d", + SpecVersion::Major, SpecVersion::Minor, SpecVersion::Patch, SpecVersion::Build); +} + +std::string Strings::getVersionImplNum() +{ + return llformat("%d%02d%02d%02d", + ImplVersion::Major, ImplVersion::Minor, ImplVersion::Patch, ImplVersion::ImplId); +} + +// ============================================================================ +// RlvUtil +// + +void Util::menuToggleVisible() +{ + bool isTopLevel = gSavedSettings.getBOOL(Settings::TopLevelMenu); + bool isRlvEnabled = RlvHandler::isEnabled(); + + LLMenuGL* menuRLVaMain = gMenuBarView->findChildMenuByName("RLVa Main", false); + LLMenuGL* menuAdvanced = gMenuBarView->findChildMenuByName("Advanced", false); + LLMenuGL* menuRLVaEmbed= menuAdvanced->findChildMenuByName("RLVa Embedded", false); + + gMenuBarView->setItemVisible("RLVa Main", isRlvEnabled && isTopLevel); + menuAdvanced->setItemVisible("RLVa Embedded", isRlvEnabled && !isTopLevel); + + if ( isRlvEnabled && menuRLVaMain && menuRLVaEmbed && + ( (isTopLevel && 1 == menuRLVaMain->getItemCount()) || (!isTopLevel && 1 == menuRLVaEmbed->getItemCount())) ) + { + LLMenuGL* menuFrom = isTopLevel ? menuRLVaEmbed : menuRLVaMain; + LLMenuGL* menuTo = isTopLevel ? menuRLVaMain : menuRLVaEmbed; + while (LLMenuItemGL* pItem = menuFrom->getItem(1)) + { + menuFrom->removeChild(pItem); + menuTo->addChild(pItem); + pItem->updateBranchParent(menuTo); + } + } +} + +bool Util::parseStringList(const std::string& strInput, std::vector<std::string>& optionList, std::string_view strSeparator) +{ + if (!strInput.empty()) + boost::split(optionList, strInput, boost::is_any_of(strSeparator)); + return !optionList.empty(); +} + +bool Util::sendChatReply(S32 nChannel, const std::string& strUTF8Text) +{ + if (!isValidReplyChannel(nChannel)) + return false; + + // Copy/paste from send_chat_from_viewer() + gMessageSystem->newMessageFast(_PREHASH_ChatFromViewer); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + gMessageSystem->nextBlockFast(_PREHASH_ChatData); + gMessageSystem->addStringFast(_PREHASH_Message, utf8str_truncate(strUTF8Text, MAX_MSG_STR_LEN)); + gMessageSystem->addU8Fast(_PREHASH_Type, CHAT_TYPE_SHOUT); + gMessageSystem->addS32("Channel", nChannel); + gAgent.sendReliableMessage(); + add(LLStatViewer::CHAT_COUNT, 1); + + return true; +} + +// ============================================================================ diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h new file mode 100644 index 0000000000..6f1bbbbdc6 --- /dev/null +++ b/indra/newview/rlvcommon.h @@ -0,0 +1,72 @@ +/** + * @file rlvcommon.h + * @author Kitty Barnett + * @brief RLVa helper functions and constants used throughout the viewer + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#pragma once + +#include "rlvdefines.h" + +namespace Rlv +{ + // ============================================================================ + // RlvStrings + // + + class Strings + { + public: + static std::string getVersion(bool wants_legacy); + static std::string getVersionAbout(); + static std::string getVersionImplNum(); + static std::string getVersionNum(); + }; + + // ============================================================================ + // RlvUtil + // + + namespace Util + { + bool isValidReplyChannel(S32 nChannel, bool isLoopback = false); + void menuToggleVisible(); + bool parseStringList(const std::string& strInput, std::vector<std::string>& optionList, std::string_view strSeparator = Constants::OptionSeparator); + bool sendChatReply(S32 nChannel, const std::string& strUTF8Text); + bool sendChatReply(const std::string& strChannel, const std::string& strUTF8Text); + }; + + inline bool Util::isValidReplyChannel(S32 nChannel, bool isLoopback) + { + return (nChannel > (!isLoopback ? 0 : -1)) && (CHAT_CHANNEL_DEBUG != nChannel); + } + + inline bool Util::sendChatReply(const std::string& strChannel, const std::string& strUTF8Text) + { + S32 nChannel; + return LLStringUtil::convertToS32(strChannel, nChannel) ? sendChatReply(nChannel, strUTF8Text) : false; + } + + // ============================================================================ +} diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h new file mode 100644 index 0000000000..e39328fdd6 --- /dev/null +++ b/indra/newview/rlvdefines.h @@ -0,0 +1,194 @@ +/** + * @file rlvdefines.h + * @author Kitty Barnett + * @brief RLVa common defines, constants and enums + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#pragma once + +// ============================================================================ +// Defines +// + +// Defining these makes it easier if we ever need to change our tag +#define RLV_WARNS LL_WARNS("RLV") +#define RLV_INFOS LL_INFOS("RLV") +#define RLV_DEBUGS LL_DEBUGS("RLV") +#define RLV_ENDL LL_ENDL +#define RLV_VERIFY(f) (f) + +#if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG + // Make sure we halt execution on errors + #define RLV_ERRS LL_ERRS("RLV") + // Keep our asserts separate from LL's + #define RLV_ASSERT(f) if (!(f)) { RLV_ERRS << "ASSERT (" << #f << ")" << RLV_ENDL; } + #define RLV_ASSERT_DBG(f) RLV_ASSERT(f) +#else + // Don't halt execution on errors in release + #define RLV_ERRS LL_WARNS("RLV") + // We don't want to check assertions in release builds + #ifdef RLV_DEBUG + #define RLV_ASSERT(f) RLV_VERIFY(f) + #define RLV_ASSERT_DBG(f) + #else + #define RLV_ASSERT(f) + #define RLV_ASSERT_DBG(f) + #endif // RLV_DEBUG +#endif // LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG + +namespace Rlv +{ + // Version of the specification we report + namespace SpecVersion { + constexpr S32 Major = 4; + constexpr S32 Minor = 0; + constexpr S32 Patch = 0; + constexpr S32 Build = 0; + }; + + // RLVa implementation version + namespace ImplVersion { + constexpr S32 Major = 3; + constexpr S32 Minor = 0; + constexpr S32 Patch = 0; + constexpr S32 ImplId = 13; + }; + + namespace Constants + { + constexpr char CmdPrefix = '@'; + constexpr char ConsolePrompt[] = "> "; + constexpr std::string_view OptionSeparator = ";"; + } +} + +// ============================================================================ +// Enumeration declarations +// + +namespace Rlv +{ + enum class EBehaviour { + Version = 0, + VersionNew, + VersionNum, + GetCommand, + + Count, + Unknown, + }; + + enum class EBehaviourOptionType + { + EmptyOrException, // Behaviour takes no parameters + Exception, // Behaviour requires an exception as a parameter + NoneOrException, // Behaviour takes either no parameters or an exception + }; + + enum class EParamType { + Unknown = 0x00, + Add = 0x01, // <param> == "n"|"add" + Remove = 0x02, // <param> == "y"|"rem" + Force = 0x04, // <param> == "force" + Reply = 0x08, // <param> == <number> + Clear = 0x10, + AddRem = Add | Remove + }; + + enum class ECmdRet { + Unknown = 0x0000, // Unknown error (should only be used internally) + Retained, // Command was retained + Succeeded = 0x0100, // Command executed successfully + SuccessUnset, // Command executed successfully (RLV_TYPE_REMOVE for an unrestricted behaviour) + SuccessDuplicate, // Command executed successfully (RLV_TYPE_ADD for an already restricted behaviour) + SuccessDeprecated, // Command executed successfully but has been marked as deprecated + SuccessDelayed, // Command parsed valid but will execute at a later time + Failed = 0x0200, // Command failed (general failure) + FailedSyntax, // Command failed (syntax error) + FailedOption, // Command failed (invalid option) + FailedParam, // Command failed (invalid param) + FailedLock, // Command failed (command is locked by another object) + FailedDisabled, // Command failed (command disabled by user) + FailedUnknown, // Command failed (unknown command) + FailedNoSharedRoot, // Command failed (missing #RLV) + FailedDeprecated, // Command failed (deprecated and no longer supported) + FailedNoBehaviour, // Command failed (force modifier on an object with no active restrictions) + FailedUnheldBehaviour, // Command failed (local modifier on an object that doesn't hold the base behaviour) + FailedBlocked, // Command failed (object is blocked) + FailedThrottled, // Command failed (throttled) + FailedNoProcessor // Command doesn't have a template processor define (legacy code) + }; + + enum class EExceptionCheck + { + Permissive, // Exception can be set by any object + Strict, // Exception must be set by all objects holding the restriction + Default, // Permissive or strict will be determined by currently enforced restrictions + }; + + // Replace&remove in c++23 + template <typename E> + constexpr std::enable_if_t<std::is_enum_v<E> && !std::is_convertible_v<E, int>, std::underlying_type_t<E>> to_underlying(E e) noexcept + { + return static_cast<std::underlying_type_t<E>>(e); + } + + template <typename E> + constexpr std::enable_if_t<std::is_enum_v<E> && !std::is_convertible_v<E, int>, bool> has_flag(E value, E flag) noexcept + { + return (to_underlying(value) & to_underlying(flag)) != 0; + } + + constexpr bool isReturnCodeSuccess(ECmdRet eRet) + { + return (to_underlying(eRet) & to_underlying(ECmdRet::Succeeded)) == to_underlying(ECmdRet::Succeeded); + } + + constexpr bool isReturnCodeFailed(ECmdRet eRet) + { + return (to_underlying(eRet) & to_underlying(ECmdRet::Failed)) == to_underlying(ECmdRet::Failed); + } +} + +// ============================================================================ +// Settings +// + +namespace Rlv +{ + namespace Settings + { + constexpr char Main[] = "RestrainedLove"; + constexpr char Debug[] = "RestrainedLoveDebug"; + + constexpr char DebugHideUnsetDup[] = "RLVaDebugHideUnsetDuplicate"; + constexpr char EnableExperimentalCommands[] = "RLVaExperimentalCommands"; + constexpr char EnableIMQuery[] = "RLVaEnableIMQuery"; + constexpr char EnableTempAttach[] = "RLVaEnableTemporaryAttachments"; + constexpr char TopLevelMenu[] = "RLVaTopLevelMenu"; + }; + +}; + +// ============================================================================ diff --git a/indra/newview/rlvfloaters.cpp b/indra/newview/rlvfloaters.cpp new file mode 100644 index 0000000000..8a074fd14d --- /dev/null +++ b/indra/newview/rlvfloaters.cpp @@ -0,0 +1,122 @@ +/** + * @file rlvfloaters.cpp + * @author Kitty Barnett + * @brief RLVa floaters class implementations + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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 "llagentdata.h" +#include "llchatentry.h" +#include "lltexteditor.h" +#include "lltrans.h" +#include "llvoavatarself.h" + +#include "rlvfloaters.h" +#include "rlvhandler.h" + +using namespace Rlv; + +// ============================================================================ +// FloaterConsole +// + +bool FloaterConsole::postBuild() +{ + mInputEdit = getChild<LLChatEntry>("console_input"); + mInputEdit->setCommitCallback(std::bind(&FloaterConsole::onInput, this)); + mInputEdit->setTextExpandedCallback(std::bind(&FloaterConsole::reshapeLayoutPanel, this)); + mInputEdit->setFocus(true); + mInputEdit->setCommitOnFocusLost(false); + + mInputPanel = getChild<LLLayoutPanel>("input_panel"); + mInputEditPad = mInputPanel->getRect().getHeight() - mInputEdit->getRect().getHeight(); + + mOutputText = getChild<LLTextEditor>("console_output"); + mOutputText->appendText(Constants::ConsolePrompt, false); + + if (RlvHandler::isEnabled()) + { + mCommandOutputConn = RlvHandler::instance().setCommandOutputCallback([this](const RlvCommand& rlvCmd, S32, const std::string strText) + { + if (rlvCmd.getObjectID() == gAgentID) + { + mOutputText->appendText(rlvCmd.getBehaviour() + ": ", true); + mOutputText->appendText(strText, false); + } + }); + } + + return true; +} + +void FloaterConsole::onClose(bool fQuitting) +{ + if (RlvHandler::isEnabled()) + { + RlvHandler::instance().processCommand(gAgentID, "clear", true); + } +} + +void FloaterConsole::onInput() +{ + if (!isAgentAvatarValid()) + { + return; + } + + std::string strText = mInputEdit->getText(); + LLStringUtil::trim(strText); + + mOutputText->appendText(strText, false); + mInputEdit->setText(LLStringUtil::null); + + if (!RlvHandler::isEnabled()) + { + mOutputText->appendText(LLTrans::getString("RlvConsoleDisable"), true); + } + else if (strText.length() <= 3 || Constants::CmdPrefix != strText[0]) + { + mOutputText->appendText(LLTrans::getString("RlvConsoleInvalidCmd"), true); + } + else + { + LLChat chat; + chat.mFromID = gAgentID; + chat.mChatType = CHAT_TYPE_OWNER; + + RlvHandler::instance().handleSimulatorChat(strText, chat, gAgentAvatarp); + + mOutputText->appendText(strText, true); + } + + mOutputText->appendText(Constants::ConsolePrompt, true); +} + +void FloaterConsole::reshapeLayoutPanel() +{ + mInputPanel->reshape(mInputPanel->getRect().getWidth(), mInputEdit->getRect().getHeight() + mInputEditPad, false); +} + +// ============================================================================ diff --git a/indra/newview/rlvfloaters.h b/indra/newview/rlvfloaters.h new file mode 100644 index 0000000000..8acfa43f28 --- /dev/null +++ b/indra/newview/rlvfloaters.h @@ -0,0 +1,68 @@ +/** + * @file rlvfloaters.h + * @author Kitty Barnett + * @brief RLVa floaters class implementations + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#pragma once + +#include "llfloater.h" + +#include "rlvdefines.h" + +class LLChatEntry; +class LLFloaterReg; +class LLLayoutPanel; +class LLTextEditor; +class RlvCommand; +class RlvHandler; + +namespace Rlv +{ + // ============================================================================ + // FloaterConsole - debug console to allow command execution without the need for a script + // + + class FloaterConsole : public LLFloater + { + friend class ::LLFloaterReg; + FloaterConsole(const LLSD& sdKey) : LLFloater(sdKey) {} + + public: + bool postBuild() override; + void onClose(bool fQuitting) override; + protected: + void onInput(); + void reshapeLayoutPanel(); + + private: + boost::signals2::scoped_connection mCommandOutputConn; + int mInputEditPad = 0; + LLLayoutPanel* mInputPanel = nullptr; + LLChatEntry* mInputEdit = nullptr; + LLTextEditor* mOutputText = nullptr; + }; + + // ============================================================================ +}; diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp new file mode 100644 index 0000000000..6c4b439105 --- /dev/null +++ b/indra/newview/rlvhandler.cpp @@ -0,0 +1,225 @@ +/** + * @file rlvhandler.cpp + * @author Kitty Barnett + * @brief RLVa helper classes for internal use only + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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 "llagent.h" +#include "llstartup.h" +#include "llviewercontrol.h" +#include "llviewerobject.h" + +#include "rlvcommon.h" +#include "rlvhandler.h" +#include "rlvhelper.h" + +#include <boost/algorithm/string.hpp> + +using namespace Rlv; + +// ============================================================================ +// Static variable initialization +// + +bool RlvHandler::mIsEnabled = false; + +// ============================================================================ +// Command processing functions +// + +bool RlvHandler::handleSimulatorChat(std::string& message, const LLChat& chat, const LLViewerObject* chatObj) +{ + // *TODO: There's an edge case for temporary attachments when going from enabled -> disabled with restrictions already in place + static LLCachedControl<bool> enable_temp_attach(gSavedSettings, Settings::EnableTempAttach); + static LLCachedControl<bool> show_debug_output(gSavedSettings, Settings::Debug); + static LLCachedControl<bool> hide_unset_dupes(gSavedSettings, Settings::DebugHideUnsetDup); + + if ( message.length() <= 3 || Constants::CmdPrefix != message[0] || CHAT_TYPE_OWNER != chat.mChatType || + (chatObj && chatObj->isTempAttachment() && !enable_temp_attach()) ) + { + return false; + } + + message.erase(0, 1); + LLStringUtil::toLower(message); + CommandDbgOut cmdDbgOut(message, chatObj->getID() == gAgentID); + + boost_tokenizer tokens(message, boost::char_separator<char>(",", "", boost::drop_empty_tokens)); + for (const std::string& strCmd : tokens) + { + ECmdRet eRet = processCommand(chat.mFromID, strCmd, true); + if ( show_debug_output() && + (!hide_unset_dupes() || (ECmdRet::SuccessUnset != eRet && ECmdRet::SuccessDuplicate != eRet)) ) + { + cmdDbgOut.add(strCmd, eRet); + } + } + + message = cmdDbgOut.get(); + return true; +} + +ECmdRet RlvHandler::processCommand(const LLUUID& idObj, const std::string& strCmd, bool fromObj) +{ + const RlvCommand rlvCmd(idObj, strCmd); + return processCommand(std::ref(rlvCmd), fromObj); +} + +ECmdRet RlvHandler::processCommand(std::reference_wrapper<const RlvCommand> rlvCmd, bool fromObj) +{ + { + const RlvCommand& rlvCmdTmp = rlvCmd; // Reference to the temporary with limited variable scope since we don't want it to leak below + + RLV_DEBUGS << "[" << rlvCmdTmp.getObjectID() << "]: " << rlvCmdTmp.asString() << RLV_ENDL; + if (!rlvCmdTmp.isValid()) + { + RLV_DEBUGS << "\t-> invalid syntax" << RLV_ENDL; + return ECmdRet::FailedSyntax; + } + if (rlvCmdTmp.isBlocked()) + { + RLV_DEBUGS << "\t-> blocked command" << RLV_ENDL; + return ECmdRet::FailedDisabled; + } + } + + ECmdRet eRet = ECmdRet::Unknown; + switch (rlvCmd.get().getParamType()) + { + case EParamType::Reply: + eRet = rlvCmd.get().processCommand(); + break; + case EParamType::Unknown: + default: + eRet = ECmdRet::FailedParam; + break; + } + RLV_ASSERT(ECmdRet::Unknown != eRet); + + RLV_DEBUGS << "\t--> command " << (isReturnCodeSuccess(eRet) ? "succeeded" : "failed") << RLV_ENDL; + + return eRet; +} + +// ============================================================================ +// Initialization helper functions +// + +bool RlvHandler::canEnable() +{ + return LLStartUp::getStartupState() <= STATE_LOGIN_CLEANUP; +} + +bool RlvHandler::setEnabled(bool enable) +{ + if (mIsEnabled == enable) + return enable; + + if (enable && canEnable()) + { + RLV_INFOS << "Enabling Restrained Love API support - " << Strings::getVersionAbout() << RLV_ENDL; + mIsEnabled = true; + } + + return mIsEnabled; +} + +// ============================================================================ +// Command handlers (RLV_TYPE_REPLY) +// + +ECmdRet CommandHandlerBaseImpl<EParamType::Reply>::processCommand(const RlvCommand& rlvCmd, ReplyHandlerFunc* pHandler) +{ + // Sanity check - <param> should specify a - valid - reply channel + S32 nChannel; + if (!LLStringUtil::convertToS32(rlvCmd.getParam(), nChannel) || !Util::isValidReplyChannel(nChannel, rlvCmd.getObjectID() == gAgentID)) + return ECmdRet::FailedParam; + + std::string strReply; + ECmdRet eRet = (*pHandler)(rlvCmd, strReply); + + // If we made it this far then: + // - the command was handled successfully so we send off the response + // - the command failed but we still send off an - empty - response to keep the issuing script from blocking + if (nChannel != 0) + { + Util::sendChatReply(nChannel, strReply); + } + RlvHandler::instance().mOnCommandOutput(rlvCmd, nChannel, strReply); + + return eRet; +} + +// Handles: @getcommand[:<behaviour>[;<type>[;<separator>]]]=<channel> +template<> template<> +ECmdRet ReplyHandler<EBehaviour::GetCommand>::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ + std::vector<std::string> optionList; + Util::parseStringList(rlvCmd.getOption(), optionList); + + // If a second parameter is present it'll specify the command type + EParamType eType = EParamType::Unknown; + if (optionList.size() >= 2) + { + if (optionList[1] == "any" || optionList[1].empty()) + eType = EParamType::Unknown; + else if (optionList[1] == "add") + eType = EParamType::AddRem; + else if (optionList[1] == "force") + eType = EParamType::Force; + else if (optionList[1] == "reply") + eType = EParamType::Reply; + else + return ECmdRet::FailedOption; + } + + std::list<std::string> cmdList; + if (BehaviourDictionary::instance().getCommands(!optionList.empty() ? optionList[0] : LLStringUtil::null, eType, cmdList)) + strReply = boost::algorithm::join(cmdList, optionList.size() >= 3 ? optionList[2] : Constants::OptionSeparator); + return ECmdRet::Succeeded; +} + +// Handles: @version=<chnannel> and @versionnew=<channel> +template<> template<> +ECmdRet VersionReplyHandler::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ + strReply = Strings::getVersion(EBehaviour::Version == rlvCmd.getBehaviourType()); + return ECmdRet::Succeeded; +} + +// Handles: @versionnum[:impl]=<channel> +template<> template<> +ECmdRet ReplyHandler<EBehaviour::VersionNum>::onCommand(const RlvCommand& rlvCmd, std::string& strReply) +{ + if (!rlvCmd.hasOption()) + strReply = Strings::getVersionNum(); + else if ("impl" == rlvCmd.getOption()) + strReply = Strings::getVersionImplNum(); + else + return ECmdRet::FailedOption; + return ECmdRet::Succeeded; +} + +// ============================================================================ diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h new file mode 100644 index 0000000000..38612485b1 --- /dev/null +++ b/indra/newview/rlvhandler.h @@ -0,0 +1,80 @@ +/** + * @file rlvhandler.h + * @author Kitty Barnett + * @brief Primary command process and orchestration class + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#pragma once + +#include "llchat.h" +#include "llsingleton.h" + +#include "rlvhelper.h" + +class LLViewerObject; + +// ============================================================================ +// RlvHandler class +// + +class RlvHandler : public LLSingleton<RlvHandler> +{ + template<Rlv::EParamType> friend struct Rlv::CommandHandlerBaseImpl; + + LLSINGLETON_EMPTY_CTOR(RlvHandler); + + /* + * Command processing + */ +public: + // Command processing helper functions + bool handleSimulatorChat(std::string& message, const LLChat& chat, const LLViewerObject* chatObj); + Rlv::ECmdRet processCommand(const LLUUID& idObj, const std::string& stCmd, bool fromObj); +protected: + Rlv::ECmdRet processCommand(std::reference_wrapper<const RlvCommand> rlvCmdRef, bool fromObj); + + /* + * Helper functions + */ +public: + // Initialization (deliberately static so they can safely be called in tight loops) + static bool canEnable(); + static bool isEnabled() { return mIsEnabled; } + static bool setEnabled(bool enable); + + /* + * Event handling + */ +public: + // The command output signal is triggered whenever a command produces channel or debug output + using command_output_signal_t = boost::signals2::signal<void (const RlvCommand&, S32, const std::string&)>; + boost::signals2::connection setCommandOutputCallback(const command_output_signal_t::slot_type& cb) { return mOnCommandOutput.connect(cb); } + +protected: + command_output_signal_t mOnCommandOutput; +private: + static bool mIsEnabled; +}; + +// ============================================================================ diff --git a/indra/newview/rlvhelper.cpp b/indra/newview/rlvhelper.cpp new file mode 100644 index 0000000000..1dac297bf1 --- /dev/null +++ b/indra/newview/rlvhelper.cpp @@ -0,0 +1,389 @@ +/** + * @file rlvhelper.cpp + * @author Kitty Barnett + * @brief RLVa helper classes for internal use only + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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 "lltrans.h" +#include "llviewercontrol.h" + +#include "rlvhelper.h" + +#include <boost/algorithm/string.hpp> + +using namespace Rlv; + +// ============================================================================ +// BehaviourDictionary +// + +BehaviourDictionary::BehaviourDictionary() +{ + // + // Restrictions + // + + // + // Reply-only + // + addEntry(new ReplyProcessor<EBehaviour::GetCommand>("getcommand")); + addEntry(new ReplyProcessor<EBehaviour::Version, VersionReplyHandler>("version")); + addEntry(new ReplyProcessor<EBehaviour::VersionNew, VersionReplyHandler>("versionnew")); + addEntry(new ReplyProcessor<EBehaviour::VersionNum>("versionnum")); + + // Populate mString2InfoMap (the tuple <behaviour, type> should be unique) + for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) + { + mString2InfoMap.insert(std::make_pair(std::make_pair(bhvr_info_p->getBehaviour(), static_cast<EParamType>(bhvr_info_p->getParamTypeMask())), bhvr_info_p)); + } + + // Populate m_Bhvr2InfoMap (there can be multiple entries per ERlvBehaviour) + for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) + { + if ((bhvr_info_p->getParamTypeMask() & to_underlying(EParamType::AddRem)) && !bhvr_info_p->isSynonym()) + { +#ifdef RLV_DEBUG + for (const auto& itBhvr : boost::make_iterator_range(mBhvr2InfoMap.lower_bound(bhvr_info_p->getBehaviourType()), mBhvr2InfoMap.upper_bound(bhvr_info_p->getBehaviourType()))) + { + RLV_ASSERT((itBhvr.first != bhvr_info_p->getBehaviourType()) || (itBhvr.second->getBehaviourFlags() != bhvr_info_p->getBehaviourFlags())); + } +#endif // RLV_DEBUG + mBhvr2InfoMap.insert(std::pair(bhvr_info_p->getBehaviourType(), bhvr_info_p)); + } + } +} + +BehaviourDictionary::~BehaviourDictionary() +{ + for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) + { + delete bhvr_info_p; + } + mBhvrInfoList.clear(); +} + +void BehaviourDictionary::addEntry(const BehaviourInfo* entry_p) +{ + // Filter experimental commands (if disabled) + static LLCachedControl<bool> sEnableExperimental(gSavedSettings, Settings::EnableExperimentalCommands); + if (!entry_p || (!sEnableExperimental && entry_p->isExperimental())) + { + return; + } + + // Sanity check for duplicate entries +#if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG + std::for_each(mBhvrInfoList.begin(), mBhvrInfoList.end(), + [&entry_p](const BehaviourInfo* bhvr_info_p) { + RLV_ASSERT_DBG((bhvr_info_p->getBehaviour() != entry_p->getBehaviour()) || ((bhvr_info_p->getParamTypeMask() & entry_p->getParamTypeMask()) == 0)); + }); +#endif // LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG + + mBhvrInfoList.push_back(entry_p); +} + +const BehaviourInfo* BehaviourDictionary::getBehaviourInfo(EBehaviour eBhvr, EParamType eParamType) const +{ + const BehaviourInfo* bhvr_info_p = nullptr; + for (auto itBhvrLower = mBhvr2InfoMap.lower_bound(eBhvr), itBhvrUpper = mBhvr2InfoMap.upper_bound(eBhvr); + std::find_if(itBhvrLower, itBhvrUpper, [eParamType](const auto& bhvrEntry) { return bhvrEntry.second->getParamTypeMask() == to_underlying(eParamType); }) != itBhvrUpper; + ++itBhvrLower) + { + if (bhvr_info_p) + return nullptr; + bhvr_info_p = itBhvrLower->second; + } + return bhvr_info_p; +} + +const BehaviourInfo* BehaviourDictionary::getBehaviourInfo(const std::string& strBhvr, EParamType eParamType, bool* is_strict_p) const +{ + size_t idxBhvrLastPart = strBhvr.find_last_of('_'); + std::string strBhvrLastPart((std::string::npos != idxBhvrLastPart) && (idxBhvrLastPart < strBhvr.size()) ? strBhvr.substr(idxBhvrLastPart + 1) : LLStringUtil::null); + + bool isStrict = (strBhvrLastPart.compare("sec") == 0); + if (is_strict_p) + *is_strict_p = isStrict; + + auto itBhvr = mString2InfoMap.find(std::make_pair((!isStrict) ? strBhvr : strBhvr.substr(0, strBhvr.size() - 4), (has_flag(eParamType, EParamType::AddRem)) ? EParamType::AddRem : eParamType)); + if ((mString2InfoMap.end() == itBhvr) && (!isStrict) && (!strBhvrLastPart.empty()) && (EParamType::Force == eParamType)) + { + // No match found but it could still be a local scope modifier + auto itBhvrMod = mString2InfoMap.find(std::make_pair(strBhvr.substr(0, idxBhvrLastPart), EParamType::AddRem)); + } + + return ((itBhvr != mString2InfoMap.end()) && ((!isStrict) || (itBhvr->second->hasStrict()))) ? itBhvr->second : nullptr; +} + +EBehaviour BehaviourDictionary::getBehaviourFromString(const std::string& strBhvr, EParamType eParamType, bool* pisStrict) const +{ + const BehaviourInfo* bhvr_info_p = getBehaviourInfo(strBhvr, eParamType, pisStrict); + // Filter out locally scoped modifier commands since they don't actually have a unique behaviour value of their own + return bhvr_info_p->getBehaviourType(); +} + +bool BehaviourDictionary::getCommands(const std::string& strMatch, EParamType eParamType, std::list<std::string>& cmdList) const +{ + cmdList.clear(); + for (const BehaviourInfo* bhvr_info_p : mBhvrInfoList) + { + if ((bhvr_info_p->getParamTypeMask() & to_underlying(eParamType)) || (EParamType::Unknown == eParamType)) + { + std::string strCmd = bhvr_info_p->getBehaviour(); + if ((std::string::npos != strCmd.find(strMatch)) || (strMatch.empty())) + cmdList.push_back(strCmd); + if ((bhvr_info_p->hasStrict()) && ((std::string::npos != strCmd.append("_sec").find(strMatch)) || (strMatch.empty()))) + cmdList.push_back(strCmd); + } + } + return !cmdList.empty(); +} + +bool BehaviourDictionary::getHasStrict(EBehaviour eBhvr) const +{ + for (const auto& itBhvr : boost::make_iterator_range(mBhvr2InfoMap.lower_bound(eBhvr), mBhvr2InfoMap.upper_bound(eBhvr))) + { + // Only restrictions can be strict + if (to_underlying(EParamType::AddRem) != itBhvr.second->getParamTypeMask()) + continue; + return itBhvr.second->hasStrict(); + } + RLV_ASSERT(false); + return false; +} + +void BehaviourDictionary::toggleBehaviourFlag(const std::string& strBhvr, EParamType eParamType, BehaviourInfo::EBehaviourFlags eBhvrFlag, bool fEnable) +{ + auto itBhvr = mString2InfoMap.find(std::make_pair(strBhvr, (has_flag(eParamType, EParamType::AddRem)) ? EParamType::AddRem : eParamType)); + if (mString2InfoMap.end() != itBhvr) + { + const_cast<BehaviourInfo*>(itBhvr->second)->toggleBehaviourFlag(eBhvrFlag, fEnable); + } +} + +// ============================================================================ +// RlvCommmand +// + +RlvCommand::RlvCommand(const LLUUID& idObj, const std::string& strCmd) + : mObjId(idObj) +{ + if (parseCommand(strCmd, mBehaviour, mOption, mParam)) + { + if ("n" == mParam || "add" == mParam) + mParamType = EParamType::Add; + else if ("y" == mParam || "rem" == mParam) + mParamType = EParamType::Remove; + else if ("clear" == mBehaviour) // clear is the odd one out so just make it its own type + mParamType = EParamType::Clear; + else if ("force" == mParam) + mParamType = EParamType::Force; + else if (S32 nTemp; LLStringUtil::convertToS32(mParam, nTemp)) // Assume it's a reply command if we can convert <param> to an S32 + mParamType = EParamType::Reply; + } + + mIsValid = mParamType != EParamType::Unknown; + if (!mIsValid) + { + mOption.clear(); + mParam.clear(); + return; + } + + mBhvrInfo = BehaviourDictionary::instance().getBehaviourInfo(mBehaviour, mParamType, &mIsStrict); +} + +RlvCommand::RlvCommand(const RlvCommand& rlvCmd, EParamType eParamType) + : mIsValid(rlvCmd.mIsValid), mObjId(rlvCmd.mObjId), mBehaviour(rlvCmd.mBehaviour), mBhvrInfo(rlvCmd.mBhvrInfo) + , mParamType( (EParamType::Unknown == eParamType) ? rlvCmd.mParamType : eParamType) + , mIsStrict(rlvCmd.mIsStrict), mOption(rlvCmd.mOption), mParam(rlvCmd.mParam), mIsRefCounted(rlvCmd.mIsRefCounted) +{ +} + +bool RlvCommand::parseCommand(const std::string& strCmd, std::string& strBhvr, std::string& strOption, std::string& strParam) +{ + // Format: <behaviour>[:<option>]=<param> + const size_t idxOption = strCmd.find(':'); + const size_t idxParam = strCmd.find('='); + + // If <behaviour> is missing it's always an improperly formatted command + // If there's an option, but it comes after <param> it's also invalid + if ( (idxOption == 0 || idxParam == 0) || + (idxOption != std::string::npos && idxOption >= idxParam) ) + { + return false; + } + + strBhvr = strCmd.substr(0, std::string::npos != idxOption ? idxOption : idxParam); + strOption = strParam = ""; + + // If <param> is missing it's an improperly formatted command + if (idxParam == std::string::npos || idxParam + 1 == strCmd.length()) + { + // Unless "<behaviour> == "clear" AND (idxOption == 0)" + // OR <behaviour> == "clear" AND (idxParam != 0) + if (strBhvr == "clear" && (!idxOption || idxParam)) + return true; + return false; + } + + if (idxOption != std::string::npos && idxOption + 1 != idxParam) + strOption = strCmd.substr(idxOption + 1, idxParam - idxOption - 1); + strParam = strCmd.substr(idxParam + 1); + + return true; +} + +std::string RlvCommand::asString() const +{ + // NOTE: @clear=<param> should be represented as clear:<param> + return mParamType != EParamType::Clear + ? getBehaviour() + (!mOption.empty() ? ":" + mOption : "") + : getBehaviour() + (!mParam.empty() ? ":" + mParam : ""); +} + +// ========================================================================= +// Various helper classes/timers/functors +// + +namespace Rlv +{ + // =========================================================================== + // CommandDbgOut + // + + void CommandDbgOut::add(std::string strCmd, ECmdRet eRet) + { + const std::string strSuffix = getReturnCodeString(eRet); + if (!strSuffix.empty()) + strCmd.append(llformat(" (%s)", strSuffix.c_str())); + else if (mForConsole) + return; // Only show console feedback on successful commands when there's an informational notice + + std::string& strResult = mCommandResults[isReturnCodeSuccess(eRet) ? ECmdRet::Succeeded : (ECmdRet::Retained == eRet ? ECmdRet::Retained : ECmdRet::Failed)]; + if (!strResult.empty()) + strResult.append(", "); + strResult.append(strCmd); + } + + std::string CommandDbgOut::get() const { + std::ostringstream result; + + if (1 == mCommandResults.size() && !mForConsole) + { + auto it = mCommandResults.begin(); + result << " " << getDebugVerbFromReturnCode(it->first) << ": @" << it->second; + } + else if (!mCommandResults.empty()) + { + auto appendResult = [&](ECmdRet eRet, const std::string& name) + { + auto it = mCommandResults.find(eRet); + if (it == mCommandResults.end()) return; + if (!mForConsole) result << "\n - "; + result << LLTrans::getString(name) << ": @" << it->second; + }; + if (!mForConsole) + result << ": @" << mOrigCmd; + appendResult(ECmdRet::Succeeded, !mForConsole ? "RlvDebugExecuted" : "RlvConsoleExecuted"); + appendResult(ECmdRet::Failed, !mForConsole ? "RlvDebugFailed" : "RlvConsoleFailed"); + appendResult(ECmdRet::Retained, !mForConsole ? "RlvDebugRetained" : "RlvConsoleRetained"); + } + + return result.str(); + } + + std::string CommandDbgOut::getDebugVerbFromReturnCode(ECmdRet eRet) + { + switch (eRet) + { + case ECmdRet::Succeeded: + return LLTrans::getString("RlvDebugExecuted"); + case ECmdRet::Failed: + return LLTrans::getString("RlvDebugFailed"); + case ECmdRet::Retained: + return LLTrans::getString("RlvDebugRetained"); + default: + RLV_ASSERT(false); + return LLStringUtil::null; + } + } + + std::string CommandDbgOut::getReturnCodeString(ECmdRet eRet) + { + switch (eRet) + { + case ECmdRet::SuccessUnset: + return LLTrans::getString("RlvReturnCodeUnset"); + case ECmdRet::SuccessDuplicate: + return LLTrans::getString("RlvReturnCodeDuplicate"); + case ECmdRet::SuccessDelayed: + return LLTrans::getString("RlvReturnCodeDelayed"); + case ECmdRet::SuccessDeprecated: + return LLTrans::getString("RlvReturnCodeDeprecated"); + case ECmdRet::FailedSyntax: + return LLTrans::getString("RlvReturnCodeSyntax"); + case ECmdRet::FailedOption: + return LLTrans::getString("RlvReturnCodeOption"); + case ECmdRet::FailedParam: + return LLTrans::getString("RlvReturnCodeParam"); + case ECmdRet::FailedLock: + return LLTrans::getString("RlvReturnCodeLock"); + case ECmdRet::FailedDisabled: + return LLTrans::getString("RlvReturnCodeDisabled"); + case ECmdRet::FailedUnknown: + return LLTrans::getString("RlvReturnCodeUnknown"); + case ECmdRet::FailedNoSharedRoot: + return LLTrans::getString("RlvReturnCodeNoSharedRoot"); + case ECmdRet::FailedDeprecated: + return LLTrans::getString("RlvReturnCodeDeprecatedAndDisabled"); + case ECmdRet::FailedNoBehaviour: + return LLTrans::getString("RlvReturnCodeNoBehaviour"); + case ECmdRet::FailedUnheldBehaviour: + return LLTrans::getString("RlvReturnCodeUnheldBehaviour"); + case ECmdRet::FailedBlocked: + return LLTrans::getString("RlvReturnCodeBlocked"); + case ECmdRet::FailedThrottled: + return LLTrans::getString("RlvReturnCodeThrottled"); + case ECmdRet::FailedNoProcessor: + return LLTrans::getString("RlvReturnCodeNoProcessor"); + // The following are identified by the chat verb + case ECmdRet::Retained: + case ECmdRet::Succeeded: + case ECmdRet::Failed: + return LLStringUtil::null; + // The following shouldn't occur + case ECmdRet::Unknown: + default: + RLV_ASSERT(false); + return LLStringUtil::null; + } + } + + // =========================================================================== +} + +// ============================================================================ diff --git a/indra/newview/rlvhelper.h b/indra/newview/rlvhelper.h new file mode 100644 index 0000000000..f241332594 --- /dev/null +++ b/indra/newview/rlvhelper.h @@ -0,0 +1,299 @@ +/** + * @file rlvhelper.h + * @author Kitty Barnett + * @brief RLVa helper classes for internal use only + * + * $LicenseInfo:firstyear=2024&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2024, 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$ + */ + +#pragma once + +#include "rlvdefines.h" + +// ============================================================================ +// Forward declarations +// + +class RlvCommand; + +// ============================================================================ + +namespace Rlv +{ + // ============================================================================ + // BehaviourInfo class - Generic behaviour descriptor (used by restrictions, reply and force commands) + // + + class BehaviourInfo + { + public: + enum EBehaviourFlags : uint32_t + { + // General behaviour flags + Strict = 0x0001, // Behaviour has a "_sec" version + Synonym = 0x0002, // Behaviour is a synonym of another + Extended = 0x0004, // Behaviour is part of the RLVa extended command set + Experimental = 0x0008, // Behaviour is part of the RLVa experimental command set + Blocked = 0x0010, // Behaviour is blocked + Deprecated = 0x0020, // Behaviour is deprecated + MaskGeneral = 0x0FFF, + + // Force-wear specific flags + ForceWear_WearReplace = 0x0001 << 16, + ForceWear_WearAdd = 0x0002 << 16, + ForceWear_WearRemove = 0x0004 << 16, + ForceWear_Node = 0x0010 << 16, + ForceWear_Subtree = 0x0020 << 16, + ForceWear_ContextNone = 0x0100 << 16, + ForceWear_ContextObject = 0x0200 << 16, + MaskForceWear = 0xFFFFu << 16 + }; + + BehaviourInfo(const std::string& strBhvr, EBehaviour eBhvr, EParamType maskParamType, std::underlying_type_t<EBehaviourFlags> nBhvrFlags = 0) + : mBhvr(strBhvr), mBhvrType(eBhvr), mBhvrFlags(nBhvrFlags), mMaskParamType(to_underlying(maskParamType)) {} + virtual ~BehaviourInfo() {} + + const std::string& getBehaviour() const { return mBhvr; } + EBehaviour getBehaviourType() const { return mBhvrType; } + std::underlying_type_t<EBehaviourFlags> getBehaviourFlags() const { return mBhvrFlags; } + std::underlying_type_t<EParamType> getParamTypeMask() const { return mMaskParamType; } + bool hasStrict() const { return mBhvrFlags & Strict; } + bool isBlocked() const { return mBhvrFlags & Blocked; } + bool isExperimental() const { return mBhvrFlags & Experimental; } + bool isExtended() const { return mBhvrFlags & Extended; } + bool isSynonym() const { return mBhvrFlags & Synonym; } + void toggleBehaviourFlag(EBehaviourFlags eBhvrFlag, bool fEnable); + + virtual ECmdRet processCommand(const RlvCommand& rlvCmd) const { return ECmdRet::FailedNoProcessor; } + + protected: + std::string mBhvr; + EBehaviour mBhvrType; + std::underlying_type_t<EBehaviourFlags> mBhvrFlags; + std::underlying_type_t<EParamType> mMaskParamType; + }; + + inline void BehaviourInfo::toggleBehaviourFlag(EBehaviourFlags eBhvrFlag, bool fEnable) + { + if (fEnable) + mBhvrFlags |= eBhvrFlag; + else + mBhvrFlags &= ~eBhvrFlag; + } + + // ============================================================================ + // BehaviourDictionary and related classes + // + + class BehaviourDictionary : public LLSingleton<BehaviourDictionary> + { + LLSINGLETON(BehaviourDictionary); + protected: + ~BehaviourDictionary() override; + public: + void addEntry(const BehaviourInfo* entry_p); + + /* + * General helper functions + */ + public: + EBehaviour getBehaviourFromString(const std::string& strBhvr, EParamType eParamType, bool* is_strict_p = nullptr) const; + const BehaviourInfo* getBehaviourInfo(EBehaviour eBhvr, EParamType eParamType) const; + const BehaviourInfo* getBehaviourInfo(const std::string& strBhvr, EParamType eParamType, bool* is_strict_p = nullptr) const; + bool getCommands(const std::string& strMatch, EParamType eParamType, std::list<std::string>& cmdList) const; + bool getHasStrict(EBehaviour eBhvr) const; + void toggleBehaviourFlag(const std::string& strBhvr, EParamType eParamType, BehaviourInfo::EBehaviourFlags eBvhrFlag, bool fEnable); + + /* + * Member variables + */ + protected: + std::list<const BehaviourInfo*> mBhvrInfoList; + std::map<std::pair<std::string, EParamType>, const BehaviourInfo*> mString2InfoMap; + std::multimap<EBehaviour, const BehaviourInfo*> mBhvr2InfoMap; + }; + + // ============================================================================ + // CommandHandler and related classes + // + + typedef ECmdRet(BhvrHandlerFunc)(const RlvCommand&, bool&); + typedef void(BhvrToggleHandlerFunc)(EBehaviour, bool); + typedef ECmdRet(ForceHandlerFunc)(const RlvCommand&); + typedef ECmdRet(ReplyHandlerFunc)(const RlvCommand&, std::string&); + + // + // CommandHandlerBaseImpl - Base implementation for each command type (the old process(AddRem|Force|Reply)Command functions) + // + template<EParamType paramType> struct CommandHandlerBaseImpl; + template<> struct CommandHandlerBaseImpl<EParamType::AddRem> { static ECmdRet processCommand(const RlvCommand&, BhvrHandlerFunc*, BhvrToggleHandlerFunc* = nullptr); }; + template<> struct CommandHandlerBaseImpl<EParamType::Force> { static ECmdRet processCommand(const RlvCommand&, ForceHandlerFunc*); }; + template<> struct CommandHandlerBaseImpl<EParamType::Reply> { static ECmdRet processCommand(const RlvCommand&, ReplyHandlerFunc*); }; + + // + // CommandHandler - The actual command handler (Note that a handler is more general than a processor; a handler can - for instance - be used by multiple processors) + // + #if LL_WINDOWS + #define RLV_TEMPL_FIX(x) template<x> + #else + #define RLV_TEMPL_FIX(x) template<typename Placeholder = int> + #endif // LL_WINDOWS + + + template <EParamType templParamType, EBehaviour templBhvr> + struct CommandHandler + { + RLV_TEMPL_FIX(typename = typename std::enable_if<templParamType == EParamType::AddRem>::type) static ECmdRet onCommand(const RlvCommand&, bool&); + RLV_TEMPL_FIX(typename = typename std::enable_if<templParamType == EParamType::AddRem>::type) static void onCommandToggle(EBehaviour, bool); + RLV_TEMPL_FIX(typename = typename std::enable_if<templParamType == EParamType::Force>::type) static ECmdRet onCommand(const RlvCommand&); + RLV_TEMPL_FIX(typename = typename std::enable_if<templParamType == EParamType::Reply>::type) static ECmdRet onCommand(const RlvCommand&, std::string&); + }; + + // Aliases to improve readability in definitions + template<EBehaviour templBhvr> using BehaviourHandler = CommandHandler<EParamType::AddRem, templBhvr>; + template<EBehaviour templBhvr> using BehaviourToggleHandler = BehaviourHandler<templBhvr>; + template<EBehaviour templBhvr> using ForceHandler = CommandHandler<EParamType::Force, templBhvr>; + template<EBehaviour templBhvr> using ReplyHandler = CommandHandler<EParamType::Reply, templBhvr>; + + // List of shared handlers + using VersionReplyHandler = ReplyHandler<EBehaviour::Version>; // Shared between @version and @versionnew + + // + // CommandProcessor - Templated glue class that brings BehaviourInfo, CommandHandlerBaseImpl and CommandHandler together + // + template <EParamType templParamType, EBehaviour templBhvr, typename handlerImpl = CommandHandler<templParamType, templBhvr>, typename baseImpl = CommandHandlerBaseImpl<templParamType>> + class CommandProcessor : public BehaviourInfo + { + public: + // Default constructor used by behaviour specializations + RLV_TEMPL_FIX(typename = typename std::enable_if<templBhvr != EBehaviour::Unknown>::type) + CommandProcessor(const std::string& strBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, templBhvr, templParamType, nBhvrFlags) {} + + // Constructor used when we don't want to specialize on behaviour (see BehaviourGenericProcessor) + RLV_TEMPL_FIX(typename = typename std::enable_if<templBhvr == EBehaviour::Unknown>::type) + CommandProcessor(const std::string& strBhvr, EBehaviour eBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, eBhvr, templParamType, nBhvrFlags) {} + + ECmdRet processCommand(const RlvCommand& rlvCmd) const override { return baseImpl::processCommand(rlvCmd, &handlerImpl::onCommand); } + }; + + // Aliases to improve readability in definitions + template<EBehaviour templBhvr, typename handlerImpl = CommandHandler<EParamType::AddRem, templBhvr>> using BehaviourProcessor = CommandProcessor<EParamType::AddRem, templBhvr, handlerImpl>; + template<EBehaviour templBhvr, typename handlerImpl = CommandHandler<EParamType::Force, templBhvr>> using ForceProcessor = CommandProcessor<EParamType::Force, templBhvr, handlerImpl>; + template<EBehaviour templBhvr, typename handlerImpl = CommandHandler<EParamType::Reply, templBhvr>> using ReplyProcessor = CommandProcessor<EParamType::Reply, templBhvr, handlerImpl>; + + // Provides pre-defined generic implementations of basic behaviours (template voodoo - see original commit for something that still made sense) + template<EBehaviourOptionType templOptionType> struct BehaviourGenericHandler { static ECmdRet onCommand(const RlvCommand& rlvCmd, bool& fRefCount); }; + template<EBehaviourOptionType templOptionType> using BehaviourGenericProcessor = BehaviourProcessor<EBehaviour::Unknown, BehaviourGenericHandler<templOptionType>>; + template<EBehaviourOptionType templOptionType> struct ForceGenericHandler { static ECmdRet onCommand(const RlvCommand& rlvCmd); }; + template<EBehaviourOptionType templOptionType> using ForceGenericProcessor = ForceProcessor<EBehaviour::Unknown, ForceGenericHandler<templOptionType>>; + + // ============================================================================ + // BehaviourProcessor and related classes - Handles add/rem comamnds aka "restrictions) + // + + template <EBehaviour eBhvr, typename handlerImpl = BehaviourHandler<eBhvr>, typename toggleHandlerImpl = BehaviourToggleHandler<eBhvr>> + class BehaviourToggleProcessor : public BehaviourInfo + { + public: + BehaviourToggleProcessor(const std::string& strBhvr, U32 nBhvrFlags = 0) : BehaviourInfo(strBhvr, eBhvr, EParamType::AddRem, nBhvrFlags) {} + ECmdRet processCommand(const RlvCommand& rlvCmd) const override { return CommandHandlerBaseImpl<EParamType::AddRem>::processCommand(rlvCmd, &handlerImpl::onCommand, &toggleHandlerImpl::onCommandToggle); } + }; + template <EBehaviour eBhvr, EBehaviourOptionType optionType, typename toggleHandlerImpl = BehaviourToggleHandler<eBhvr>> using RlvBehaviourGenericToggleProcessor = BehaviourToggleProcessor<eBhvr, BehaviourGenericHandler<optionType>, toggleHandlerImpl>; + + // ============================================================================ + // Various helper classes/timers/functors + // + + struct CommandDbgOut + { + CommandDbgOut(const std::string& orig_cmd, bool for_console) : mOrigCmd(orig_cmd), mForConsole(for_console) {} + void add(std::string strCmd, ECmdRet eRet); + std::string get() const; + static std::string getDebugVerbFromReturnCode(ECmdRet eRet); + static std::string getReturnCodeString(ECmdRet eRet); + private: + std::string mOrigCmd; + std::map<ECmdRet, std::string> mCommandResults; + bool mForConsole = false; + }; +} + +// ============================================================================ +// RlvCommand +// + +class RlvCommand +{ +public: + explicit RlvCommand(const LLUUID& idObj, const std::string& strCmd); + RlvCommand(const RlvCommand& rlvCmd, Rlv::EParamType eParamType = Rlv::EParamType::Unknown); + + /* + * Member functions + */ +public: + std::string asString() const; + const std::string& getBehaviour() const { return mBehaviour; } + const Rlv::BehaviourInfo* getBehaviourInfo() const { return mBhvrInfo; } + Rlv::EBehaviour getBehaviourType() const { return (mBhvrInfo) ? mBhvrInfo->getBehaviourType() : Rlv::EBehaviour::Unknown; } + U32 getBehaviourFlags() const { return (mBhvrInfo) ? mBhvrInfo->getBehaviourFlags() : 0; } + const LLUUID& getObjectID() const { return mObjId; } + const std::string& getOption() const { return mOption; } + const std::string& getParam() const { return mParam; } + Rlv::EParamType getParamType() const { return mParamType; } + bool hasOption() const { return !mOption.empty(); } + bool isBlocked() const { return (mBhvrInfo) ? mBhvrInfo->isBlocked() : false; } + bool isRefCounted() const { return mIsRefCounted; } + bool isStrict() const { return mIsStrict; } + bool isValid() const { return mIsValid; } + Rlv::ECmdRet processCommand() const { return (mBhvrInfo) ? mBhvrInfo->processCommand(*this) : Rlv::ECmdRet::FailedNoProcessor; } + +protected: + static bool parseCommand(const std::string& strCommand, std::string& strBehaviour, std::string& strOption, std::string& strParam); + bool markRefCounted() const { return mIsRefCounted = true; } + + /* + * Operators + */ +public: + bool operator ==(const RlvCommand&) const; + + /* + * Member variables + */ +protected: + bool mIsValid = false; + LLUUID mObjId; + std::string mBehaviour; + const Rlv::BehaviourInfo* mBhvrInfo = nullptr; + Rlv::EParamType mParamType = Rlv::EParamType::Unknown; + bool mIsStrict = false; + std::string mOption; + std::string mParam; + mutable bool mIsRefCounted = false; + + friend class RlvHandler; + friend class RlvObject; + template<Rlv::EParamType> friend struct Rlv::CommandHandlerBaseImpl; +}; + +// ============================================================================ diff --git a/indra/newview/scripts/lua/frame_profile.lua b/indra/newview/scripts/lua/frame_profile.lua index 3c6353ff68..4d0874f55e 100644 --- a/indra/newview/scripts/lua/frame_profile.lua +++ b/indra/newview/scripts/lua/frame_profile.lua @@ -4,11 +4,13 @@ LLAgent = require 'LLAgent' startup = require 'startup' Timer = (require 'timers').Timer UI = require 'UI' +teleport_util = require('teleport_util') startup.wait('STATE_STARTED') -- teleport to http://maps.secondlife.com/secondlife/Bug%20Island/220/224/27 print(LLAgent.teleport{regionname='Bug Island', x=220, y=224, z=27}) +--teleport_util.wait() Timer(10, 'wait') LLAgent.setCamera{camera_pos={220, 224, 26}, camera_locked=true, focus_pos ={228, 232, 26}, focus_locked=true} diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua index 4a1132fe7e..6068a916ed 100644 --- a/indra/newview/scripts/lua/require/LLAgent.lua +++ b/indra/newview/scripts/lua/require/LLAgent.lua @@ -1,8 +1,26 @@ local leap = require 'leap' local mapargs = require 'mapargs' +local result_view = require 'result_view' + +local function result(keys) + local result_table = { + result=result_view(keys.result), + -- call result_table:close() to release result sets before garbage + -- collection or script completion + close = function(self) + result_view.close(keys.result[1]) + end + } + -- When the result_table is destroyed, close its result_views. + return LL.setdtor('LLAgent result', result_table, result_table.close) +end local LLAgent = {} +function LLAgent.getID() + return leap.request('LLAgent', {op = 'getID'}).id +end + function LLAgent.getRegionPosition() return leap.request('LLAgent', {op = 'getPosition'}).region end @@ -95,6 +113,33 @@ function LLAgent.requestStand() leap.send('LLAgent', {op = 'requestStand'}) end +-- Get the nearby avatars in a range of provided "dist", +-- if "dist" is not specified, "RenderFarClip" setting is used +-- reply will contain "result" table with following fields: +-- "id", "global_pos", "region_pos", "name", "region_id" +function LLAgent.getNearbyAvatarsList(...) + local args = mapargs('dist', ...) + args.op = 'getNearbyAvatarsList' + return result(leap.request('LLAgent', args)) +end + +-- reply will contain "result" table with following fields: +-- "id", "global_pos", "region_pos", "region_id" +function LLAgent.getNearbyObjectsList(...) + local args = mapargs('dist', ...) + args.op = 'getNearbyObjectsList' + return result(leap.request('LLAgent', args)) +end + +-- Get screen position of your own avatar or any other (if "avatar_id" is specified) +-- reply contains "x", "y" coordinates and "onscreen" flag to indicate if it's actually in within the current window +-- avatar render position is used as the point +function LLAgent.getAgentScreenPos(...) + local args = mapargs('avatar_id', ...) + args.op = 'getAgentScreenPos' + return leap.request('LLAgent', args) +end + -- *************************************************************************** -- Autopilot -- *************************************************************************** diff --git a/indra/newview/scripts/lua/require/UI.lua b/indra/newview/scripts/lua/require/UI.lua index cf2695917e..34f3fb75eb 100644 --- a/indra/newview/scripts/lua/require/UI.lua +++ b/indra/newview/scripts/lua/require/UI.lua @@ -222,8 +222,10 @@ function UI.hideFloater(floater_name) leap.send("LLFloaterReg", {op = "hideInstance", name = floater_name}) end -function UI.toggleFloater(floater_name) - leap.send("LLFloaterReg", {op = "toggleInstance", name = floater_name}) +function UI.toggleFloater(...) + local args = mapargs('name,key', ...) + args.op = 'toggleInstance' + leap.send("LLFloaterReg", args) end function UI.isFloaterVisible(floater_name) diff --git a/indra/newview/scripts/lua/require/teleport_util.lua b/indra/newview/scripts/lua/require/teleport_util.lua new file mode 100644 index 0000000000..8a46768e54 --- /dev/null +++ b/indra/newview/scripts/lua/require/teleport_util.lua @@ -0,0 +1,28 @@ +local leap = require 'leap' + +local teleport_util = {} + +local teleport_pump = 'LLTeleport' +local waitfor = leap.WaitFor(0, teleport_pump) +function waitfor:filter(pump, data) + if pump == self.name then + return data + end +end + +function waitfor:process(data) + teleport_util._success = data.success + leap.WaitFor.process(self, data) +end + +leap.request(leap.cmdpump(), + {op='listen', source=teleport_pump, listener='teleport.lua', tweak=true}) + +function teleport_util.wait() + while teleport_util._success == nil do + local item = waitfor:wait() + end + return teleport_util._success +end + +return teleport_util diff --git a/indra/newview/scripts/lua/test_LLChatListener.lua b/indra/newview/scripts/lua/test_LLChatListener.lua index 0f269b54e6..fa00b048b2 100644 --- a/indra/newview/scripts/lua/test_LLChatListener.lua +++ b/indra/newview/scripts/lua/test_LLChatListener.lua @@ -1,5 +1,6 @@ -local LLListener = require 'LLListener' +local LLAgent = require 'LLAgent' local LLChat = require 'LLChat' +local LLListener = require 'LLListener' local UI = require 'UI' -- Chat listener script allows to use the following commands in Nearby chat: @@ -23,9 +24,13 @@ function openOrEcho(message) end local listener = LLListener(LLChat.nearbyChatPump) +local agent_id = LLAgent.getID() function listener:handleMessages(event_data) - if string.find(event_data.message, '[LUA]') then + -- ignore messages and commands from other avatars + if event_data.from_id ~= agent_id then + return true + elseif string.find(event_data.message, '[LUA]') then return true elseif event_data.message == 'stop' then LLChat.sendNearby('Closing echo script.') diff --git a/indra/newview/scripts/lua/test_animation.lua b/indra/newview/scripts/lua/test_animation.lua index 37e7254a6c..ae5eabe148 100644 --- a/indra/newview/scripts/lua/test_animation.lua +++ b/indra/newview/scripts/lua/test_animation.lua @@ -7,8 +7,8 @@ animations_id = LLInventory.getBasicFolderID('animatn') anims = LLInventory.collectDescendentsIf{folder_id=animations_id, type="animatn"}.items local anim_ids = {} -for key in pairs(anims) do - table.insert(anim_ids, key) +for _, key in pairs(anims) do + table.insert(anim_ids, {id = key.id, name = key.name}) end if #anim_ids == 0 then @@ -16,17 +16,17 @@ if #anim_ids == 0 then else -- Start playing a random animation math.randomseed(os.time()) - local random_id = anim_ids[math.random(#anim_ids)] - local anim_info = LLAgent.getAnimationInfo(random_id) + local random_anim = anim_ids[math.random(#anim_ids)] + local anim_info = LLAgent.getAnimationInfo(random_anim.id) - print("Starting animation locally: " .. anims[random_id].name) - print("Loop: " .. anim_info.is_loop .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration))) - LLAgent.playAnimation{item_id=random_id} + print("Starting animation locally: " .. random_anim.name) + print("Loop: " .. tostring(anim_info.is_loop) .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration))) + LLAgent.playAnimation{item_id=random_anim.id} -- Stop animation after 3 sec if it's looped or longer than 3 sec - if anim_info.is_loop == 1 or anim_info.duration > 3 then + if anim_info.is_loop or anim_info.duration > 3 then LL.sleep(3) print("Stop animation.") - LLAgent.stopAnimation(random_id) + LLAgent.stopAnimation(random_anim.id) end end diff --git a/indra/newview/scripts/lua/test_luafloater_demo.lua b/indra/newview/scripts/lua/test_luafloater_demo.lua index 2158134511..23310c6176 100644 --- a/indra/newview/scripts/lua/test_luafloater_demo.lua +++ b/indra/newview/scripts/lua/test_luafloater_demo.lua @@ -14,7 +14,7 @@ function flt:handleEvents(event_data) end function flt:commit_disable_ctrl(event_data) - self:post({action="set_enabled", ctrl_name="open_btn", value = (1 - event_data.value)}) + self:post({action="set_enabled", ctrl_name="open_btn", value = not event_data.value}) end function flt:commit_title_cmb(event_data) diff --git a/indra/newview/scripts/lua/test_nearby_avatars.lua b/indra/newview/scripts/lua/test_nearby_avatars.lua new file mode 100644 index 0000000000..c7fa686076 --- /dev/null +++ b/indra/newview/scripts/lua/test_nearby_avatars.lua @@ -0,0 +1,25 @@ +inspect = require 'inspect' +LLAgent = require 'LLAgent' + +-- Get the list of nearby avatars and print the info +local nearby_avatars = LLAgent.getNearbyAvatarsList() +if nearby_avatars.result.length == 0 then + print("No avatars nearby") +else + print("Nearby avatars:") + for _, av in pairs(nearby_avatars.result) do + print(av.name ..' ID: ' .. av.id ..' Global pos:' .. inspect(av.global_pos)) + end +end + +-- Get the list of nearby objects in 3m range and print the info +local nearby_objects = LLAgent.getNearbyObjectsList(3) +if nearby_objects.result.length == 0 then + print("No objects nearby") +else + print("Nearby objects:") + local pos={} + for _, obj in pairs(nearby_objects.result) do + print('ID: ' .. obj.id ..' Global pos:' .. inspect(obj.global_pos)) + end +end diff --git a/indra/newview/scripts/lua/test_screen_position.lua b/indra/newview/scripts/lua/test_screen_position.lua new file mode 100644 index 0000000000..94d57339b1 --- /dev/null +++ b/indra/newview/scripts/lua/test_screen_position.lua @@ -0,0 +1,8 @@ +LLAgent = require 'LLAgent' + +local screen_pos = LLAgent.getAgentScreenPos() +if screen_pos.onscreen then + print("Avatar screen coordinates X: " .. screen_pos.x .. " Y: " .. screen_pos.y) +else + print("Avatar is not on the screen") +end diff --git a/indra/newview/scripts/lua/test_toolbars.lua b/indra/newview/scripts/lua/test_toolbars.lua index 7683fca8a3..9cd1043446 100644 --- a/indra/newview/scripts/lua/test_toolbars.lua +++ b/indra/newview/scripts/lua/test_toolbars.lua @@ -20,7 +20,7 @@ if response == 'OK' then UI.removeToolbarBtn(BUTTONS[i]) end end - popup:tip('Toolbars were reshuffled') + UI.popup:tip('Toolbars were reshuffled') else - popup:tip('Canceled') + UI.popup:tip('Canceled') end diff --git a/indra/newview/skins/default/xui/da/sidepanel_item_info.xml b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml index d52845160b..6a2acfedf7 100644 --- a/indra/newview/skins/default/xui/da/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/da/sidepanel_item_info.xml @@ -15,6 +15,9 @@ <panel.string name="acquiredDate"> [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] </panel.string> + <panel.string name="acquiredDateAMPM"> + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + </panel.string> <panel.string name="origin_inventory"> (Beholdning) </panel.string> diff --git a/indra/newview/skins/default/xui/da/strings.xml b/indra/newview/skins/default/xui/da/strings.xml index e4f99d14e9..c4275d43f7 100644 --- a/indra/newview/skins/default/xui/da/strings.xml +++ b/indra/newview/skins/default/xui/da/strings.xml @@ -3723,6 +3723,10 @@ Hvis du bliver ved med at modtage denne besked, kontakt venligst [SUPPORT_SITE]. <string name="conference-title-incoming"> Konference med [AGENT_NAME] </string> + <string name="bot_warning"> +Du chatter med en bot, [NAME]. Del ikke personlige oplysninger. +Læs mere på https://second.life/scripted-agents. + </string> <string name="no_session_message"> (IM session eksisterer ikke) </string> diff --git a/indra/newview/skins/default/xui/de/floater_about_land.xml b/indra/newview/skins/default/xui/de/floater_about_land.xml index bb9ab26ef5..9c93ff38ad 100644 --- a/indra/newview/skins/default/xui/de/floater_about_land.xml +++ b/indra/newview/skins/default/xui/de/floater_about_land.xml @@ -25,7 +25,8 @@ <panel.string name="none_text">(keiner)</panel.string> <panel.string name="sale_pending_text">(Wird verkauft)</panel.string> <panel.string name="no_selection_text">Keine Parzelle ausgewählt.</panel.string> - <panel.string name="time_stamp_template">[wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local]</panel.string> + <panel.string name="time_stamp_template_ampm">[wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,slt]</panel.string> + <panel.string name="time_stamp_template">[wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local]</panel.string> <text name="Name:">Name:</text> <text name="Description:">Beschreibung:</text> <text name="LandType">Typ:</text> diff --git a/indra/newview/skins/default/xui/de/floater_inspect.xml b/indra/newview/skins/default/xui/de/floater_inspect.xml index da97ceb2d8..a193e1123e 100644 --- a/indra/newview/skins/default/xui/de/floater_inspect.xml +++ b/indra/newview/skins/default/xui/de/floater_inspect.xml @@ -1,7 +1,10 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <floater min_width="460" name="inspect" title="OBJEKTE UNTERSUCHEN" width="460"> + <floater.string name="timeStampAMPM"> + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,slt] + </floater.string> <floater.string name="timeStamp"> - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] </floater.string> <scroll_list name="object_list" tool_tip="Wählen Sie ein Objekt aus dieser Liste, um es in der Second Life-Welt zu markieren"> <scroll_list.columns label="Objektname" name="object_name"/> diff --git a/indra/newview/skins/default/xui/de/panel_classified_info.xml b/indra/newview/skins/default/xui/de/panel_classified_info.xml index 007e9d69f0..1b8caf5f78 100644 --- a/indra/newview/skins/default/xui/de/panel_classified_info.xml +++ b/indra/newview/skins/default/xui/de/panel_classified_info.xml @@ -13,7 +13,7 @@ [TELEPORT] teleportieren, [MAP] Karte, [PROFILE] Profil </panel.string> <panel.string name="date_fmt"> - [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] + [day,datetime,slt].[mthnum,datetime,slt].[year,datetime,slt] </panel.string> <panel.string name="auto_renew_on"> Aktiviert diff --git a/indra/newview/skins/default/xui/de/panel_landmark_info.xml b/indra/newview/skins/default/xui/de/panel_landmark_info.xml index 10cf34c170..8726d5e645 100644 --- a/indra/newview/skins/default/xui/de/panel_landmark_info.xml +++ b/indra/newview/skins/default/xui/de/panel_landmark_info.xml @@ -15,8 +15,11 @@ <string name="server_forbidden_text"> Die Informationen über diesen Standort sind zugriffsbeschränkt. Bitte wenden Sie sich bezüglich Ihrer Berechtigungen an den Eigentümer der Parzelle. </string> + <string name="acquired_date_ampm"> + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] + </string> <string name="acquired_date"> - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] </string> <button name="back_btn" tool_tip="Hinten"/> <text name="title" value="Ortsprofil"/> diff --git a/indra/newview/skins/default/xui/de/panel_place_profile.xml b/indra/newview/skins/default/xui/de/panel_place_profile.xml index 4077fdab36..a0b5a1e9dc 100644 --- a/indra/newview/skins/default/xui/de/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/de/panel_place_profile.xml @@ -38,8 +38,11 @@ <string name="server_forbidden_text"> Die Informationen über diesen Standort sind zugriffsbeschränkt. Bitte wenden Sie sich bezüglich Ihrer Berechtigungen an den Eigentümer der Parzelle. </string> + <string name="acquired_date_ampm"> + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] + </string> <string name="acquired_date"> - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] </string> <button name="back_btn" tool_tip="Hinten"/> <text name="title" value="Ortsprofil"/> diff --git a/indra/newview/skins/default/xui/de/panel_profile_classified.xml b/indra/newview/skins/default/xui/de/panel_profile_classified.xml index 5c11a01977..5a4b42870c 100644 --- a/indra/newview/skins/default/xui/de/panel_profile_classified.xml +++ b/indra/newview/skins/default/xui/de/panel_profile_classified.xml @@ -13,7 +13,7 @@ [TELEPORT] Teleportieren, [MAP] Karten, [PROFILE] Profil </panel.string> <panel.string name="date_fmt"> - [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] + [day,datetime,slt].[mthnum,datetime,slt].[year,datetime,slt] </panel.string> <panel.string name="auto_renew_on"> Aktiviert diff --git a/indra/newview/skins/default/xui/de/panel_profile_secondlife.xml b/indra/newview/skins/default/xui/de/panel_profile_secondlife.xml index baaa58e1d7..437cde99fe 100644 --- a/indra/newview/skins/default/xui/de/panel_profile_secondlife.xml +++ b/indra/newview/skins/default/xui/de/panel_profile_secondlife.xml @@ -1,5 +1,11 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel label="Profil" name="panel_profile"> + <string + name="date_format_full" + value="SL-Geburtstag: [day,datetime,utc]. [mth,datetime,utc]. [year,datetime,utc]" /> + <string + name="date_format_short" + value="SL-Geburtstag: [day,datetime,utc]. [mth,datetime,utc]." /> <string name="status_online"> Zurzeit online </string> diff --git a/indra/newview/skins/default/xui/de/panel_status_bar.xml b/indra/newview/skins/default/xui/de/panel_status_bar.xml index 0829814220..e017c6dd82 100644 --- a/indra/newview/skins/default/xui/de/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/de/panel_status_bar.xml @@ -10,7 +10,7 @@ [hour12, datetime, slt]:[min, datetime, slt] [ampm, datetime, slt] [timezone,datetime, slt] </panel.string> <panel.string name="timeTooltip"> - [weekday, datetime, slt], [day, datetime, slt] [month, datetime, slt] [year, datetime, slt] + [weekday, datetime, slt], [day, datetime, slt]. [month, datetime, slt] [year, datetime, slt] </panel.string> <panel.string name="buycurrencylabel"> [AMT] L$ diff --git a/indra/newview/skins/default/xui/de/sidepanel_item_info.xml b/indra/newview/skins/default/xui/de/sidepanel_item_info.xml index 168bb14248..30886d1433 100644 --- a/indra/newview/skins/default/xui/de/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/de/sidepanel_item_info.xml @@ -19,7 +19,10 @@ Eigentümer kann: </panel.string> <panel.string name="acquiredDate"> - [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] + </panel.string> + <panel.string name="acquiredDateAMPM"> + [wkday,datetime,local]. [day,datetime,local]. [mth,datetime,local]. [year,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] </panel.string> <panel.string name="origin_inventory"> (Inventar) diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index 486d604e9f..dd003e72d9 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -39,7 +39,7 @@ Audiotreiberversion: [AUDIO_DRIVER_VERSION] LibVLC-Version: [LIBVLC_VERSION] Voice-Server-Version: [VOICE_VERSION]</string> <string name="AboutTraffic">Paketverlust: [PACKETS_LOST,number,0]/[PACKETS_IN,number,0] ([PACKETS_PCT,number,1] %)</string> - <string name="AboutTime">[month, datetime, slt] [day, datetime, slt] [year, datetime, slt] [hour, datetime, slt]:[min, datetime, slt]:[second,datetime,slt]</string> + <string name="AboutTime">[day, datetime, slt]. [month, datetime, slt] [year, datetime, slt] [hour, datetime, slt]:[min, datetime, slt]:[second,datetime,slt]</string> <string name="ErrorFetchingServerReleaseNotesURL">Fehler beim Abrufen der URL für die Server-Versionshinweise.</string> <string name="BuildConfiguration">Build-Konfiguration</string> <string name="ProgressRestoring">Wird wiederhergestellt...</string> @@ -812,7 +812,7 @@ Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich unter http://suppo <string name="Unknown">(unbekannt)</string> <string name="SummaryForTheWeek" value="Zusammenfassung für diese Woche, beginnend am "/> <string name="NextStipendDay" value=". Der nächste Stipendium-Tag ist "/> - <string name="GroupPlanningDate">[mthnum,datetime,utc]/[day,datetime,utc]/[year,datetime,utc]</string> + <string name="GroupPlanningDate">[day,datetime,utc].[mthnum,datetime,utc].[year,datetime,utc]</string> <string name="GroupIndividualShare" value=" Gruppenanteil Einzelanteil"/> <string name="GroupColumn" value="Gruppe"/> <string name="Balance">Kontostand</string> @@ -961,7 +961,7 @@ Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich unter http://suppo <string name="GroupMoneyBalance">Kontostand</string> <string name="GroupMoneyCredits">Danksagung</string> <string name="GroupMoneyDebits">Soll</string> - <string name="GroupMoneyDate">[weekday,datetime,utc] [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc]</string> + <string name="GroupMoneyDate">[weekday,datetime,utc] [day,datetime,utc]. [mth,datetime,utc]. [year,datetime,utc]</string> <string name="AcquiredItems">Erworbene Artikel</string> <string name="Cancel">Abbrechen</string> <string name="UploadingCosts">Das Hochladen von [NAME] kostet [AMOUNT] L$</string> @@ -1613,6 +1613,10 @@ Falls diese Meldung weiterhin angezeigt wird, wenden Sie sich bitte an [SUPPORT_ <string name="conference-title-incoming">Konferenz mit [AGENT_NAME]</string> <string name="inventory_item_offered-im">Inventarobjekt „[ITEM_NAME]“ angeboten</string> <string name="inventory_folder_offered-im">Inventarordner „[ITEM_NAME]“ angeboten</string> + <string name="bot_warning"> + Sie chatten mit einem Bot, [NAME]. Geben Sie keine persönlichen Informationen weiter. +Erfahren Sie mehr unter https://second.life/scripted-agents. + </string> <string name="share_alert">Objekte aus dem Inventar hier her ziehen</string> <string name="facebook_post_success">Sie haben auf Facebook gepostet.</string> <string name="flickr_post_success">Sie haben auf Flickr gepostet.</string> @@ -1765,7 +1769,7 @@ Missbrauchsbericht</string> <string name="dance6">Tanzen6</string> <string name="dance7">Tanzen7</string> <string name="dance8">Tanzen8</string> - <string name="AvatarBirthDateFormat">[mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt]</string> + <string name="AvatarBirthDateFormat">[day,datetime,slt].[mthnum,datetime,slt].[year,datetime,slt]</string> <string name="DefaultMimeType">Keine/Keiner</string> <string name="texture_load_dimensions_error">Bilder, die größer sind als [WIDTH]*[HEIGHT] können nicht geladen werden</string> <string name="outfit_photo_load_dimensions_error">Max. Fotogröße für Outfit ist [WIDTH]*[HEIGHT]. Bitte verkleinern Sie das Bild oder verwenden Sie ein anderes.</string> diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 508aba6ae1..c5b42b6dae 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -125,6 +125,9 @@ name="no_selection_text"> No parcel selected. </panel.string> + <panel.string name="time_stamp_template_ampm"> + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + </panel.string> <panel.string name="time_stamp_template"> [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] </panel.string> diff --git a/indra/newview/skins/default/xui/en/floater_inspect.xml b/indra/newview/skins/default/xui/en/floater_inspect.xml index 9403d58441..a083683c23 100644 --- a/indra/newview/skins/default/xui/en/floater_inspect.xml +++ b/indra/newview/skins/default/xui/en/floater_inspect.xml @@ -12,6 +12,10 @@ title="INSPECT OBJECTS" width="400"> <floater.string + name="timeStampAMPM"> + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + </floater.string> + <floater.string name="timeStamp"> [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] </floater.string> diff --git a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml index 6c3214a76d..1aa216d6b4 100644 --- a/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml +++ b/indra/newview/skins/default/xui/en/floater_inventory_item_properties.xml @@ -28,6 +28,10 @@ name="acquiredDate"> [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] </floater.string> + <floater.string + name="acquiredDateAMPM"> + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + </floater.string> <icon follows="top|right" height="18" diff --git a/indra/newview/skins/default/xui/en/floater_joystick.xml b/indra/newview/skins/default/xui/en/floater_joystick.xml index 597744238c..623f282305 100644 --- a/indra/newview/skins/default/xui/en/floater_joystick.xml +++ b/indra/newview/skins/default/xui/en/floater_joystick.xml @@ -35,7 +35,7 @@ <spinner bottom="56" height="10" - control_name="Axis1" + control_name="JoystickAxis1" decimal_digits="0" increment="1" label="X Axis Mapping" @@ -44,12 +44,12 @@ left="20" max_val="5" min_val="-1" - name="Axis1" + name="JoystickAxis1" width="140" /> <spinner bottom_delta="0" - height="10" - control_name="Axis2" + height="10" + control_name="JoystickAxis2" decimal_digits="0" increment="1" label="Y Axis Mapping" @@ -58,12 +58,12 @@ left="190" max_val="5" min_val="-1" - name="Axis2" + name="JoystickAxis2" width="140" /> <spinner bottom_delta="0" - height="10" - control_name="Axis0" + height="10" + control_name="JoystickAxis0" decimal_digits="0" increment="1" label="Z Axis Mapping" @@ -72,12 +72,12 @@ left="360" max_val="5" min_val="-1" - name="Axis0" + name="JoystickAxis0" width="140" /> <spinner bottom="76" - height="10" - control_name="Axis4" + height="10" + control_name="JoystickAxis4" decimal_digits="0" increment="1" label="Pitch Mapping" @@ -86,12 +86,12 @@ left="20" max_val="5" min_val="-1" - name="Axis4" + name="JoystickAxis4" width="140" /> <spinner bottom_delta="0" - height="10" - control_name="Axis5" + height="10" + control_name="JoystickAxis5" decimal_digits="0" increment="1" label="Yaw Mapping" @@ -100,12 +100,12 @@ left="190" max_val="5" min_val="-1" - name="Axis5" + name="JoystickAxis5" width="140" /> <spinner bottom_delta="0" - height="10" - control_name="Axis3" + height="10" + control_name="JoystickAxis3" decimal_digits="0" increment="1" label="Roll Mapping" @@ -114,12 +114,12 @@ left="360" max_val="5" min_val="-1" - name="Axis3" + name="JoystickAxis3" width="140" /> <spinner bottom="96" - height="10" - control_name="Axis6" + height="10" + control_name="JoystickAxis6" decimal_digits="0" increment="1" label="Zoom Mapping" @@ -128,7 +128,7 @@ left="20" max_val="5" min_val="-1" - name="Axis6" + name="JoystickAxis6" width="140" /> <check_box bottom_delta="18" @@ -141,7 +141,7 @@ width="60" /> <check_box bottom_delta="0" - height="10" + height="10" control_name="Cursor3D" label="3D Cursor" layout="topleft" @@ -150,7 +150,7 @@ width="60" /> <check_box bottom_delta="0" - height="10" + height="10" control_name="AutoLeveling" label="Auto Level" layout="topleft" @@ -173,7 +173,7 @@ </text> <check_box bottom="134" - height="10" + height="10" control_name="JoystickAvatarEnabled" halign="center" label="Avatar" @@ -183,7 +183,7 @@ width="60" /> <check_box bottom_delta="0" - height="10" + height="10" control_name="JoystickBuildEnabled" halign="center" label="Build" @@ -193,7 +193,7 @@ width="60" /> <check_box bottom_delta="0" - height="10" + height="10" control_name="JoystickFlycamEnabled" halign="center" label="Flycam" @@ -213,7 +213,7 @@ <stat_bar bar_max="2" bar_min="-2" - show_bar="true" + show_bar="true" label="Axis 0" label_spacing="1" layout="topleft" @@ -225,7 +225,7 @@ <stat_bar bar_max="2" bar_min="-2" - show_bar="true" + show_bar="true" label="Axis 1" label_spacing="1" layout="topleft" @@ -234,7 +234,7 @@ <stat_bar bar_max="2" bar_min="-2" - show_bar="true" + show_bar="true" label="Axis 2" label_spacing="1" layout="topleft" @@ -243,7 +243,7 @@ <stat_bar bar_max="2" bar_min="-2" - show_bar="true" + show_bar="true" label="Axis 3" label_spacing="1" layout="topleft" @@ -252,7 +252,7 @@ <stat_bar bar_max="2" bar_min="-2" - show_bar="true" + show_bar="true" label="Axis 4" label_spacing="1" layout="topleft" @@ -262,7 +262,7 @@ bar_max="2" bar_min="-2" label="Axis 5" - show_bar="true" + show_bar="true" label_spacing="1" layout="topleft" name="axis5" @@ -281,7 +281,7 @@ </text> <spinner bottom_delta="0" - height="10" + height="10" control_name="AvatarAxisScale1" decimal_digits="2" label_width="0" @@ -293,7 +293,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="BuildAxisScale1" decimal_digits="2" label_width="0" @@ -305,7 +305,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="FlycamAxisScale1" decimal_digits="2" label_width="0" @@ -328,7 +328,7 @@ </text> <spinner bottom_delta="0" - height="10" + height="10" control_name="AvatarAxisScale2" decimal_digits="2" label_width="0" @@ -340,7 +340,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="BuildAxisScale2" decimal_digits="2" label_width="0" @@ -352,7 +352,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="FlycamAxisScale2" decimal_digits="2" label_width="0" @@ -375,7 +375,7 @@ </text> <spinner bottom_delta="0" - height="10" + height="10" control_name="AvatarAxisScale0" decimal_digits="2" label_width="0" @@ -387,7 +387,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="BuildAxisScale0" decimal_digits="2" label_width="0" @@ -399,7 +399,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="FlycamAxisScale0" decimal_digits="2" label_width="0" @@ -422,7 +422,7 @@ </text> <spinner bottom_delta="0" - height="10" + height="10" control_name="AvatarAxisScale4" decimal_digits="2" label_width="0" @@ -434,7 +434,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="BuildAxisScale4" decimal_digits="2" label_width="0" @@ -446,7 +446,7 @@ width="56" /> <spinner bottom_delta="0" - height="10" + height="10" control_name="FlycamAxisScale4" decimal_digits="2" label_width="0" @@ -595,7 +595,7 @@ </text> <spinner bottom_delta="0" - height="10" + height="10" control_name="AvatarAxisDeadZone2" decimal_digits="2" increment="0.01" diff --git a/indra/newview/skins/default/xui/en/floater_lua_debug.xml b/indra/newview/skins/default/xui/en/floater_lua_debug.xml index 15027f1647..5efe1c958a 100644 --- a/indra/newview/skins/default/xui/en/floater_lua_debug.xml +++ b/indra/newview/skins/default/xui/en/floater_lua_debug.xml @@ -25,6 +25,17 @@ width="100"> LUA string: </text> + <check_box + control_name="LuaDebugShowSource" + follows="right|top" + height="15" + label="Show source info" + layout="topleft" + top="10" + right ="-70" + tool_tip="Show source info in Lua Debug Console output" + name="show_script_name" + width="70"/> <line_editor border_style="line" border_thickness="1" diff --git a/indra/newview/skins/default/xui/en/floater_lua_scripts.xml b/indra/newview/skins/default/xui/en/floater_lua_scripts.xml index 6859201650..211cea75d8 100644 --- a/indra/newview/skins/default/xui/en/floater_lua_scripts.xml +++ b/indra/newview/skins/default/xui/en/floater_lua_scripts.xml @@ -22,6 +22,7 @@ follows="all" layout="topleft" sort_column="script_name" + multi_select="true" name="scripts_list" top_pad="10" width="535"> diff --git a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml index 73ea0f548e..e81ff7f343 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences_graphics_advanced.xml @@ -821,7 +821,7 @@ layout="topleft" left="420" min_val="0.5" - max_val="1.5" + max_val="4.0" name="RenderExposure" show_text="true" top_delta="24" diff --git a/indra/newview/skins/default/xui/en/floater_rlv_console.xml b/indra/newview/skins/default/xui/en/floater_rlv_console.xml new file mode 100644 index 0000000000..708055d1b6 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_rlv_console.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + can_resize="true" + height="400" + layout="topleft" + min_height="300" + min_width="300" + name="rlv_console" + title="RLVa console" + width="600" + > + <layout_stack + animate="false" + bottom="-1" + default_tab_group="2" + follows="all" + left="5" + layout="topleft" + mouse_opaque="false" + name="main_stack" + right="-5" + orientation="vertical" + tab_group="1" + top="1" + > + <layout_panel + name="body_panel" + height="235"> + <text_editor + follows="all" + left="1" + right="-1" + top="0" + length="1" + font="Monospace" + bottom="-1" + ignore_tab="false" + layout="topleft" + max_length="65536" + name="console_output" + read_only="true" + track_end="true" + type="string" + word_wrap="true" + > + </text_editor> + </layout_panel> + + <layout_panel + height="26" + auto_resize="false" + name="input_panel"> + <chat_editor + layout="topleft" + expand_lines_count="5" + follows="left|right|bottom" + font="SansSerifSmall" + height="20" + is_expandable="true" + text_tentative_color="TextFgTentativeColor" + name="console_input" + max_length="1023" + spellcheck="true" + tab_group="3" + bottom_delta="20" + left="1" + top="1" + right="-1" + show_emoji_helper="false" + wrap="true" + /> + </layout_panel> + </layout_stack> +</floater> diff --git a/indra/newview/skins/default/xui/en/floater_world_map.xml b/indra/newview/skins/default/xui/en/floater_world_map.xml index 5ab0177de6..6883ec1cd0 100644 --- a/indra/newview/skins/default/xui/en/floater_world_map.xml +++ b/indra/newview/skins/default/xui/en/floater_world_map.xml @@ -109,7 +109,7 @@ </panel> <panel follows="right|top" - height="126" + height="150" top_pad="4" width="238" left="1" @@ -285,6 +285,29 @@ by owner </text> + <check_box + name="grid_coords_chk" + control_name="MapShowGridCoords" + layout="topleft" + follows="top|right" + top_pad="2" + left="3" + height="16" + width="22" + /> + <text + name="grid_coords_label" + type="string" + layout="topleft" + follows="top|right" + top_delta="2" + left_pad="3" + height="16" + width="220" + halign="left" + length="1" + >Show grid map coordinates</text> + <button follows="top|right" height="22" @@ -455,7 +478,7 @@ <panel follows="right|top|bottom" - height="330" + height="306" top_pad="0" width="238" name="layout_panel_4"> @@ -576,7 +599,7 @@ draw_stripes="false" bg_writeable_color="MouseGray" follows="all" - height="145" + height="121" layout="topleft" left="28" name="search_results" diff --git a/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml b/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml index 6074b72dc8..ef3d7ee4f7 100644 --- a/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml +++ b/indra/newview/skins/default/xui/en/game_control_table_camera_rows.xml @@ -22,7 +22,7 @@ halign="left" name="action" tool_tip="Camera move forward/backward" - value="Advance Forward" /> + value="Forward/Backward" /> </rows> <rows name="pan" @@ -33,7 +33,7 @@ halign="left" name="action" tool_tip="Camera pan left/right" - value="Pan Left" /> + value="Pan Left/Right" /> </rows> <rows name="rise" @@ -44,7 +44,7 @@ halign="left" name="action" tool_tip="Camera rise/fall" - value="Rise Up" /> + value="Rise Up/Down" /> </rows> <rows name="pitch" @@ -55,7 +55,7 @@ halign="left" name="action" tool_tip="Camera adjust pitch" - value="Pitch Down" /> + value="Pitch Up/Down" /> </rows> <rows name="yaw" @@ -66,7 +66,7 @@ halign="left" name="action" tool_tip="Camera turn left/right" - value="Yaw CCW" /> + value="Yaw CCW/CW" /> </rows> <rows name="zoom" @@ -77,7 +77,7 @@ halign="left" name="action" tool_tip="Camera zoom in/out" - value="Zoom In" /> + value="Zoom In/Out" /> </rows> <!-- <rows diff --git a/indra/newview/skins/default/xui/en/game_control_table_rows.xml b/indra/newview/skins/default/xui/en/game_control_table_rows.xml index 181bc8592c..cd6f483782 100644 --- a/indra/newview/skins/default/xui/en/game_control_table_rows.xml +++ b/indra/newview/skins/default/xui/en/game_control_table_rows.xml @@ -21,7 +21,7 @@ font="SansSerif" halign="left" name="action" - value="Walk Forward" /> + value="Forward/Backward" /> </rows> <rows name="slide" @@ -31,7 +31,7 @@ font="SansSerif" halign="left" name="action" - value="Strafe Left" /> + value="Strafe" /> </rows> <rows name="jump" @@ -51,7 +51,7 @@ font="SansSerif" halign="left" name="action" - value="Turn Left" /> + value="Turn" /> </rows> <rows name="look" @@ -61,7 +61,7 @@ font="SansSerif" halign="left" name="action" - value="Look Down" /> + value="Look Up/Down" /> </rows> <rows name="stop" diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index da9cc0c98d..53615968e0 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -562,25 +562,6 @@ </menu_item_check> <menu_item_separator/> <menu_item_check - label="Hear Media and Sound from Avatar" - name="Hear Media and Sound from Avatar"> - <menu_item_check.on_check - control="MediaSoundsEarLocation" /> - <menu_item_check.on_click - function="Agent.ToggleHearMediaSoundFromAvatar" /> - </menu_item_check> - <menu_item_check - label="Hear Voice from Avatar" - name="Hear Voice from Avatar"> - <menu_item_check.on_check - control="VoiceEarLocation" /> - <menu_item_check.on_click - function="Agent.ToggleHearVoiceFromAvatar" /> - <menu_item_call.on_enable - control="EnableVoiceChat" /> - </menu_item_check> - <menu_item_separator/> - <menu_item_check label="Gestures..." name="Gestures" shortcut="control|G"> @@ -1838,6 +1819,71 @@ function="World.EnvPreset" </menu> <menu create_jump_keys="true" + label="RLVa" + name="RLVa Main" + tear_off="true" + visible="true"> + <menu + label="Debug" + name="Debug" + tear_off="true"> + <menu_item_check + label="Show Top-level RLVa Menu" + name="Show Top-level RLVa Menu"> + <menu_item_check.on_check + function="CheckControl" + parameter="RLVaTopLevelMenu" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="RLVaTopLevelMenu" /> + </menu_item_check> + <menu_item_separator/> + <menu_item_check + label="Show Debug Messages" + name="Show Debug Messages"> + <menu_item_check.on_check + function="CheckControl" + parameter="RestrainedLoveDebug" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="RestrainedLoveDebug" /> + </menu_item_check> + <menu_item_check + label="Hide Unset or Duplicate Messages" + name="Hide Unset or Duplicate Messages"> + <menu_item_check.on_check + function="CheckControl" + parameter="RLVaDebugHideUnsetDuplicate" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="RLVaDebugHideUnsetDuplicate" /> + </menu_item_check> + </menu> + <menu_item_separator/> + <menu_item_check + label="Allow Temporary Attachments" + name="Allow Temporary Attachments"> + <menu_item_check.on_check + function="CheckControl" + parameter="RLVaEnableTemporaryAttachments" /> + <menu_item_check.on_click + function="ToggleControl" + parameter="RLVaEnableTemporaryAttachments" /> + </menu_item_check> + <menu_item_separator /> + <menu_item_check + label="Console..." + name="Console"> + <menu_item_check.on_check + function="Floater.Visible" + parameter="rlv_console" /> + <menu_item_check.on_click + function="Floater.Toggle" + parameter="rlv_console" /> + </menu_item_check> + </menu> + <menu + create_jump_keys="true" label="Advanced" name="Advanced" tear_off="true" @@ -2343,7 +2389,12 @@ function="World.EnvPreset" function="Advanced.ToggleFeature" parameter="flexible" /> </menu_item_check> - </menu> + </menu> + <menu + label="RLVa" + name="RLVa Embedded" + tear_off="true" + visible="true" /> <menu_item_check label="Use Plugin Read Thread" name="Use Plugin Read Thread"> @@ -3756,6 +3807,13 @@ function="World.EnvPreset" </menu_item_call> <menu_item_call enabled="true" + label="Edit Local Paintmap" + name="Edit Local Paintmap"> + <menu_item_call.on_click + function="Advanced.TerrainEditLocalPaintMap" /> + </menu_item_call> + <menu_item_call + enabled="true" label="Delete Local Paintmap" name="Delete Local Paintmap"> <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 071f6458c5..cbc0f7ceae 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -1662,7 +1662,7 @@ The new skin will appear after you restart [APP_NAME]. icon="alertmodal.tga" name="ChangeLanguage" type="alertmodal"> -Changing language will take effect after you restart [APP_NAME]. +Changing language or time format will take effect after you restart [APP_NAME]. </notification> <notification @@ -6532,6 +6532,22 @@ Do you want to replace it with the selected object? </notification> <notification + icon="alertmodal.tga" + name="CantLinkNotecard" + type="alertmodal"> + You must save the notecard before creating a link to it. + <tag>fail</tag> + </notification> + + <notification + icon="alertmodal.tga" + name="CantLinkMaterial" + type="alertmodal"> + You must save the material before creating a link to it. + <tag>fail</tag> + </notification> + + <notification icon="alert.tga" label="Do Not Disturb Mode Warning" name="DoNotDisturbModePay" @@ -12587,4 +12603,34 @@ are wearing now. yestext="OK"/> </notification> + <notification + icon="notify.tga" + name="TextureDiscardBackgrounded" + type="notify"> + <unique> + <context>DELAY</context> + </unique> + To improve system performance, [SECOND_LIFE] has reduced texture memory usage after being in the background for [DELAY] seconds. It may take some time for texture image quality to return to normal. + <usetemplate + ignoretext="Ask me about background texture memory usage and recovery" + name="okcancelignore" + yestext="OK" + notext="Disable"/> + </notification> + + <notification + icon="notify.tga" + name="TextureDiscardMinimized" + type="notify"> + <unique> + <context>DELAY</context> + </unique> + To improve system performance, [SECOND_LIFE] has reduced texture memory usage after being minimized for [DELAY] seconds. It may take some time for texture image quality to return to normal. + <usetemplate + ignoretext="Ask me about minimized texture memory usage and recovery" + name="okcancelignore" + yestext="OK" + notext="Disable"/> + </notification> + </notifications> diff --git a/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml index ca6e94397d..53551b2f79 100644 --- a/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml @@ -156,6 +156,5 @@ mouse_opaque="true" name="speaking_indicator" tool_tip="Voice volume" - visible="true" width="20" /> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml b/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml index 7902588598..e0b7f71321 100644 --- a/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_conversation_list_item.xml @@ -91,7 +91,6 @@ left_pad="5" mouse_opaque="true" name="speaking_indicator" - visible="false" width="20" /> </layout_panel> </layout_stack> diff --git a/indra/newview/skins/default/xui/en/panel_landmark_info.xml b/indra/newview/skins/default/xui/en/panel_landmark_info.xml index af68bd7fee..19599efdc2 100644 --- a/indra/newview/skins/default/xui/en/panel_landmark_info.xml +++ b/indra/newview/skins/default/xui/en/panel_landmark_info.xml @@ -40,6 +40,10 @@ Information about this location is unavailable due to access restrictions. Please check your permissions with the parcel owner. </string> <string + name="acquired_date_ampm"> + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + </string> + <string name="acquired_date"> [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] </string> diff --git a/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml b/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml index 1a89d07cbb..48f6580d61 100644 --- a/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml @@ -34,7 +34,6 @@ mouse_opaque="true" name="chat_zone_indicator" top="6" - visible="true" width="20" /> <button follows="right" diff --git a/indra/newview/skins/default/xui/en/panel_place_profile.xml b/indra/newview/skins/default/xui/en/panel_place_profile.xml index 8f5292c531..bb877080b1 100644 --- a/indra/newview/skins/default/xui/en/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_place_profile.xml @@ -89,6 +89,10 @@ Information about this location is unavailable due to access restrictions. Please check your permissions with the parcel owner. </string> <string + name="acquired_date_ampm"> + [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour12,datetime,local]:[min,datetime,local]:[second,datetime,local] [ampm,datetime,local] [year,datetime,local] + </string> + <string name="acquired_date"> [wkday,datetime,local] [mth,datetime,local] [day,datetime,local] [hour,datetime,local]:[min,datetime,local]:[second,datetime,local] [year,datetime,local] </string> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml b/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml index bd92fe895d..fb6cc8dc14 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml @@ -62,6 +62,7 @@ label="Action" relative_width="0.25" /> <scroll_list.columns + name="channel" label="Channel" relative_width="0.75" /> </scroll_list> @@ -81,6 +82,7 @@ right="-1" bottom="-1">No device</text> <panel + name="device_selection_panel" layout="topleft" follows="top|left|right" top="1" @@ -156,15 +158,19 @@ selection_type="header" fg_disable_color="ScrollUnselectedColor"> <scroll_list.columns + name="axis" label="Axis" relative_width="0.5"/> <scroll_list.columns + name="invert" label="Invert" relative_width="0.1"/> <scroll_list.columns + name="dead_zone" label="Dead Zone" relative_width="0.2"/> <scroll_list.columns + name="offset" label="Offset" relative_width="0.2"/> </scroll_list> @@ -212,6 +218,7 @@ </panel> </tab_container> <panel + name="reset_to_defaults_panel" layout="topleft" follows="top|left" top="405" @@ -237,13 +244,13 @@ name="axis_selector" width="250" height="18"> - <combo_box.item label="AXIS_0 (Left stick: tilt left/right)"/> - <combo_box.item label="AXIS_1 (Left stick: tilt forward/back)"/> - <combo_box.item label="AXIS_2 (Right stick: tilt left/right)"/> - <combo_box.item label="AXIS_3 (Right stick: tilt forward/back)"/> - <combo_box.item label="AXIS_4 (Left trigger: push/release)"/> - <combo_box.item label="AXIS_5 (Right trigger: push/release)"/> - <combo_box.item label="NONE (No mapping)"/> + <combo_box.item name="axis_0" label="AXIS_0 (Left stick: tilt left/right)"/> + <combo_box.item name="axis_1" label="AXIS_1 (Left stick: tilt forward/back)"/> + <combo_box.item name="axis_2" label="AXIS_2 (Right stick: tilt left/right)"/> + <combo_box.item name="axis_3" label="AXIS_3 (Right stick: tilt forward/back)"/> + <combo_box.item name="axis_4" label="AXIS_4 (Left trigger: push/release)"/> + <combo_box.item name="axis_5" label="AXIS_5 (Right trigger: push/release)"/> + <combo_box.item name="axis_none" label="NONE (No mapping)"/> </combo_box> <combo_box name="analog_channel_selector" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_general.xml b/indra/newview/skins/default/xui/en/panel_preferences_general.xml index 101c506309..ddddb4855f 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_general.xml @@ -91,6 +91,37 @@ name="Traditional Chinese" value="zh" /> </combo_box> + <text + type="string" + length="1" + follows="left|top" + height="15" + layout="topleft" + left="255" + name="time_format_textbox" + top="10" + width="200"> + Time Format: + </text> + <combo_box + follows="left|top" + height="23" + layout="topleft" + left="255" + max_chars="135" + name="time_format_combobox" + width="70"> + <combo_box.item + enabled="true" + label="1:00 AM" + name="12H" + value="0" /> + <combo_box.item + enabled="true" + label="13:00" + name="24H" + value="1" /> + </combo_box> <text font="SansSerifSmall" type="string" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml index cb3e0e4b6a..a64b3eee36 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml @@ -371,7 +371,7 @@ layout="topleft" left="30" min_val="0.5" - max_val="1.5" + max_val="4.0" name="RenderExposure" show_text="true" top_pad="14" diff --git a/indra/newview/skins/default/xui/en/panel_region_general.xml b/indra/newview/skins/default/xui/en/panel_region_general.xml index 6ef1b6e44a..47e1e669d1 100644 --- a/indra/newview/skins/default/xui/en/panel_region_general.xml +++ b/indra/newview/skins/default/xui/en/panel_region_general.xml @@ -33,6 +33,28 @@ unknown </text> <text + follows="right|top" + font="SansSerif" + height="20" + layout="topleft" + top_delta="0" + right="-100" + name="estate_id_lbl" + width="80"> + Estate ID: + </text> + <line_editor + follows="right|top" + font="SansSerif" + height="20" + layout="topleft" + top_delta="0" + name="estate_id" + enabled="false" + right="-10" + initial_value="unknown" + width="85" /> + <text follows="left|top" font="SansSerif" height="20" @@ -55,6 +77,39 @@ unknown </text> <text + follows="right|top" + font="SansSerif" + height="20" + layout="topleft" + top_delta="0" + right="-100" + name="grid_position_lbl" + width="80"> + Grid Position: + </text> + <line_editor + follows="right|top" + font="SansSerif" + height="20" + layout="topleft" + right="-55" + name="grid_position_x" + enabled="false" + top_delta="0" + default_text="n/a" + width="40" /> + <line_editor + follows="right|top" + font="SansSerif" + height="20" + layout="topleft" + right="-10" + name="grid_position_y" + enabled="false" + top_delta="0" + default_text="n/a" + width="40" /> + <text follows="left|top" font="SansSerif" height="20" diff --git a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml index 1cb3eca2eb..40a88d4121 100644 --- a/indra/newview/skins/default/xui/en/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/en/sidepanel_item_info.xml @@ -32,6 +32,10 @@ Owner can: </panel.string> <panel.string + name="acquiredDateAMPM"> + [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour12,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [ampm,datetime,slt] [year,datetime,slt] + </panel.string> + <panel.string name="acquiredDate"> [wkday,datetime,slt] [mth,datetime,slt] [day,datetime,slt] [hour,datetime,slt]:[min,datetime,slt]:[second,datetime,slt] [year,datetime,slt] </panel.string> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index effd19b708..9be4a8f737 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -60,6 +60,7 @@ Disk cache: [DISK_CACHE_INFO] HiDPI display mode: [HIDPI] </string> <string name="AboutLibs"> +RestrainedLove API: [RLV_VERSION] J2C Decoder Version: [J2C_VERSION] Audio Driver Version: [AUDIO_DRIVER_VERSION] [LIBCEF_VERSION] @@ -3719,6 +3720,10 @@ Please reinstall viewer from https://secondlife.com/support/downloads/ and cont <string name="inventory_folder_offered-im"> Inventory folder '[ITEM_NAME]' offered </string> + <string name="bot_warning"> + You are chatting with a bot, [NAME]. Do not share any personal information. +Learn more at https://second.life/scripted-agents. + </string> <string name="share_alert"> Drag items from inventory here </string> @@ -4381,4 +4386,31 @@ and report the problem. [https://community.secondlife.com/knowledgebase/english/error-messages-r520/#Section__3 Knowledge Base] </string> + <!-- RLVa --> + <string name="RlvConsoleDisable">RLVa is disabled</string> + <string name="RlvConsoleInvalidCmd">Invalid command</string> + <string name="RlvConsoleExecuted">INFO</string> + <string name="RlvConsoleFailed">ERR</string> + <string name="RlvConsoleRetained">RET</string> + <string name="RlvDebugExecuted">executed</string> + <string name="RlvDebugFailed">failed</string> + <string name="RlvDebugRetained">retained</string> + <string name="RlvReturnCodeUnset">unset</string> + <string name="RlvReturnCodeDuplicate">duplicate</string> + <string name="RlvReturnCodeDelayed">delayed</string> + <string name="RlvReturnCodeDeprecated">deprecated</string> + <string name="RlvReturnCodeSyntax">thingy error</string> + <string name="RlvReturnCodeOption">invalid option</string> + <string name="RlvReturnCodeParam">invalid param</string> + <string name="RlvReturnCodeLock">locked command</string> + <string name="RlvReturnCodeDisabled">disabled command</string> + <string name="RlvReturnCodeUnknown">unknown command</string> + <string name="RlvReturnCodeNoSharedRoot">missing #RLV</string> + <string name="RlvReturnCodeDeprecatedAndDisabled">deprecated and disabled</string> + <string name="RlvReturnCodeNoBehaviour">no active behaviours</string> + <string name="RlvReturnCodeUnheldBehaviour">base behaviour not held</string> + <string name="RlvReturnCodeBlocked">blocked object</string> + <string name="RlvReturnCodeThrottled">throttled</string> + <string name="RlvReturnCodeNoProcessor">no command processor found</string> + </strings> diff --git a/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml b/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml index cf995e5833..180071a321 100644 --- a/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml +++ b/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml @@ -37,6 +37,5 @@ right="-3" mouse_opaque="true" name="speaking_indicator" - visible="true" - width="20" /> + width="20" /> </conversation_view_participant> diff --git a/indra/newview/skins/default/xui/en/widgets/output_monitor.xml b/indra/newview/skins/default/xui/en/widgets/output_monitor.xml index 788c733ca8..10e39fa030 100644 --- a/indra/newview/skins/default/xui/en/widgets/output_monitor.xml +++ b/indra/newview/skins/default/xui/en/widgets/output_monitor.xml @@ -10,4 +10,5 @@ image_level_3="VoicePTT_Lvl3" mouse_opaque="false" name="output_monitor" + visible="false" /> diff --git a/indra/newview/skins/default/xui/en/widgets/person_view.xml b/indra/newview/skins/default/xui/en/widgets/person_view.xml index 82dbdf0dab..e07f340be1 100644 --- a/indra/newview/skins/default/xui/en/widgets/person_view.xml +++ b/indra/newview/skins/default/xui/en/widgets/person_view.xml @@ -111,7 +111,6 @@ right="-3" mouse_opaque="true" name="speaking_indicator" - visible="false" width="20" /> </person_view> diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index 9fcfc2daa5..97e86e994c 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -1584,6 +1584,10 @@ Si sigues recibiendo este mensaje, contacta con [SUPPORT_SITE].</string> <string name="conference-title-incoming">Conferencia con [AGENT_NAME]</string> <string name="inventory_item_offered-im">Ítem del inventario '[ITEM_NAME]' ofrecido</string> <string name="inventory_folder_offered-im">Carpeta del inventario '[ITEM_NAME]' ofrecida</string> + <string name="bot_warning"> +Estás conversando con un bot, [NAME]. No compartas información personal. +Más información en https://second.life/scripted-agents. + </string> <string name="share_alert">Arrastra los ítems desde el invenbtario hasta aquí</string> <string name="facebook_post_success">Has publicado en Facebook.</string> <string name="flickr_post_success">Has publicado en Flickr.</string> diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml index 55f6209fe1..60916ef92b 100644 --- a/indra/newview/skins/default/xui/fr/strings.xml +++ b/indra/newview/skins/default/xui/fr/strings.xml @@ -1614,6 +1614,10 @@ Si ce message persiste, veuillez aller sur la page [SUPPORT_SITE].</string> <string name="conference-title-incoming">Conférence avec [AGENT_NAME]</string> <string name="inventory_item_offered-im">Objet de l’inventaire [ITEM_NAME] offert</string> <string name="inventory_folder_offered-im">Dossier de l’inventaire [ITEM_NAME] offert</string> + <string name="bot_warning"> +Vous discutez avec un bot, [NAME]. Ne partagez pas d’informations personnelles. +En savoir plus sur https://second.life/scripted-agents. + </string> <string name="share_alert">Faire glisser les objets de l'inventaire ici</string> <string name="facebook_post_success">Vous avez publié sur Facebook.</string> <string name="flickr_post_success">Vous avez publié sur Flickr.</string> diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml index f77ab1062a..88708a2b4d 100644 --- a/indra/newview/skins/default/xui/it/strings.xml +++ b/indra/newview/skins/default/xui/it/strings.xml @@ -1586,6 +1586,10 @@ Se il messaggio persiste, contatta [SUPPORT_SITE].</string> <string name="conference-title-incoming">Chiamata in conferenza con [AGENT_NAME]</string> <string name="inventory_item_offered-im">Offerto oggetto di inventario "[ITEM_NAME]"</string> <string name="inventory_folder_offered-im">Offerta cartella di inventario "[ITEM_NAME]"</string> + <string name="bot_warning"> +Stai parlando con un bot, [NAME]. Non condividere informazioni personali. +Scopri di più su https://second.life/scripted-agents. + </string> <string name="facebook_post_success">Hai pubblicato su Facebook.</string> <string name="flickr_post_success">Hai pubblicato su Flickr.</string> <string name="twitter_post_success">Hai pubblicato su Twitter.</string> diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml index fa6c329fe7..ff3b1a53a2 100644 --- a/indra/newview/skins/default/xui/ja/strings.xml +++ b/indra/newview/skins/default/xui/ja/strings.xml @@ -6150,6 +6150,10 @@ www.secondlife.com から最新バージョンをダウンロードしてくだ <string name="inventory_folder_offered-im"> フォルダ「[ITEM_NAME]」がインベントリに送られてきました。 </string> + <string name="bot_warning"> +[NAME]とチャットしています。個人情報を共有しないでください。 +詳細は https://second.life/scripted-agents をご覧ください。 + </string> <string name="share_alert"> インベントリからここにアイテムをドラッグします。 </string> diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml index 8032443020..65b487e1b3 100644 --- a/indra/newview/skins/default/xui/pl/strings.xml +++ b/indra/newview/skins/default/xui/pl/strings.xml @@ -4412,6 +4412,10 @@ Jeżeli nadal otrzymujesz ten komunikat, skontaktuj się z [SUPPORT_SITE]. <string name="inventory_folder_offered-im"> Zaoferowano folder: '[ITEM_NAME]' </string> + <string name="bot_warning"> +Rozmawiasz z botem [NAME]. Nie udostępniaj żadnych danych osobowych. +Dowiedz się więcej na https://second.life/scripted-agents. + </string> <string name="share_alert"> Przeciągaj tutaj rzeczy z Szafy </string> diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index 4ce1e6d2ec..9e66777b5a 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -1549,6 +1549,10 @@ If you continue to receive this message, contact the [SUPPORT_SITE].</string> <string name="conference-title-incoming">Conversa com [AGENT_NAME]</string> <string name="inventory_item_offered-im">Item do inventário '[ITEM_NAME]' oferecido</string> <string name="inventory_folder_offered-im">Pasta do inventário '[ITEM_NAME]' oferecida</string> + <string name="bot_warning"> +Você está conversando com um bot, [NAME]. Não compartilhe informações pessoais. +Saiba mais em https://second.life/scripted-agents. + </string> <string name="facebook_post_success">Você publicou no Facebook.</string> <string name="flickr_post_success">Você publicou no Flickr.</string> <string name="twitter_post_success">Você publicou no Twitter.</string> diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml index 0079309ba2..174999ea36 100644 --- a/indra/newview/skins/default/xui/ru/strings.xml +++ b/indra/newview/skins/default/xui/ru/strings.xml @@ -4576,6 +4576,10 @@ support@secondlife.com. <string name="inventory_folder_offered-im"> Предложена папка инвентаря «[ITEM_NAME]» </string> + <string name="bot_warning"> +Вы общаетесь с ботом [NAME]. Не передавайте личные данные. +Подробнее на https://second.life/scripted-agents. + </string> <string name="share_alert"> Перетаскивайте вещи из инвентаря сюда </string> diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml index fa2fd3a802..6c1f6506a2 100644 --- a/indra/newview/skins/default/xui/tr/strings.xml +++ b/indra/newview/skins/default/xui/tr/strings.xml @@ -4579,6 +4579,10 @@ Bu iletiyi almaya devam ederseniz, lütfen [SUPPORT_SITE] bölümüne başvurun. <string name="inventory_folder_offered-im"> "[ITEM_NAME]" envanter klasörü sunuldu </string> + <string name="bot_warning"> +Bir bot ile sohbet ediyorsunuz, [NAME]. Kişisel bilgilerinizi paylaşmayın. +Daha fazla bilgi için: https://second.life/scripted-agents. + </string> <string name="share_alert"> Envanterinizden buraya öğeler sürükleyin </string> diff --git a/indra/newview/skins/default/xui/zh/strings.xml b/indra/newview/skins/default/xui/zh/strings.xml index bdb16c9bf1..a3a9915dc4 100644 --- a/indra/newview/skins/default/xui/zh/strings.xml +++ b/indra/newview/skins/default/xui/zh/strings.xml @@ -4573,6 +4573,10 @@ http://secondlife.com/support 求助解決問題。 <string name="inventory_folder_offered-im"> 收納區資料夾'[ITEM_NAME]'已向人提供 </string> + <string name="bot_warning"> +您正在与人工智能机器人 [NAME] 聊天。请勿分享任何个人信息。 +了解更多:https://second.life/scripted-agents。 + </string> <string name="share_alert"> 將收納區物品拖曳到這裡 </string> diff --git a/indra/newview/tests/llluamanager_test.cpp b/indra/newview/tests/llluamanager_test.cpp index f0a1b32eed..9687b68451 100644 --- a/indra/newview/tests/llluamanager_test.cpp +++ b/indra/newview/tests/llluamanager_test.cpp @@ -55,14 +55,14 @@ namespace tut // indra/newview/tests/llluamanager_test.cpp => // indra/newview auto newview{ fsyspath(__FILE__).parent_path().parent_path() }; - auto settings{ newview / "app_settings" / "settings.xml" }; + fsyspath settings{ newview / "app_settings" / "settings.xml" }; // true suppresses implicit declare; implicit declare requires // that every variable in settings.xml has a Comment, which many don't. - gSavedSettings.loadFromFile(settings.u8string(), true); + gSavedSettings.loadFromFile(settings, true); // At test time, since we don't have the app bundle available, // extend LuaRequirePath to include the require directory in the // source tree. - auto require{ (newview / "scripts" / "lua" / "require").u8string() }; + std::string require{ fsyspath(newview / "scripts" / "lua" / "require") }; auto paths{ gSavedSettings.getLLSD("LuaRequirePath") }; bool found = false; for (const auto& path : llsd::inArray(paths)) diff --git a/indra/newview/tests/llworldmap_test.cpp b/indra/newview/tests/llworldmap_test.cpp index d5bf189d82..99c98f63ed 100644 --- a/indra/newview/tests/llworldmap_test.cpp +++ b/indra/newview/tests/llworldmap_test.cpp @@ -28,6 +28,7 @@ // Dependencies #include "linden_common.h" #include "llapr.h" +#include "llcontrol.h" // LLControlGroup #include "llsingleton.h" #include "lltrans.h" #include "lluistring.h" @@ -71,6 +72,8 @@ void LLUIString::updateResult() const { } void LLUIString::setArg(const std::string& , const std::string& ) { } void LLUIString::assign(const std::string& ) { } +LLControlGroup gSavedSettings("Global"); // saved at end of session + // End Stubbing // ------------------------------------------------------------------------------------------- @@ -131,6 +134,7 @@ namespace tut // Constructor and destructor of the test wrapper worldmap_test() { + gSavedSettings.declareBOOL("Use24HourClock", true, "", LLControlVariable::PERSIST_NO); mWorld = LLWorldMap::getInstance(); } ~worldmap_test() diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index efc90b8991..18ff6cf7b6 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1032,6 +1032,12 @@ class Darwin_x86_64_Manifest(ViewerManifest): ): dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile) + # SDL2 + for libfile in ( + 'libSDL2-2.0.dylib', + ): + dylibs += path_optional(os.path.join(relpkgdir, libfile), libfile) + # our apps executable_path = {} embedded_apps = [ (os.path.join("llplugin", "slplugin"), "SLPlugin.app") ] diff --git a/indra/test/lltut.h b/indra/test/lltut.h index fbf60444be..986bdd0619 100644 --- a/indra/test/lltut.h +++ b/indra/test/lltut.h @@ -30,8 +30,10 @@ #define LL_LLTUT_H #include "is_approx_equal_fraction.h" // instead of llmath.h +#include "stringize.h" #include <cstring> #include <string> +#include <string_view> #include <vector> class LLDate; @@ -89,49 +91,38 @@ namespace tut // The functions BELOW this point actually consume tut.hpp functionality. namespace tut { - inline void ensure_approximately_equals(const char* msg, F64 actual, F64 expected, U32 frac_bits) + template <typename F> // replace with C++20 floating-point concept + inline void ensure_approximately_equals(std::string_view msg, F actual, F expected, U32 frac_bits) { if(!is_approx_equal_fraction(actual, expected, frac_bits)) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "not equal actual: ", + actual, " expected: ", expected)); } } - inline void ensure_approximately_equals(const char* msg, F32 actual, F32 expected, U32 frac_bits) + template <typename F> // replace with C++20 floating-point concept + inline void ensure_approximately_equals(F actual, F expected, U32 frac_bits) { - if(!is_approx_equal_fraction(actual, expected, frac_bits)) - { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected; - throw tut::failure(ss.str().c_str()); - } - } - - inline void ensure_approximately_equals(F32 actual, F32 expected, U32 frac_bits) - { - ensure_approximately_equals(NULL, actual, expected, frac_bits); + ensure_approximately_equals("", actual, expected, frac_bits); } - inline void ensure_approximately_equals_range(const char *msg, F32 actual, F32 expected, F32 delta) + template <typename F> // replace with C++20 floating-point concept + inline void ensure_approximately_equals_range(std::string_view msg, F actual, F expected, F delta) { if (fabs(actual-expected)>delta) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal actual: " << actual << " expected: " << expected << " tolerance: " << delta; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "not equal actual: ", + actual, " expected: ", expected, " tolerance: ", delta)); } } - inline void ensure_memory_matches(const char* msg,const void* actual, U32 actual_len, const void* expected,U32 expected_len) + inline void ensure_memory_matches(std::string_view msg,const void* actual, U32 actual_len, const void* expected,U32 expected_len) { if((expected_len != actual_len) || (std::memcmp(actual, expected, actual_len) != 0)) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "not equal"; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "not equal")); } } @@ -141,20 +132,18 @@ namespace tut } template <class T,class Q> - void ensure_not_equals(const char* msg,const Q& actual,const T& expected) + void ensure_not_equals(std::string_view msg,const Q& actual,const T& expected) { if( expected == actual ) { - std::stringstream ss; - ss << (msg?msg:"") << (msg?": ":"") << "both equal " << expected; - throw tut::failure(ss.str().c_str()); + throw tut::failure(stringize(msg, (msg.empty()?"":": "), "both equal ", expected)); } } template <class T,class Q> void ensure_not_equals(const Q& actual,const T& expected) { - ensure_not_equals(NULL, actual, expected); + ensure_not_equals("", actual, expected); } } diff --git a/indra/test/sync.h b/indra/test/sync.h index 82eef1e5f5..abeb4e17a8 100644 --- a/indra/test/sync.h +++ b/indra/test/sync.h @@ -69,7 +69,7 @@ public: // misleading, as it will be emitted after waiting threads have // already awakened. But emitting the log message within the lock // would seem to hold the lock longer than we really ought. - LL_DEBUGS() << llcoro::logname() << " bump(" << n << ") -> " << updated << LL_ENDL; + LL_DEBUGS() << LLCoros::getName() << " bump(" << n << ") -> " << updated << LL_ENDL; } /** @@ -82,7 +82,7 @@ public: */ void set(int n) { - LL_DEBUGS() << llcoro::logname() << " set(" << n << ")" << LL_ENDL; + LL_DEBUGS() << LLCoros::getName() << " set(" << n << ")" << LL_ENDL; mCond.set_all(n); } @@ -101,7 +101,7 @@ public: private: void yield_until(const char* func, int arg, int until) { - std::string name(llcoro::logname()); + std::string name(LLCoros::getName()); LL_DEBUGS() << name << " yield_until(" << until << ") suspending" << LL_ENDL; if (! mCond.wait_for_equal(mTimeout, until)) { diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index bdabab70e0..cb330eba9b 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -129,8 +129,7 @@ void LLLogin::Impl::connect(const std::string& uri, const LLSD& login_params) // Launch a coroutine with our login_() method. Run the coroutine until // its first wait; at that point, return here. - std::string coroname = - LLCoros::instance().launch("LLLogin::Impl::login_", [=]() { loginCoro(uri, login_params); }); + std::string coroname = LLCoros::instance().launch("LLLogin::Impl::login_", [=, this]() { loginCoro(uri, login_params); }); LL_DEBUGS("LLLogin") << " connected with uri '" << uri << "', login_params " << login_params << LL_ENDL; } |